diff --git a/composer.json b/composer.json index ba0dc3a5..afa32744 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,6 @@ "php": "^7.4 || ^8.0", "silverstripe/framework": "^4.10", "monolog/monolog": "~1.15", - "ptcinc/solr-php-client": "^1.0", "symfony/process": "^3.4 || ^4", "tractorcow/silverstripe-proxy-db": "~0.1", "ext-curl": "*" @@ -38,7 +37,10 @@ "psr-4": { "SilverStripe\\FullTextSearch\\": "src/", "SilverStripe\\FullTextSearch\\Tests\\": "tests/" - } + }, + "classmap": [ + "thirdparty/solr-php-client" + ] }, "bin": [ "bin/fulltextsearch_quickstart" diff --git a/src/Search/Processors/SearchUpdateProcessor.php b/src/Search/Processors/SearchUpdateProcessor.php index 0ecc5bd0..0655e6d8 100644 --- a/src/Search/Processors/SearchUpdateProcessor.php +++ b/src/Search/Processors/SearchUpdateProcessor.php @@ -5,6 +5,7 @@ use SilverStripe\FullTextSearch\Search\Services\SearchableService; use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned; use SilverStripe\ORM\DataObject; +use SilverStripe\ORM\DB; use SilverStripe\FullTextSearch\Search\Variants\SearchVariant; use SilverStripe\FullTextSearch\Search\FullTextSearch; use SilverStripe\Versioned\Versioned; @@ -154,6 +155,9 @@ protected function getSource() */ public function process() { + if (!DB::is_active()) { + return false; + } // Generate and commit all instances $indexes = $this->prepareIndexes(); foreach ($indexes as $index) { diff --git a/thirdparty/_manifest_exclude b/thirdparty/_manifest_exclude new file mode 100644 index 00000000..e69de29b diff --git a/thirdparty/solr-php-client/Apache/Solr/Document.php b/thirdparty/solr-php-client/Apache/Solr/Document.php new file mode 100644 index 00000000..5477f514 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/Document.php @@ -0,0 +1,367 @@ + + * ... + * $document->title = 'Something'; + * echo $document->title; + * ... + * + * + * Additionally, the field values can be iterated with foreach + * + * + * foreach ($document as $fieldName => $fieldValue) + * { + * ... + * } + * + */ +class Apache_Solr_Document implements IteratorAggregate +{ + /** + * SVN Revision meta data for this class + */ + const SVN_REVISION = '$Revision$'; + + /** + * SVN ID meta data for this class + */ + const SVN_ID = '$Id$'; + + /** + * Document boost value + * + * @var float + */ + protected $_documentBoost = false; + + /** + * Document field values, indexed by name + * + * @var array + */ + protected $_fields = array(); + + /** + * Document field boost values, indexed by name + * + * @var array array of floats + */ + protected $_fieldBoosts = array(); + + /** + * Clear all boosts and fields from this document + */ + public function clear() + { + $this->_documentBoost = false; + + $this->_fields = array(); + $this->_fieldBoosts = array(); + } + + /** + * Get current document boost + * + * @return mixed will be false for default, or else a float + */ + public function getBoost() + { + return $this->_documentBoost; + } + + /** + * Set document boost factor + * + * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false + */ + public function setBoost($boost) + { + $boost = (float) $boost; + + if ($boost > 0.0) + { + $this->_documentBoost = $boost; + } + else + { + $this->_documentBoost = false; + } + } + + /** + * Add a value to a multi-valued field + * + * NOTE: the solr XML format allows you to specify boosts + * PER value even though the underlying Lucene implementation + * only allows a boost per field. To remedy this, the final + * field boost value will be the product of all specified boosts + * on field values - this is similar to SolrJ's functionality. + * + * + * $doc = new Apache_Solr_Document(); + * + * $doc->addField('foo', 'bar', 2.0); + * $doc->addField('foo', 'baz', 3.0); + * + * // resultant field boost will be 6! + * echo $doc->getFieldBoost('foo'); + * + * + * @param string $key + * @param mixed $value + * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false + */ + public function addField($key, $value, $boost = false) + { + if (!isset($this->_fields[$key])) + { + // create holding array if this is the first value + $this->_fields[$key] = array(); + } + else if (!is_array($this->_fields[$key])) + { + // move existing value into array if it is not already an array + $this->_fields[$key] = array($this->_fields[$key]); + } + + if ($this->getFieldBoost($key) === false) + { + // boost not already set, set it now + $this->setFieldBoost($key, $boost); + } + else if ((float) $boost > 0.0) + { + // multiply passed boost with current field boost - similar to SolrJ implementation + $this->_fieldBoosts[$key] *= (float) $boost; + } + + // add value to array + $this->_fields[$key][] = $value; + } + + /** + * Handle the array manipulation for a multi-valued field + * + * @param string $key + * @param string $value + * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false + * + * @deprecated Use addField(...) instead + */ + public function setMultiValue($key, $value, $boost = false) + { + $this->addField($key, $value, $boost); + } + + /** + * Get field information + * + * @param string $key + * @return mixed associative array of info if field exists, false otherwise + */ + public function getField($key) + { + if (isset($this->_fields[$key])) + { + return array( + 'name' => $key, + 'value' => $this->_fields[$key], + 'boost' => $this->getFieldBoost($key) + ); + } + + return false; + } + + /** + * Set a field value. Multi-valued fields should be set as arrays + * or instead use the addField(...) function which will automatically + * make sure the field is an array. + * + * @param string $key + * @param mixed $value + * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false + */ + public function setField($key, $value, $boost = false) + { + $this->_fields[$key] = $value; + $this->setFieldBoost($key, $boost); + } + + /** + * Get the currently set field boost for a document field + * + * @param string $key + * @return float currently set field boost, false if one is not set + */ + public function getFieldBoost($key) + { + return isset($this->_fieldBoosts[$key]) ? $this->_fieldBoosts[$key] : false; + } + + /** + * Set the field boost for a document field + * + * @param string $key field name for the boost + * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false + */ + public function setFieldBoost($key, $boost) + { + $boost = (float) $boost; + + if ($boost > 0.0) + { + $this->_fieldBoosts[$key] = $boost; + } + else + { + $this->_fieldBoosts[$key] = false; + } + } + + /** + * Return current field boosts, indexed by field name + * + * @return array + */ + public function getFieldBoosts() + { + return $this->_fieldBoosts; + } + + /** + * Get the names of all fields in this document + * + * @return array + */ + public function getFieldNames() + { + return array_keys($this->_fields); + } + + /** + * Get the values of all fields in this document + * + * @return array + */ + public function getFieldValues() + { + return array_values($this->_fields); + } + + /** + * IteratorAggregate implementation function. Allows usage: + * + * + * foreach ($document as $key => $value) + * { + * ... + * } + * + */ + #[\ReturnTypeWillChange] + public function getIterator() + { + $arrayObject = new ArrayObject($this->_fields); + + return $arrayObject->getIterator(); + } + + /** + * Magic get for field values + * + * @param string $key + * @return mixed + */ + public function __get($key) + { + if (isset($this->_fields[$key])) + { + return $this->_fields[$key]; + } + + return null; + } + + /** + * Magic set for field values. Multi-valued fields should be set as arrays + * or instead use the addField(...) function which will automatically + * make sure the field is an array. + * + * @param string $key + * @param mixed $value + */ + public function __set($key, $value) + { + $this->setField($key, $value); + } + + /** + * Magic isset for fields values. Do not call directly. Allows usage: + * + * + * isset($document->some_field); + * + * + * @param string $key + * @return boolean + */ + public function __isset($key) + { + return isset($this->_fields[$key]); + } + + /** + * Magic unset for field values. Do not call directly. Allows usage: + * + * + * unset($document->some_field); + * + * + * @param string $key + */ + public function __unset($key) + { + unset($this->_fields[$key]); + unset($this->_fieldBoosts[$key]); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/Exception.php b/thirdparty/solr-php-client/Apache/Solr/Exception.php new file mode 100644 index 00000000..28c30bac --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/Exception.php @@ -0,0 +1,49 @@ +, Donovan Jimenez + */ + +/** + * Convenience class that implements the transport implementation. Can be extended by + * real implementations to do some of the common book keeping + */ +abstract class Apache_Solr_HttpTransport_Abstract implements Apache_Solr_HttpTransport_Interface +{ + /** + * Our default timeout value for requests that don't specify a timeout + * + * @var float + */ + private $_defaultTimeout = false; + + /** + * Get the current default timeout setting (initially the default_socket_timeout ini setting) + * in seconds + * + * @return float + */ + public function getDefaultTimeout() + { + // lazy load the default timeout from the ini settings + if ($this->_defaultTimeout === false) + { + $this->_defaultTimeout = (int) ini_get('default_socket_timeout'); + + // double check we didn't get 0 for a timeout + if ($this->_defaultTimeout <= 0) + { + $this->_defaultTimeout = 60; + } + } + + return $this->_defaultTimeout; + } + + /** + * Set the current default timeout for all HTTP requests + * + * @param float $timeout + */ + public function setDefaultTimeout($timeout) + { + $timeout = (float) $timeout; + + if ($timeout >= 0) + { + $this->_defaultTimeout = $timeout; + } + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Curl.php b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Curl.php new file mode 100644 index 00000000..6b4dc1e8 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Curl.php @@ -0,0 +1,206 @@ +, Donovan Jimenez + */ + +// Require Apache_Solr_HttpTransport_Abstract +require_once(dirname(__FILE__) . '/Abstract.php'); + +/** + * A Curl based HTTP transport. Uses a single curl session for all requests. + */ +class Apache_Solr_HttpTransport_Curl extends Apache_Solr_HttpTransport_Abstract +{ + /** + * SVN Revision meta data for this class + */ + const SVN_REVISION = '$Revision:$'; + + /** + * SVN ID meta data for this class + */ + const SVN_ID = '$Id:$'; + + /** + * Curl Session Handle + * + * @var resource + */ + private $_curl; + + /** + * Initializes a curl session + */ + public function __construct() + { + // initialize a CURL session + $this->_curl = curl_init(); + + // set common options that will not be changed during the session + curl_setopt_array($this->_curl, array( + // return the response body from curl_exec + CURLOPT_RETURNTRANSFER => true, + + // get the output as binary data + CURLOPT_BINARYTRANSFER => true, + + // we do not need the headers in the output, we get everything we need from curl_getinfo + CURLOPT_HEADER => false + )); + } + + /** + * Closes a curl session + */ + function __destruct() + { + // close our curl session + curl_close($this->_curl); + } + + public function setAuthenticationCredentials($username, $password) + { + // add the options to our curl handle + curl_setopt_array($this->_curl, array( + CURLOPT_USERPWD => $username . ":" . $password, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC + )); + } + + public function performGetRequest($url, $timeout = false) + { + // check the timeout value + if ($timeout === false || $timeout <= 0.0) + { + // use the default timeout + $timeout = $this->getDefaultTimeout(); + } + + // set curl GET options + curl_setopt_array($this->_curl, array( + // make sure we're returning the body + CURLOPT_NOBODY => false, + + // make sure we're GET + CURLOPT_HTTPGET => true, + + // set the URL + CURLOPT_URL => $url, + + // set the timeout + CURLOPT_TIMEOUT => $timeout + )); + + // make the request + $responseBody = curl_exec($this->_curl); + + // get info from the transfer + $statusCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($this->_curl, CURLINFO_CONTENT_TYPE); + + return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); + } + + public function performHeadRequest($url, $timeout = false) + { + // check the timeout value + if ($timeout === false || $timeout <= 0.0) + { + // use the default timeout + $timeout = $this->getDefaultTimeout(); + } + + // set curl HEAD options + curl_setopt_array($this->_curl, array( + // this both sets the method to HEAD and says not to return a body + CURLOPT_NOBODY => true, + + // set the URL + CURLOPT_URL => $url, + + // set the timeout + CURLOPT_TIMEOUT => $timeout + )); + + // make the request + $responseBody = curl_exec($this->_curl); + + // get info from the transfer + $statusCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($this->_curl, CURLINFO_CONTENT_TYPE); + + return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); + } + + public function performPostRequest($url, $postData, $contentType, $timeout = false) + { + // check the timeout value + if ($timeout === false || $timeout <= 0.0) + { + // use the default timeout + $timeout = $this->getDefaultTimeout(); + } + + // set curl POST options + curl_setopt_array($this->_curl, array( + // make sure we're returning the body + CURLOPT_NOBODY => false, + + // make sure we're POST + CURLOPT_POST => true, + + // set the URL + CURLOPT_URL => $url, + + // set the post data + CURLOPT_POSTFIELDS => $postData, + + // set the content type + CURLOPT_HTTPHEADER => array("Content-Type: {$contentType}"), + + // set the timeout + CURLOPT_TIMEOUT => $timeout + )); + + // make the request + $responseBody = curl_exec($this->_curl); + + // get info from the transfer + $statusCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($this->_curl, CURLINFO_CONTENT_TYPE); + + return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/HttpTransport/CurlNoReuse.php b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/CurlNoReuse.php new file mode 100644 index 00000000..3220edda --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/CurlNoReuse.php @@ -0,0 +1,230 @@ +, Donovan Jimenez + */ + +// Require Apache_Solr_HttpTransport_Abstract +require_once(dirname(__FILE__) . '/Abstract.php'); + +/** + * An alternative Curl HTTP transport that opens and closes a curl session for + * every request. This isn't the recommended way to use curl, but some version of + * PHP have memory issues. + */ +class Apache_Solr_HttpTransport_CurlNoReuse extends Apache_Solr_HttpTransport_Abstract +{ + /** + * SVN Revision meta data for this class + */ + const SVN_REVISION = '$Revision:$'; + + /** + * SVN ID meta data for this class + */ + const SVN_ID = '$Id:$'; + + private $_authString = false; + + public function setAuthenticationCredentials($username, $password) + { + // this is how curl wants it for the CURLOPT_USERPWD + $this->_authString = $username . ":" . $password; + } + + public function performGetRequest($url, $timeout = false) + { + // check the timeout value + if ($timeout === false || $timeout <= 0.0) + { + // use the default timeout + $timeout = $this->getDefaultTimeout(); + } + + $curl = curl_init(); + + // set curl GET options + curl_setopt_array($curl, array( + // return the response body from curl_exec + CURLOPT_RETURNTRANSFER => true, + + // get the output as binary data + CURLOPT_BINARYTRANSFER => true, + + // we do not need the headers in the output, we get everything we need from curl_getinfo + CURLOPT_HEADER => false, + + // set the URL + CURLOPT_URL => $url, + + // set the timeout + CURLOPT_TIMEOUT => $timeout + )); + + // set auth if appropriate + if ($this->_authString !== false) + { + curl_setopt_array($curl, array( + CURLOPT_USERPWD => $this->_authString, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC + )); + } + + // make the request + $responseBody = curl_exec($curl); + + // get info from the transfer + $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); + + // close our curl session - we're done with it + curl_close($curl); + + return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); + } + + public function performHeadRequest($url, $timeout = false) + { + // check the timeout value + if ($timeout === false || $timeout <= 0.0) + { + // use the default timeout + $timeout = $this->getDefaultTimeout(); + } + + $curl = curl_init(); + + // set curl HEAD options + curl_setopt_array($curl, array( + // return the response body from curl_exec + CURLOPT_RETURNTRANSFER => true, + + // get the output as binary data + CURLOPT_BINARYTRANSFER => true, + + // we do not need the headers in the output, we get everything we need from curl_getinfo + CURLOPT_HEADER => false, + + // this both sets the method to HEAD and says not to return a body + CURLOPT_NOBODY => true, + + // set the URL + CURLOPT_URL => $url, + + // set the timeout + CURLOPT_TIMEOUT => $timeout + )); + + // set auth if appropriate + if ($this->_authString !== false) + { + curl_setopt_array($curl, array( + CURLOPT_USERPWD => $this->_authString, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC + )); + } + + // make the request + $responseBody = curl_exec($curl); + + // get info from the transfer + $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); + + // close our curl session - we're done with it + curl_close($curl); + + return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); + } + + public function performPostRequest($url, $postData, $contentType, $timeout = false) + { + // check the timeout value + if ($timeout === false || $timeout <= 0.0) + { + // use the default timeout + $timeout = $this->getDefaultTimeout(); + } + + $curl = curl_init(); + + // set curl POST options + curl_setopt_array($curl, array( + // return the response body from curl_exec + CURLOPT_RETURNTRANSFER => true, + + // get the output as binary data + CURLOPT_BINARYTRANSFER => true, + + // we do not need the headers in the output, we get everything we need from curl_getinfo + CURLOPT_HEADER => false, + + // make sure we're POST + CURLOPT_POST => true, + + // set the URL + CURLOPT_URL => $url, + + // set the post data + CURLOPT_POSTFIELDS => $postData, + + // set the content type + CURLOPT_HTTPHEADER => array("Content-Type: {$contentType}"), + + // set the timeout + CURLOPT_TIMEOUT => $timeout + )); + + // set auth if appropriate + if ($this->_authString !== false) + { + curl_setopt_array($curl, array( + CURLOPT_USERPWD => $this->_authString, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC + )); + } + + // make the request + $responseBody = curl_exec($curl); + + // get info from the transfer + $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); + + // close our curl session - we're done with it + curl_close($curl); + + return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/HttpTransport/FileGetContents.php b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/FileGetContents.php new file mode 100644 index 00000000..7cd9687a --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/FileGetContents.php @@ -0,0 +1,238 @@ +_getContext = stream_context_create(); + $this->_headContext = stream_context_create(); + $this->_postContext = stream_context_create(); + } + + public function setAuthenticationCredentials($username, $password) + { + // compute the Authorization header + $this->_authHeader = "Authorization: Basic " . base64_encode($username . ":" . $password); + + // set it now for get and head contexts + stream_context_set_option($this->_getContext, 'http', 'header', $this->_authHeader); + stream_context_set_option($this->_headContext, 'http', 'header', $this->_authHeader); + + // for post, it'll be set each time, so add an \r\n so it can be concatenated + // with the Content-Type + $this->_authHeader .= "\r\n"; + } + + public function performGetRequest($url, $timeout = false) + { + // set the timeout if specified + if ($timeout !== FALSE && $timeout > 0.0) + { + // timeouts with file_get_contents seem to need + // to be halved to work as expected + $timeout = (float) $timeout / 2; + + stream_context_set_option($this->_getContext, 'http', 'timeout', $timeout); + } + else + { + // use the default timeout pulled from default_socket_timeout otherwise + stream_context_set_option($this->_getContext, 'http', 'timeout', $this->getDefaultTimeout()); + } + + // $http_response_headers will be updated by the call to file_get_contents later + // see http://us.php.net/manual/en/wrappers.http.php for documentation + // Unfortunately, it will still create a notice in analyzers if we don't set it here + $http_response_header = null; + $responseBody = @file_get_contents($url, false, $this->_getContext); + + return $this->_getResponseFromParts($responseBody, $http_response_header); + } + + public function performHeadRequest($url, $timeout = false) + { + stream_context_set_option($this->_headContext, array( + 'http' => array( + // set HTTP method + 'method' => 'HEAD', + + // default timeout + 'timeout' => $this->getDefaultTimeout() + ) + ) + ); + + // set the timeout if specified + if ($timeout !== FALSE && $timeout > 0.0) + { + // timeouts with file_get_contents seem to need + // to be halved to work as expected + $timeout = (float) $timeout / 2; + + stream_context_set_option($this->_headContext, 'http', 'timeout', $timeout); + } + + // $http_response_headers will be updated by the call to file_get_contents later + // see http://us.php.net/manual/en/wrappers.http.php for documentation + // Unfortunately, it will still create a notice in analyzers if we don't set it here + $http_response_header = null; + $responseBody = @file_get_contents($url, false, $this->_headContext); + + return $this->_getResponseFromParts($responseBody, $http_response_header); + } + + public function performPostRequest($url, $rawPost, $contentType, $timeout = false) + { + stream_context_set_option($this->_postContext, array( + 'http' => array( + // set HTTP method + 'method' => 'POST', + + // Add our posted content type (and auth header - see setAuthentication) + 'header' => "{$this->_authHeader}Content-Type: {$contentType}", + + // the posted content + 'content' => $rawPost, + + // default timeout + 'timeout' => $this->getDefaultTimeout() + ) + ) + ); + + // set the timeout if specified + if ($timeout !== FALSE && $timeout > 0.0) + { + // timeouts with file_get_contents seem to need + // to be halved to work as expected + $timeout = (float) $timeout / 2; + + stream_context_set_option($this->_postContext, 'http', 'timeout', $timeout); + } + + // $http_response_header will be updated by the call to file_get_contents later + // see http://us.php.net/manual/en/wrappers.http.php for documentation + // Unfortunately, it will still create a notice in analyzers if we don't set it here + $http_response_header = null; + $responseBody = @file_get_contents($url, false, $this->_postContext); + + // reset content of post context to reclaim memory + stream_context_set_option($this->_postContext, 'http', 'content', ''); + + return $this->_getResponseFromParts($responseBody, $http_response_header); + } + + private function _getResponseFromParts($rawResponse, $httpHeaders) + { + //Assume 0, false as defaults + $status = 0; + $contentType = false; + + //iterate through headers for real status, type, and encoding + if (is_array($httpHeaders) && count($httpHeaders) > 0) + { + //look at the first headers for the HTTP status code + //and message (errors are usually returned this way) + // + //HTTP 100 Continue response can also be returned before + //the REAL status header, so we need look until we find + //the last header starting with HTTP + // + //the spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1 + // + //Thanks to Daniel Andersson for pointing out this oversight + while (isset($httpHeaders[0]) && substr($httpHeaders[0], 0, 4) == 'HTTP') + { + // we can do a intval on status line without the "HTTP/1.X " to get the code + $status = intval(substr($httpHeaders[0], 9)); + + // remove this from the headers so we can check for more + array_shift($httpHeaders); + } + + //Look for the Content-Type response header and determine type + //and encoding from it (if possible - such as 'Content-Type: text/plain; charset=UTF-8') + foreach ($httpHeaders as $header) + { + // look for the header that starts appropriately + if (strncasecmp($header, 'Content-Type:', 13) == 0) + { + $contentType = substr($header, 13); + break; + } + } + } + + return new Apache_Solr_HttpTransport_Response($status, $contentType, $rawResponse); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Interface.php b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Interface.php new file mode 100644 index 00000000..1ca59d90 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Interface.php @@ -0,0 +1,103 @@ +, Donovan Jimenez + */ + +// require Apache_Solr_HttpTransport_Response +require_once(dirname(__FILE__) . '/Response.php'); + +/** + * Interface that all Transport (HTTP Requester) implementations must implement. These + * Implementations can then be plugged into the Service instance in order to user their + * the desired method for making HTTP requests + */ +interface Apache_Solr_HttpTransport_Interface +{ + /** + * Get the current default timeout for all HTTP requests + * + * @return float + */ + public function getDefaultTimeout(); + + /** + * Set the current default timeout for all HTTP requests + * + * @param float $timeout + */ + public function setDefaultTimeout($timeout); + + /** + * Set authentication credentials to pass along with the requests. + * + * These will be used to perform HTTP Basic authentication. + * + * @param string $username + * @param string $password + */ + public function setAuthenticationCredentials($username, $password); + + /** + * Perform a GET HTTP operation with an optional timeout and return the response + * contents, use getLastResponseHeaders to retrieve HTTP headers + * + * @param string $url + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performGetRequest($url, $timeout = false); + + /** + * Perform a HEAD HTTP operation with an optional timeout and return the response + * headers - NOTE: head requests have no response body + * + * @param string $url + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performHeadRequest($url, $timeout = false); + + /** + * Perform a POST HTTP operation with an optional timeout and return the response + * contents, use getLastResponseHeaders to retrieve HTTP headers + * + * @param string $url + * @param string $rawPost + * @param string $contentType + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performPostRequest($url, $rawPost, $contentType, $timeout = false); +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Response.php b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Response.php new file mode 100644 index 00000000..f65bca40 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/HttpTransport/Response.php @@ -0,0 +1,254 @@ + "Communication Error", + + // Informational 1XX + 100 => "Continue", + 101 => "Switching Protocols", + + // Successful 2XX + 200 => "OK", + 201 => "Created", + 202 => "Accepted", + 203 => "Non-Authoritative Information", + 204 => "No Content", + 205 => "Reset Content", + 206 => "Partial Content", + + // Redirection 3XX + 300 => "Multiple Choices", + 301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", + 304 => "Not Modified", + 305 => "Use Proxy", + 307 => "Temporary Redirect", + + // Client Error 4XX + 400 => "Bad Request", + 401 => "Unauthorized", + 402 => "Payment Required", + 403 => "Forbidden", + 404 => "Not Found", + 405 => "Method Not Allowed", + 406 => "Not Acceptable", + 407 => "Proxy Authentication Required", + 408 => "Request Timeout", + 409 => "Conflict", + 410 => "Gone", + 411 => "Length Required", + 412 => "Precondition Failed", + 413 => "Request Entity Too Large", + 414 => "Request-URI Too Long", + 415 => "Unsupported Media Type", + 416 => "Request Range Not Satisfiable", + 417 => "Expectation Failed", + + // Server Error 5XX + 500 => "Internal Server Error", + 501 => "Not Implemented", + 502 => "Bad Gateway", + 503 => "Service Unavailable", + 504 => "Gateway Timeout", + 505 => "HTTP Version Not Supported" + ); + + /** + * Get the HTTP status message based on status code + * + * @return string + */ + public static function getDefaultStatusMessage($statusCode) + { + $statusCode = (int) $statusCode; + + if (isset(self::$_defaultStatusMessages[$statusCode])) + { + return self::$_defaultStatusMessages[$statusCode]; + } + + return "Unknown Status"; + } + + /** + * The response's HTTP status code + * + * @var integer + */ + private $_statusCode; + + /** + * The response's HTTP status message + * + * @var string + */ + private $_statusMessage; + + /** + * The response's mime type + * + * @var string + */ + private $_mimeType; + + /** + * The response's character encoding + * + * @var string + */ + private $_encoding; + + /** + * The response's data + * + * @var string + */ + private $_responseBody; + + /** + * Construct a HTTP transport response + * + * @param integer $statusCode The HTTP status code + * @param string $contentType The VALUE of the Content-Type HTTP header + * @param string $responseBody The body of the HTTP response + */ + public function __construct($statusCode, $contentType, $responseBody) + { + // set the status code, make sure its an integer + $this->_statusCode = (int) $statusCode; + + // lookup up status message based on code + $this->_statusMessage = self::getDefaultStatusMessage($this->_statusCode); + + // set the response body, it should always be a string + $this->_responseBody = (string) $responseBody; + + // parse the content type header value for mimetype and encoding + // first set default values that will remain if we can't find + // what we're looking for in the content type + $this->_mimeType = "text/plain"; + $this->_encoding = "UTF-8"; + + if ($contentType) + { + // now break apart the header to see if there's character encoding + $contentTypeParts = explode(';', $contentType, 2); + + if (isset($contentTypeParts[0])) + { + $this->_mimeType = trim($contentTypeParts[0]); + } + + if (isset($contentTypeParts[1])) + { + // we have a second part, split it further + $contentTypeParts = explode('=', $contentTypeParts[1]); + + if (isset($contentTypeParts[1])) + { + $this->_encoding = trim($contentTypeParts[1]); + } + } + } + } + + /** + * Get the status code of the response + * + * @return integer + */ + public function getStatusCode() + { + return $this->_statusCode; + } + + /** + * Get the status message of the response + * + * @return string + */ + public function getStatusMessage() + { + return $this->_statusMessage; + } + + /** + * Get the mimetype of the response body + * + * @return string + */ + public function getMimeType() + { + return $this->_mimeType; + } + + /** + * Get the charset encoding of the response body. + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Get the raw response body + * + * @return string + */ + public function getBody() + { + return $this->_responseBody; + } +} diff --git a/thirdparty/solr-php-client/Apache/Solr/HttpTransportException.php b/thirdparty/solr-php-client/Apache/Solr/HttpTransportException.php new file mode 100644 index 00000000..642b7b64 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/HttpTransportException.php @@ -0,0 +1,78 @@ +getHttpStatus()}' Status: {$response->getHttpStatusMessage()}", $response->getHttpStatus()); + + $this->_response = $response; + } + + /** + * Get the response for which this exception was generated + * + * @return Apache_Solr_Response + */ + public function getResponse() + { + return $this->_response; + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/InvalidArgumentException.php b/thirdparty/solr-php-client/Apache/Solr/InvalidArgumentException.php new file mode 100644 index 00000000..46c15702 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/InvalidArgumentException.php @@ -0,0 +1,49 @@ += 5.2.0, Alternatively can be + * installed with PECL. Zend Framework also includes a purely PHP solution. + */ +class Apache_Solr_Response +{ + /** + * SVN Revision meta data for this class + */ + const SVN_REVISION = '$Revision$'; + + /** + * SVN ID meta data for this class + */ + const SVN_ID = '$Id$'; + + /** + * Holds the raw response used in construction + * + * @var Apache_Solr_HttpTransport_Response HTTP response + */ + protected $_response; + + /** + * Whether the raw response has been parsed + * + * @var boolean + */ + protected $_isParsed = false; + + /** + * Parsed representation of the data + * + * @var mixed + */ + protected $_parsedData; + + /** + * Data parsing flags. Determines what extra processing should be done + * after the data is initially converted to a data structure. + * + * @var boolean + */ + protected $_createDocuments = true, + $_collapseSingleValueArrays = true; + + /** + * Constructor. Takes the raw HTTP response body and the exploded HTTP headers + * + * @return Apache_Solr_HttpTransport_Response HTTP response + * @param boolean $createDocuments Whether to convert the documents json_decoded as stdClass instances to Apache_Solr_Document instances + * @param boolean $collapseSingleValueArrays Whether to make multivalued fields appear as single values + */ + public function __construct(Apache_Solr_HttpTransport_Response $response, $createDocuments = true, $collapseSingleValueArrays = true) + { + $this->_response = $response; + $this->_createDocuments = (bool) $createDocuments; + $this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays; + } + + /** + * Get the HTTP status code + * + * @return integer + */ + public function getHttpStatus() + { + return $this->_response->getStatusCode(); + } + + /** + * Get the HTTP status message of the response + * + * @return string + */ + public function getHttpStatusMessage() + { + return $this->_response->getStatusMessage(); + } + + /** + * Get content type of this Solr response + * + * @return string + */ + public function getType() + { + return $this->_response->getMimeType(); + } + + /** + * Get character encoding of this response. Should usually be utf-8, but just in case + * + * @return string + */ + public function getEncoding() + { + return $this->_response->getEncoding(); + } + + /** + * Get the raw response as it was given to this object + * + * @return string + */ + public function getRawResponse() + { + return $this->_response->getBody(); + } + + /** + * Magic get to expose the parsed data and to lazily load it + * + * @param string $key + * @return mixed + */ + public function __get($key) + { + if (!$this->_isParsed) + { + $this->_parseData(); + $this->_isParsed = true; + } + + if (isset($this->_parsedData->$key)) + { + return $this->_parsedData->$key; + } + + return null; + } + + /** + * Magic function for isset function on parsed data + * + * @param string $key + * @return boolean + */ + public function __isset($key) + { + if (!$this->_isParsed) + { + $this->_parseData(); + $this->_isParsed = true; + } + + return isset($this->_parsedData->$key); + } + + /** + * Parse the raw response into the parsed_data array for access + * + * @throws Apache_Solr_ParserException If the data could not be parsed + */ + protected function _parseData() + { + //An alternative would be to use Zend_Json::decode(...) + $data = json_decode($this->_response->getBody()); + + // check that we receive a valid JSON response - we should never receive a null + if ($data === null) + { + throw new Apache_Solr_ParserException('Solr response does not appear to be valid JSON, please examine the raw response with getRawResponse() method'); + } + + //if we're configured to collapse single valued arrays or to convert them to Apache_Solr_Document objects + //and we have response documents, then try to collapse the values and / or convert them now + if (($this->_createDocuments || $this->_collapseSingleValueArrays) && isset($data->response) && is_array($data->response->docs)) + { + $documents = array(); + + foreach ($data->response->docs as $originalDocument) + { + if ($this->_createDocuments) + { + $document = new Apache_Solr_Document(); + } + else + { + $document = $originalDocument; + } + + foreach ($originalDocument as $key => $value) + { + //If a result is an array with only a single + //value then its nice to be able to access + //it as if it were always a single value + if ($this->_collapseSingleValueArrays && is_array($value) && count($value) <= 1) + { + $value = array_shift($value); + } + + $document->$key = $value; + } + + $documents[] = $document; + } + + $data->response->docs = $documents; + } + + $this->_parsedData = $data; + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/Service.php b/thirdparty/solr-php-client/Apache/Solr/Service.php new file mode 100644 index 00000000..cdf53e54 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/Service.php @@ -0,0 +1,1211 @@ + + * ... + * $solr = new Apache_Solr_Service(); //or explicitly new Apache_Solr_Service('localhost', 8180, '/solr') + * + * $solr->deleteByQuery('*:*'); //deletes ALL documents - be careful :) + * + * $document = new Apache_Solr_Document(); + * $document->id = uniqid(); //or something else suitably unique + * + * $document->title = 'Some Title'; + * $document->content = 'Some content for this wonderful document. Blah blah blah.'; + * + * $solr->addDocument($document); //if you're going to be adding documents in bulk using addDocuments + * //with an array of documents is faster + * + * $solr->commit(); //commit to see the deletes and the document + * $solr->optimize(); //merges multiple segments into one + * + * //and the one we all care about, search! + * //any other common or custom parameters to the request handler can go in the + * //optional 4th array argument. + * $solr->search('content:blah', 0, 10, array('sort' => 'timestamp desc')); + * ... + * + */ +class Apache_Solr_Service +{ + /** + * SVN Revision meta data for this class + */ + const SVN_REVISION = '$Revision$'; + + /** + * SVN ID meta data for this class + */ + const SVN_ID = '$Id$'; + + /** + * SVN HeadURL meta data for this class + */ + const SVN_URL = '$HeadURL$'; + + /** + * Response writer we'll request - JSON. See http://code.google.com/p/solr-php-client/issues/detail?id=6#c1 for reasoning + */ + const SOLR_WRITER = 'json'; + + /** + * NamedList Treatment constants + */ + const NAMED_LIST_FLAT = 'flat'; + const NAMED_LIST_MAP = 'map'; + + /** + * Search HTTP Methods + */ + const METHOD_GET = 'GET'; + const METHOD_POST = 'POST'; + + /** + * Servlet mappings + */ + const PING_SERVLET = 'admin/ping'; + const UPDATE_SERVLET = 'update'; + const SEARCH_SERVLET = 'select'; + const SYSTEM_SERVLET = 'admin/system'; + const THREADS_SERVLET = 'admin/threads'; + const EXTRACT_SERVLET = 'update/extract'; + + /** + * Server identification strings + * + * @var string + */ + protected $_host, $_port, $_path; + + /** + * Whether {@link Apache_Solr_Response} objects should create {@link Apache_Solr_Document}s in + * the returned parsed data + * + * @var boolean + */ + protected $_createDocuments = true; + + /** + * Whether {@link Apache_Solr_Response} objects should have multivalue fields with only a single value + * collapsed to appear as a single value would. + * + * @var boolean + */ + protected $_collapseSingleValueArrays = true; + + /** + * How NamedLists should be formatted in the output. This specifically effects facet counts. Valid values + * are {@link Apache_Solr_Service::NAMED_LIST_MAP} (default) or {@link Apache_Solr_Service::NAMED_LIST_FLAT}. + * + * @var string + */ + protected $_namedListTreatment = self::NAMED_LIST_MAP; + + /** + * Query delimiters. Someone might want to be able to change + * these (to use & instead of & for example), so I've provided them. + * + * @var string + */ + protected $_queryDelimiter = '?', $_queryStringDelimiter = '&', $_queryBracketsEscaped = true; + + /** + * Constructed servlet full path URLs + * + * @var string + */ + protected $_pingUrl, $_updateUrl, $_searchUrl, $_systemUrl, $_threadsUrl; + + /** + * Keep track of whether our URLs have been constructed + * + * @var boolean + */ + protected $_urlsInited = false; + + /** + * HTTP Transport implementation (pluggable) + * + * @var Apache_Solr_HttpTransport_Interface + */ + protected $_httpTransport = false; + + /** + * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. + * + * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead + * + * @param string $value + * @return string + */ + static public function escape($value) + { + //list taken from http://lucene.apache.org/java/docs/queryparsersyntax.html#Escaping%20Special%20Characters + $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; + $replace = '\\\$1'; + + return preg_replace($pattern, $replace, $value); + } + + /** + * Escape a value meant to be contained in a phrase for special query characters + * + * @param string $value + * @return string + */ + static public function escapePhrase($value) + { + $pattern = '/("|\\\)/'; + $replace = '\\\$1'; + + return preg_replace($pattern, $replace, $value); + } + + /** + * Convenience function for creating phrase syntax from a value + * + * @param string $value + * @return string + */ + static public function phrase($value) + { + return '"' . self::escapePhrase($value) . '"'; + } + + /** + * Constructor. All parameters are optional and will take on default values + * if not specified. + * + * @param string $host + * @param string $port + * @param string $path + * @param Apache_Solr_HttpTransport_Interface $httpTransport + */ + public function __construct($host = 'localhost', $port = 8180, $path = '/solr/', $httpTransport = false) + { + $this->setHost($host); + $this->setPort($port); + $this->setPath($path); + + $this->_initUrls(); + + if ($httpTransport) + { + $this->setHttpTransport($httpTransport); + } + + // check that our php version is >= 5.1.3 so we can correct for http_build_query behavior later + $this->_queryBracketsEscaped = version_compare(phpversion(), '5.1.3', '>='); + } + + /** + * Return a valid http URL given this server's host, port and path and a provided servlet name + * + * @param string $servlet + * @return string + */ + protected function _constructUrl($servlet, $params = array()) + { + if (count($params)) + { + //escape all parameters appropriately for inclusion in the query string + $escapedParams = array(); + + foreach ($params as $key => $value) + { + $escapedParams[] = urlencode($key) . '=' . urlencode($value); + } + + $queryString = $this->_queryDelimiter . implode($this->_queryStringDelimiter, $escapedParams); + } + else + { + $queryString = ''; + } + + return 'http://' . $this->_host . ':' . $this->_port . $this->_path . $servlet . $queryString; + } + + /** + * Construct the Full URLs for the three servlets we reference + */ + protected function _initUrls() + { + //Initialize our full servlet URLs now that we have server information + $this->_extractUrl = $this->_constructUrl(self::EXTRACT_SERVLET); + $this->_pingUrl = $this->_constructUrl(self::PING_SERVLET); + $this->_searchUrl = $this->_constructUrl(self::SEARCH_SERVLET); + $this->_systemUrl = $this->_constructUrl(self::SYSTEM_SERVLET, array('wt' => self::SOLR_WRITER)); + $this->_threadsUrl = $this->_constructUrl(self::THREADS_SERVLET, array('wt' => self::SOLR_WRITER )); + $this->_updateUrl = $this->_constructUrl(self::UPDATE_SERVLET, array('wt' => self::SOLR_WRITER )); + + $this->_urlsInited = true; + } + + protected function _generateQueryString($params) + { + // use http_build_query to encode our arguments because its faster + // than urlencoding all the parts ourselves in a loop + // + // because http_build_query treats arrays differently than we want to, correct the query + // string by changing foo[#]=bar (# being an actual number) parameter strings to just + // multiple foo=bar strings. This regex should always work since '=' will be urlencoded + // anywhere else the regex isn't expecting it + // + // NOTE: before php 5.1.3 brackets were not url encoded by http_build query - we've checked + // the php version in the constructor and put the results in the instance variable. Also, before + // 5.1.2 the arg_separator parameter was not available, so don't use it + if ($this->_queryBracketsEscaped) + { + $queryString = http_build_query($params, null, $this->_queryStringDelimiter); + return preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $queryString); + } + else + { + $queryString = http_build_query($params); + return preg_replace('/\\[(?:[0-9]|[1-9][0-9]+)\\]=/', '=', $queryString); + } + } + + /** + * Central method for making a get operation against this Solr Server + * + * @param string $url + * @param float $timeout Read timeout in seconds + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If a non 200 response status is returned + */ + protected function _sendRawGet($url, $timeout = FALSE) + { + $httpTransport = $this->getHttpTransport(); + + $httpResponse = $httpTransport->performGetRequest($url, $timeout); + $solrResponse = new Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays); + + if ($solrResponse->getHttpStatus() != 200) + { + throw new Apache_Solr_HttpTransportException($solrResponse); + } + + return $solrResponse; + } + + /** + * Central method for making a post operation against this Solr Server + * + * @param string $url + * @param string $rawPost + * @param float $timeout Read timeout in seconds + * @param string $contentType + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If a non 200 response status is returned + */ + protected function _sendRawPost($url, $rawPost, $timeout = FALSE, $contentType = 'text/xml; charset=UTF-8') + { + $httpTransport = $this->getHttpTransport(); + + $httpResponse = $httpTransport->performPostRequest($url, $rawPost, $contentType, $timeout); + $solrResponse = new Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays); + + if ($solrResponse->getHttpStatus() != 200) + { + throw new Apache_Solr_HttpTransportException($solrResponse); + } + + return $solrResponse; + } + + /** + * Returns the set host + * + * @return string + */ + public function getHost() + { + return $this->_host; + } + + /** + * Set the host used. If empty will fallback to constants + * + * @param string $host + * + * @throws Apache_Solr_InvalidArgumentException If the host parameter is empty + */ + public function setHost($host) + { + //Use the provided host or use the default + if (empty($host)) + { + throw new Apache_Solr_InvalidArgumentException('Host parameter is empty'); + } + else + { + $this->_host = $host; + } + + if ($this->_urlsInited) + { + $this->_initUrls(); + } + } + + /** + * Get the set port + * + * @return integer + */ + public function getPort() + { + return $this->_port; + } + + /** + * Set the port used. If empty will fallback to constants + * + * @param integer $port + * + * @throws Apache_Solr_InvalidArgumentException If the port parameter is empty + */ + public function setPort($port) + { + //Use the provided port or use the default + $port = (int) $port; + + if ($port <= 0) + { + throw new Apache_Solr_InvalidArgumentException('Port is not a valid port number'); + } + else + { + $this->_port = $port; + } + + if ($this->_urlsInited) + { + $this->_initUrls(); + } + } + + /** + * Get the set path. + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Set the path used. If empty will fallback to constants + * + * @param string $path + */ + public function setPath($path) + { + $path = trim($path, '/'); + + if (strlen($path) > 0) + { + $this->_path = '/' . $path . '/'; + } + else + { + $this->_path = '/'; + } + + if ($this->_urlsInited) + { + $this->_initUrls(); + } + } + + /** + * Get the current configured HTTP Transport + * + * @return HttpTransportInterface + */ + public function getHttpTransport() + { + // lazy load a default if one has not be set + if ($this->_httpTransport === false) + { + require_once(dirname(__FILE__) . '/HttpTransport/FileGetContents.php'); + + $this->_httpTransport = new Apache_Solr_HttpTransport_FileGetContents(); + } + + return $this->_httpTransport; + } + + /** + * Set the HTTP Transport implemenation that will be used for all HTTP requests + * + * @param Apache_Solr_HttpTransport_Interface + */ + public function setHttpTransport(Apache_Solr_HttpTransport_Interface $httpTransport) + { + $this->_httpTransport = $httpTransport; + } + + /** + * Set the create documents flag. This determines whether {@link Apache_Solr_Response} objects will + * parse the response and create {@link Apache_Solr_Document} instances in place. + * + * @param boolean $createDocuments + */ + public function setCreateDocuments($createDocuments) + { + $this->_createDocuments = (bool) $createDocuments; + } + + /** + * Get the current state of teh create documents flag. + * + * @return boolean + */ + public function getCreateDocuments() + { + return $this->_createDocuments; + } + + /** + * Set the collapse single value arrays flag. + * + * @param boolean $collapseSingleValueArrays + */ + public function setCollapseSingleValueArrays($collapseSingleValueArrays) + { + $this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays; + } + + /** + * Get the current state of the collapse single value arrays flag. + * + * @return boolean + */ + public function getCollapseSingleValueArrays() + { + return $this->_collapseSingleValueArrays; + } + + /** + * Get the current default timeout setting (initially the default_socket_timeout ini setting) + * in seconds + * + * @return float + * + * @deprecated Use the getDefaultTimeout method on the HTTP transport implementation + */ + public function getDefaultTimeout() + { + return $this->getHttpTransport()->getDefaultTimeout(); + } + + /** + * Set the default timeout for all calls that aren't passed a specific timeout + * + * @param float $timeout Timeout value in seconds + * + * @deprecated Use the setDefaultTimeout method on the HTTP transport implementation + */ + public function setDefaultTimeout($timeout) + { + $this->getHttpTransport()->setDefaultTimeout($timeout); + } + + /** + * Convenience method to set authentication credentials on the current HTTP transport implementation + * + * @param string $username + * @param string $password + */ + public function setAuthenticationCredentials($username, $password) + { + $this->getHttpTransport()->setAuthenticationCredentials($username, $password); + } + + /** + * Set how NamedLists should be formatted in the response data. This mainly effects + * the facet counts format. + * + * @param string $namedListTreatment + * @throws Apache_Solr_InvalidArgumentException If invalid option is set + */ + public function setNamedListTreatment($namedListTreatment) + { + switch ((string) $namedListTreatment) + { + case Apache_Solr_Service::NAMED_LIST_FLAT: + $this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_FLAT; + break; + + case Apache_Solr_Service::NAMED_LIST_MAP: + $this->_namedListTreatment = Apache_Solr_Service::NAMED_LIST_MAP; + break; + + default: + throw new Apache_Solr_InvalidArgumentException('Not a valid named list treatement option'); + } + } + + /** + * Get the current setting for named list treatment. + * + * @return string + */ + public function getNamedListTreatment() + { + return $this->_namedListTreatment; + } + + /** + * Set the string used to separate the path form the query string. + * Defaulted to '?' + * + * @param string $queryDelimiter + */ + public function setQueryDelimiter($queryDelimiter) + { + $this->_queryDelimiter = $queryDelimiter; + } + + /** + * Set the string used to separate the parameters in thequery string + * Defaulted to '&' + * + * @param string $queryStringDelimiter + */ + public function setQueryStringDelimiter($queryStringDelimiter) + { + $this->_queryStringDelimiter = $queryStringDelimiter; + } + + /** + * Call the /admin/ping servlet, can be used to quickly tell if a connection to the + * server is able to be made. + * + * @param float $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2) + * @return float Actual time taken to ping the server, FALSE if timeout or HTTP error status occurs + */ + public function ping($timeout = 2) + { + $start = microtime(true); + + $httpTransport = $this->getHttpTransport(); + + $httpResponse = $httpTransport->performHeadRequest($this->_pingUrl, $timeout); + $solrResponse = new Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays); + + if ($solrResponse->getHttpStatus() == 200) + { + return microtime(true) - $start; + } + else + { + return false; + } + } + + /** + * Call the /admin/system servlet and retrieve system information about Solr + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function system() + { + return $this->_sendRawGet($this->_systemUrl); + } + + /** + * Call the /admin/threads servlet and retrieve information about all threads in the + * Solr servlet's thread group. Useful for diagnostics. + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function threads() + { + return $this->_sendRawGet($this->_threadsUrl); + } + + /** + * Raw Add Method. Takes a raw post body and sends it to the update service. Post body + * should be a complete and well formed "add" xml document. + * + * @param string $rawPost + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function add($rawPost) + { + return $this->_sendRawPost($this->_updateUrl, $rawPost); + } + + /** + * Add a Solr Document to the index + * + * @param Apache_Solr_Document $document + * @param boolean $allowDups + * @param boolean $overwritePending + * @param boolean $overwriteCommitted + * @param integer $commitWithin The number of milliseconds that a document must be committed within, see @{link http://wiki.apache.org/solr/UpdateXmlMessages#The_Update_Schema} for details. If left empty this property will not be set in the request. + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true, $commitWithin = 0) + { + $dupValue = $allowDups ? 'true' : 'false'; + $pendingValue = $overwritePending ? 'true' : 'false'; + $committedValue = $overwriteCommitted ? 'true' : 'false'; + + $commitWithin = (int) $commitWithin; + $commitWithinString = $commitWithin > 0 ? " commitWithin=\"{$commitWithin}\"" : ''; + + $rawPost = ""; + $rawPost .= $this->_documentToXmlFragment($document); + $rawPost .= ''; + + return $this->add($rawPost); + } + + /** + * Add an array of Solr Documents to the index all at once + * + * @param array $documents Should be an array of Apache_Solr_Document instances + * @param boolean $allowDups + * @param boolean $overwritePending + * @param boolean $overwriteCommitted + * @param integer $commitWithin The number of milliseconds that a document must be committed within, see @{link http://wiki.apache.org/solr/UpdateXmlMessages#The_Update_Schema} for details. If left empty this property will not be set in the request. + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true, $commitWithin = 0) + { + $dupValue = $allowDups ? 'true' : 'false'; + $pendingValue = $overwritePending ? 'true' : 'false'; + $committedValue = $overwriteCommitted ? 'true' : 'false'; + + $commitWithin = (int) $commitWithin; + $commitWithinString = $commitWithin > 0 ? " commitWithin=\"{$commitWithin}\"" : ''; + + $rawPost = ""; + + foreach ($documents as $document) + { + if ($document instanceof Apache_Solr_Document) + { + $rawPost .= $this->_documentToXmlFragment($document); + } + } + + $rawPost .= ''; + + return $this->add($rawPost); + } + + /** + * Create an XML fragment from a {@link Apache_Solr_Document} instance appropriate for use inside a Solr add call + * + * @return string + */ + protected function _documentToXmlFragment(Apache_Solr_Document $document) + { + $xml = 'getBoost() !== false) + { + $xml .= ' boost="' . $document->getBoost() . '"'; + } + + $xml .= '>'; + + foreach ($document as $key => $value) + { + $fieldBoost = $document->getFieldBoost($key); + $key = htmlspecialchars($key, ENT_QUOTES, 'UTF-8'); + + if (is_array($value)) + { + foreach ($value as $multivalue) + { + $xml .= ''; + } + } + else + { + $xml .= ''; + } + } + + $xml .= ''; + + // replace any control characters to avoid Solr XML parser exception + return $this->_stripCtrlChars($xml); + } + + /** + * Replace control (non-printable) characters from string that are invalid to Solr's XML parser with a space. + * + * @param string $string + * @return string + */ + protected function _stripCtrlChars($string) + { + // See: http://w3.org/International/questions/qa-forms-utf-8.html + // Printable utf-8 does not include any of these chars below x7F + return preg_replace('@[\x00-\x08\x0B\x0C\x0E-\x1F]@', ' ', $string); + } + + /** + * Send a commit command. Will be synchronous unless both wait parameters are set to false. + * + * @param boolean $expungeDeletes Defaults to false, merge segments with deletes away + * @param boolean $waitFlush Defaults to true, block until index changes are flushed to disk + * @param boolean $waitSearcher Defaults to true, block until a new searcher is opened and registered as the main query searcher, making the changes visible + * @param float $timeout Maximum expected duration (in seconds) of the commit operation on the server (otherwise, will throw a communication exception). Defaults to 1 hour + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function commit($expungeDeletes = false, $waitFlush = true, $waitSearcher = true, $timeout = 3600) + { + $expungeValue = $expungeDeletes ? 'true' : 'false'; + $flushValue = $waitFlush ? 'true' : 'false'; + $searcherValue = $waitSearcher ? 'true' : 'false'; + + $rawPost = ''; + + return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout); + } + + /** + * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be + * a complete and well formed "delete" xml document + * + * @param string $rawPost Expected to be utf-8 encoded xml document + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function delete($rawPost, $timeout = 3600) + { + return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout); + } + + /** + * Create a delete document based on document ID + * + * @param string $id Expected to be utf-8 encoded + * @param boolean $fromPending + * @param boolean $fromCommitted + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function deleteById($id, $fromPending = true, $fromCommitted = true, $timeout = 3600) + { + $pendingValue = $fromPending ? 'true' : 'false'; + $committedValue = $fromCommitted ? 'true' : 'false'; + + //escape special xml characters + $id = htmlspecialchars($id, ENT_NOQUOTES, 'UTF-8'); + + $rawPost = '' . $id . ''; + + return $this->delete($rawPost, $timeout); + } + + /** + * Create and post a delete document based on multiple document IDs. + * + * @param array $ids Expected to be utf-8 encoded strings + * @param boolean $fromPending + * @param boolean $fromCommitted + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600) + { + $pendingValue = $fromPending ? 'true' : 'false'; + $committedValue = $fromCommitted ? 'true' : 'false'; + + $rawPost = ''; + + foreach ($ids as $id) + { + //escape special xml characters + $id = htmlspecialchars($id, ENT_NOQUOTES, 'UTF-8'); + + $rawPost .= '' . $id . ''; + } + + $rawPost .= ''; + + return $this->delete($rawPost, $timeout); + } + + /** + * Create a delete document based on a query and submit it + * + * @param string $rawQuery Expected to be utf-8 encoded + * @param boolean $fromPending + * @param boolean $fromCommitted + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true, $timeout = 3600) + { + $pendingValue = $fromPending ? 'true' : 'false'; + $committedValue = $fromCommitted ? 'true' : 'false'; + + // escape special xml characters + $rawQuery = htmlspecialchars($rawQuery, ENT_NOQUOTES, 'UTF-8'); + + $rawPost = '' . $rawQuery . ''; + + return $this->delete($rawPost, $timeout); + } + + /** + * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how + * to use Solr Cell and what parameters are available. + * + * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." + * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value + * pairs in the params array if they have the same name (e.g. you pass a "literal.id" key and value in your $params array but you also + * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). + * + * @param string $file Path to file to extract data from + * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) + * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) + * @param string $mimetype optional mimetype specification (for the file being extracted) + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_InvalidArgumentException if $file, $params, or $document are invalid. + */ + public function extract($file, $params = array(), $document = null, $mimetype = 'application/octet-stream') + { + // check if $params is an array (allow null for default empty array) + if (!is_null($params)) + { + if (!is_array($params)) + { + throw new Apache_Solr_InvalidArgumentException("\$params must be a valid array or null"); + } + } + else + { + $params = array(); + } + + // if $file is an http request, defer to extractFromUrl instead + if (substr($file, 0, 7) == 'http://' || substr($file, 0, 8) == 'https://') + { + return $this->extractFromUrl($file, $params, $document, $mimetype); + } + + // read the contents of the file + $contents = @file_get_contents($file); + + if ($contents !== false) + { + // add the resource.name parameter if not specified + if (!isset($params['resource.name'])) + { + $params['resource.name'] = basename($file); + } + + // delegate the rest to extractFromString + return $this->extractFromString($contents, $params, $document, $mimetype); + } + else + { + throw new Apache_Solr_InvalidArgumentException("File '{$file}' is empty or could not be read"); + } + } + + /** + * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how + * to use Solr Cell and what parameters are available. + * + * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." + * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value + * pairs in the params array if they have the same name (e.g. you pass a "literal.id" key and value in your $params array but you also + * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). + * + * @param string $data Data that will be passed to Solr Cell + * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) + * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) + * @param string $mimetype optional mimetype specification (for the file being extracted) + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_InvalidArgumentException if $file, $params, or $document are invalid. + * + * @todo Should be using multipart/form-data to post parameter values, but I could not get my implementation to work. Needs revisisted. + */ + public function extractFromString($data, $params = array(), $document = null, $mimetype = 'application/octet-stream') + { + // check if $params is an array (allow null for default empty array) + if (!is_null($params)) + { + if (!is_array($params)) + { + throw new Apache_Solr_InvalidArgumentException("\$params must be a valid array or null"); + } + } + else + { + $params = array(); + } + + // make sure we receive our response in JSON and have proper name list treatment + $params['wt'] = self::SOLR_WRITER; + $params['json.nl'] = $this->_namedListTreatment; + + // check if $document is an Apache_Solr_Document instance + if (!is_null($document) && $document instanceof Apache_Solr_Document) + { + // iterate document, adding literal.* and boost.* fields to $params as appropriate + foreach ($document as $field => $fieldValue) + { + // check if we need to add a boost.* parameters + $fieldBoost = $document->getFieldBoost($field); + + if ($fieldBoost !== false) + { + $params["boost.{$field}"] = $fieldBoost; + } + + // add the literal.* parameter + $params["literal.{$field}"] = $fieldValue; + } + } + + // params will be sent to SOLR in the QUERY STRING + $queryString = $this->_generateQueryString($params); + + // the file contents will be sent to SOLR as the POST BODY - we use application/octect-stream as default mimetype + return $this->_sendRawPost($this->_extractUrl . $this->_queryDelimiter . $queryString, $data, false, $mimetype); + } + + /** + * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how + * to use Solr Cell and what parameters are available. + * + * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." + * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value + * pairs in the params array if they have the same name (e.g. you pass a "literal.id" key and value in your $params array but you also + * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). + * + * @param string $url URL + * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) + * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) + * @param string $mimetype optional mimetype specification (for the file being extracted) + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_InvalidArgumentException if $url, $params, or $document are invalid. + */ + public function extractFromUrl($url, $params = array(), $document = null, $mimetype = 'application/octet-stream') + { + // check if $params is an array (allow null for default empty array) + if (!is_null($params)) + { + if (!is_array($params)) + { + throw new Apache_Solr_InvalidArgumentException("\$params must be a valid array or null"); + } + } + else + { + $params = array(); + } + + $httpTransport = $this->getHttpTransport(); + + // read the contents of the URL using our configured Http Transport and default timeout + $httpResponse = $httpTransport->performGetRequest($url); + + // check that its a 200 response + if ($httpResponse->getStatusCode() == 200) + { + // add the resource.name parameter if not specified + if (!isset($params['resource.name'])) + { + $params['resource.name'] = $url; + } + + // delegate the rest to extractFromString + return $this->extractFromString($httpResponse->getBody(), $params, $document, $mimetype); + } + else + { + throw new Apache_Solr_InvalidArgumentException("URL '{$url}' returned non 200 response code"); + } + } + + /** + * Send an optimize command. Will be synchronous unless both wait parameters are set + * to false. + * + * @param boolean $waitFlush + * @param boolean $waitSearcher + * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function optimize($waitFlush = true, $waitSearcher = true, $timeout = 3600) + { + $flushValue = $waitFlush ? 'true' : 'false'; + $searcherValue = $waitSearcher ? 'true' : 'false'; + + $rawPost = ''; + + return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout); + } + + /** + * Simple Search interface + * + * @param string $query The raw query string + * @param int $offset The starting offset for result documents + * @param int $limit The maximum number of result documents to return + * @param array $params key / value pairs for other query parameters (see Solr documentation), use arrays for parameter keys used more than once (e.g. facet.field) + * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + * @throws Apache_Solr_InvalidArgumentException If an invalid HTTP method is used + */ + public function search($query, $offset = 0, $limit = 10, $params = array(), $method = self::METHOD_GET) + { + // ensure params is an array + if (!is_null($params)) + { + if (!is_array($params)) + { + // params was specified but was not an array - invalid + throw new Apache_Solr_InvalidArgumentException("\$params must be a valid array or null"); + } + } + else + { + $params = array(); + } + + // construct our full parameters + + // common parameters in this interface + $params['wt'] = self::SOLR_WRITER; + $params['json.nl'] = $this->_namedListTreatment; + + $params['q'] = $query; + $params['start'] = $offset; + $params['rows'] = $limit; + + $queryString = $this->_generateQueryString($params); + + if ($method == self::METHOD_GET) + { + return $this->_sendRawGet($this->_searchUrl . $this->_queryDelimiter . $queryString); + } + else if ($method == self::METHOD_POST) + { + return $this->_sendRawPost($this->_searchUrl, $queryString, FALSE, 'application/x-www-form-urlencoded; charset=UTF-8'); + } + else + { + throw new Apache_Solr_InvalidArgumentException("Unsupported method '$method', please use the Apache_Solr_Service::METHOD_* constants"); + } + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/Apache/Solr/Service/Balancer.php b/thirdparty/solr-php-client/Apache/Solr/Service/Balancer.php new file mode 100644 index 00000000..1a8c0c98 --- /dev/null +++ b/thirdparty/solr-php-client/Apache/Solr/Service/Balancer.php @@ -0,0 +1,917 @@ +addReadService($service); + } + + //setup writeable services + foreach ($writeableServices as $service) + { + $this->addWriteService($service); + } + } + + public function setReadPingTimeout($timeout) + { + $this->_readPingTimeout = $timeout; + } + + public function setWritePingTimeout($timeout) + { + $this->_writePingTimeout = $timeout; + } + + public function setUseBackoff($enable) + { + $this->_useBackoff = $enable; + } + + /** + * Generates a service ID + * + * @param string $host + * @param integer $port + * @param string $path + * @return string + */ + protected function _getServiceId($host, $port, $path) + { + return $host . ':' . $port . $path; + } + + /** + * Adds a service instance or service descriptor (if it is already + * not added) + * + * @param mixed $service + * + * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid + */ + public function addReadService($service) + { + if ($service instanceof Apache_Solr_Service) + { + $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); + + $this->_readableServices[$id] = $service; + } + else if (is_array($service)) + { + if (isset($service['host']) && isset($service['port']) && isset($service['path'])) + { + $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); + + $this->_readableServices[$id] = $service; + } + else + { + throw new Apache_Solr_InvalidArgumentException('A Readable Service description array does not have all required elements of host, port, and path'); + } + } + } + + /** + * Removes a service instance or descriptor from the available services + * + * @param mixed $service + * + * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid + */ + public function removeReadService($service) + { + $id = ''; + + if ($service instanceof Apache_Solr_Service) + { + $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); + } + else if (is_array($service)) + { + if (isset($service['host']) && isset($service['port']) && isset($service['path'])) + { + $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); + } + else + { + throw new Apache_Solr_InvalidArgumentException('A Readable Service description array does not have all required elements of host, port, and path'); + } + } + else if (is_string($service)) + { + $id = $service; + } + + if ($id && isset($this->_readableServices[$id])) + { + unset($this->_readableServices[$id]); + } + } + + /** + * Adds a service instance or service descriptor (if it is already + * not added) + * + * @param mixed $service + * + * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid + */ + public function addWriteService($service) + { + if ($service instanceof Apache_Solr_Service) + { + $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); + + $this->_writeableServices[$id] = $service; + } + else if (is_array($service)) + { + if (isset($service['host']) && isset($service['port']) && isset($service['path'])) + { + $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); + + $this->_writeableServices[$id] = $service; + } + else + { + throw new Apache_Solr_InvalidArgumentException('A Writeable Service description array does not have all required elements of host, port, and path'); + } + } + } + + /** + * Removes a service instance or descriptor from the available services + * + * @param mixed $service + * + * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid + */ + public function removeWriteService($service) + { + $id = ''; + + if ($service instanceof Apache_Solr_Service) + { + $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); + } + else if (is_array($service)) + { + if (isset($service['host']) && isset($service['port']) && isset($service['path'])) + { + $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); + } + else + { + throw new Apache_Solr_InvalidArgumentException('A Readable Service description array does not have all required elements of host, port, and path'); + } + } + else if (is_string($service)) + { + $id = $service; + } + + if ($id && isset($this->_writeableServices[$id])) + { + unset($this->_writeableServices[$id]); + } + } + + /** + * Iterate through available read services and select the first with a ping + * that satisfies configured timeout restrictions (or the default) + * + * @return Apache_Solr_Service + * + * @throws Apache_Solr_NoServiceAvailableException If there are no read services that meet requirements + */ + protected function _selectReadService($forceSelect = false) + { + if (!$this->_currentReadService || !isset($this->_readableServices[$this->_currentReadService]) || $forceSelect) + { + if ($this->_currentReadService && isset($this->_readableServices[$this->_currentReadService]) && $forceSelect) + { + // we probably had a communication error, ping the current read service, remove it if it times out + if ($this->_readableServices[$this->_currentReadService]->ping($this->_readPingTimeout) === false) + { + $this->removeReadService($this->_currentReadService); + } + } + + if (count($this->_readableServices)) + { + // select one of the read services at random + $ids = array_keys($this->_readableServices); + + $id = $ids[rand(0, count($ids) - 1)]; + $service = $this->_readableServices[$id]; + + if (is_array($service)) + { + //convert the array definition to a client object + $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']); + $this->_readableServices[$id] = $service; + } + + $service->setCreateDocuments($this->_createDocuments); + $this->_currentReadService = $id; + } + else + { + throw new Apache_Solr_NoServiceAvailableException('No read services were available'); + } + } + + return $this->_readableServices[$this->_currentReadService]; + } + + /** + * Iterate through available write services and select the first with a ping + * that satisfies configured timeout restrictions (or the default) + * + * @return Apache_Solr_Service + * + * @throws Apache_Solr_NoServiceAvailableException If there are no write services that meet requirements + */ + protected function _selectWriteService($forceSelect = false) + { + if($this->_useBackoff) + { + return $this->_selectWriteServiceSafe($forceSelect); + } + + if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect) + { + if ($this->_currentWriteService && isset($this->_writeableServices[$this->_currentWriteService]) && $forceSelect) + { + // we probably had a communication error, ping the current read service, remove it if it times out + if ($this->_writeableServices[$this->_currentWriteService]->ping($this->_writePingTimeout) === false) + { + $this->removeWriteService($this->_currentWriteService); + } + } + + if (count($this->_writeableServices)) + { + // select one of the read services at random + $ids = array_keys($this->_writeableServices); + + $id = $ids[rand(0, count($ids) - 1)]; + $service = $this->_writeableServices[$id]; + + if (is_array($service)) + { + //convert the array definition to a client object + $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']); + $this->_writeableServices[$id] = $service; + } + + $this->_currentWriteService = $id; + } + else + { + throw new Apache_Solr_NoServiceAvailableException('No write services were available'); + } + } + + return $this->_writeableServices[$this->_currentWriteService]; + } + + /** + * Iterate through available write services and select the first with a ping + * that satisfies configured timeout restrictions (or the default). The + * timeout period will increase until a connection is made or the limit is + * reached. This will allow for increased reliability with heavily loaded + * server(s). + * + * @return Apache_Solr_Service + * + * @throws Apache_Solr_NoServiceAvailableException If there are no write services that meet requirements + */ + + protected function _selectWriteServiceSafe($forceSelect = false) + { + if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect) + { + if (count($this->_writeableServices)) + { + $backoff = $this->_defaultBackoff; + + do { + // select one of the read services at random + $ids = array_keys($this->_writeableServices); + + $id = $ids[rand(0, count($ids) - 1)]; + $service = $this->_writeableServices[$id]; + + if (is_array($service)) + { + //convert the array definition to a client object + $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']); + $this->_writeableServices[$id] = $service; + } + + $this->_currentWriteService = $id; + + $backoff *= $this->_backoffEscalation; + + if($backoff > $this->_backoffLimit) + { + throw new Apache_Solr_NoServiceAvailableException('No write services were available. All timeouts exceeded.'); + } + + } while($this->_writeableServices[$this->_currentWriteService]->ping($backoff) === false); + } + else + { + throw new Apache_Solr_NoServiceAvailableException('No write services were available'); + } + } + + return $this->_writeableServices[$this->_currentWriteService]; + } + + /** + * Set the create documents flag. This determines whether {@link Apache_Solr_Response} objects will + * parse the response and create {@link Apache_Solr_Document} instances in place. + * + * @param boolean $createDocuments + */ + public function setCreateDocuments($createDocuments) + { + $this->_createDocuments = (bool) $createDocuments; + + // set on current read service + if ($this->_currentReadService) + { + $service = $this->_selectReadService(); + $service->setCreateDocuments($createDocuments); + } + } + + /** + * Get the current state of teh create documents flag. + * + * @return boolean + */ + public function getCreateDocuments() + { + return $this->_createDocuments; + } + + /** + * Raw Add Method. Takes a raw post body and sends it to the update service. Post body + * should be a complete and well formed "add" xml document. + * + * @param string $rawPost + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function add($rawPost) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->add($rawPost); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Add a Solr Document to the index + * + * @param Apache_Solr_Document $document + * @param boolean $allowDups + * @param boolean $overwritePending + * @param boolean $overwriteCommitted + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->addDocument($document, $allowDups, $overwritePending, $overwriteCommitted); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Add an array of Solr Documents to the index all at once + * + * @param array $documents Should be an array of Apache_Solr_Document instances + * @param boolean $allowDups + * @param boolean $overwritePending + * @param boolean $overwriteCommitted + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->addDocuments($documents, $allowDups, $overwritePending, $overwriteCommitted); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Send a commit command. Will be synchronous unless both wait parameters are set + * to false. + * + * @param boolean $waitFlush + * @param boolean $waitSearcher + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function commit($optimize = true, $waitFlush = true, $waitSearcher = true, $timeout = 3600) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->commit($optimize, $waitFlush, $waitSearcher, $timeout); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be + * a complete and well formed "delete" xml document + * + * @param string $rawPost + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function delete($rawPost, $timeout = 3600) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->delete($rawPost, $timeout); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Create a delete document based on document ID + * + * @param string $id + * @param boolean $fromPending + * @param boolean $fromCommitted + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function deleteById($id, $fromPending = true, $fromCommitted = true, $timeout = 3600) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->deleteById($id, $fromPending, $fromCommitted, $timeout); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Create and post a delete document based on multiple document IDs. + * + * @param array $ids Expected to be utf-8 encoded strings + * @param boolean $fromPending + * @param boolean $fromCommitted + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->deleteByMultipleId($ids, $fromPending, $fromCommitted, $timeout); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Create a delete document based on a query and submit it + * + * @param string $rawQuery + * @param boolean $fromPending + * @param boolean $fromCommitted + * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true, $timeout = 3600) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->deleteByQuery($rawQuery, $fromPending, $fromCommitted, $timeout); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how + * to use Solr Cell and what parameters are available. + * + * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." + * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value + * pairs in the params array if they have the same name (e.g. you pass a "literal.id" key and value in your $params array but you also + * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). + * + * @param string $file Path to file to extract data from + * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) + * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) + * @param string $mimetype optional mimetype specification (for the file being extracted) + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_InvalidArgumentException if $file, $params, or $document are invalid. + */ + public function extract($file, $params = array(), $document = null, $mimetype = 'application/octet-stream') + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->extract($file, $params, $document, $mimetype); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how + * to use Solr Cell and what parameters are available. + * + * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." + * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value + * pairs in the params array if they have the same name (e.g. you pass a "literal.id" key and value in your $params array but you also + * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). + * + * @param string $data Data that will be passed to Solr Cell + * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) + * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) + * @param string $mimetype optional mimetype specification (for the file being extracted) + * + * @return Apache_Solr_Response + * + * @throws Apache_Solr_InvalidArgumentException if $file, $params, or $document are invalid. + * + * @todo Should be using multipart/form-data to post parameter values, but I could not get my implementation to work. Needs revisisted. + */ + public function extractFromString($data, $params = array(), $document = null, $mimetype = 'application/octet-stream') + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->extractFromString($data, $params, $document, $mimetype); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Send an optimize command. Will be synchronous unless both wait parameters are set + * to false. + * + * @param boolean $waitFlush + * @param boolean $waitSearcher + * @param float $timeout Maximum expected duration of the optimize operation on the server (otherwise, will throw a communication exception) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function optimize($waitFlush = true, $waitSearcher = true, $timeout = 3600) + { + $service = $this->_selectWriteService(); + + do + { + try + { + return $service->optimize($waitFlush, $waitSearcher, $timeout); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectWriteService(true); + } while ($service); + + return false; + } + + /** + * Simple Search interface + * + * @param string $query The raw query string + * @param int $offset The starting offset for result documents + * @param int $limit The maximum number of result documents to return + * @param array $params key / value pairs for query parameters, use arrays for multivalued parameters + * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST) + * @return Apache_Solr_Response + * + * @throws Apache_Solr_HttpTransportException If an error occurs during the service call + */ + public function search($query, $offset = 0, $limit = 10, $params = array(), $method = Apache_Solr_Service::METHOD_GET) + { + $service = $this->_selectReadService(); + + do + { + try + { + return $service->search($query, $offset, $limit, $params, $method); + } + catch (Apache_Solr_HttpTransportException $e) + { + if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR + { + throw $e; + } + } + + $service = $this->_selectReadService(true); + } while ($service); + + return false; + } +} diff --git a/thirdparty/solr-php-client/COPYING b/thirdparty/solr-php-client/COPYING new file mode 100644 index 00000000..499af4bc --- /dev/null +++ b/thirdparty/solr-php-client/COPYING @@ -0,0 +1,26 @@ +Copyright (c) 2007-2013, PTC Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of PTC Inc. nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/solr-php-client/README.md b/thirdparty/solr-php-client/README.md new file mode 100644 index 00000000..7edf13be --- /dev/null +++ b/thirdparty/solr-php-client/README.md @@ -0,0 +1,15 @@ +# Solr PHP Client + +A purely PHP library for indexing and searching documents against an Apache Solr installation. + +This project was formerly maintained at [http://code.google.com/p/solr-php-client](http://code.google.com/p/solr-php-client). Previous issues and downloads are still available for viewing there. + +## Need Help? + +Try starting with the [FAQ](https://github.com/PTCInc/solr-php-client/wiki/FAQ) and [Example Usage](https://github.com/PTCInc/solr-php-client/wiki/Example-Usage). + +Also, feel free to search and ask questions on the google mailing list: [php-solr-client@googlegroups.com](mailto:php-solr-client@googlegroups.com) / [Group Page](https://groups.google.com/forum/?fromgroups#!forum/php-solr-client) + +## License + +Code is released under a 3-Clause BSD license, see [COPYING](https://github.com/PTCInc/solr-php-client/blob/master/COPYING) for full text. \ No newline at end of file diff --git a/thirdparty/solr-php-client/composer.json b/thirdparty/solr-php-client/composer.json new file mode 100644 index 00000000..e775aeff --- /dev/null +++ b/thirdparty/solr-php-client/composer.json @@ -0,0 +1,17 @@ +{ + "name": "ptcinc/solr-php-client", + "description": "A purely PHP library for indexing and searching documents against an Apache Solr installation", + "keywords": ["php", "solr", "client"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Donovan Jimenez", + "homepage": "https://github.com/djimenez" + } + ], + "autoload": { + "psr-0": { + "Apache_Solr_": "" + } + } +} diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/DocumentTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/DocumentTest.php new file mode 100644 index 00000000..0a1e3617 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/DocumentTest.php @@ -0,0 +1,439 @@ + + */ + +/** + * Apache_Solr_Document Unit Test + */ +class Apache_Solr_DocumentTest extends PHPUnit_Framework_TestCase +{ + /** + * Fixture used for testing + * + * @var Apache_Solr_Document + */ + private $_fixture; + + /** + * Setup for the fixture before each unit test - part of test case API + */ + protected function setup() + { + $this->_fixture = new Apache_Solr_Document(); + } + + /** + * Teardown after each unit test - part of test case API + */ + protected function tearDown() + { + unset($this->_fixture); + } + + public function testDefaultStateAfterConstructor() + { + // document boost should be false + $this->assertFalse($this->_fixture->getBoost()); + + // document fields should be empty + $this->assertEquals(0, count($this->_fixture->getFieldNames())); + $this->assertEquals(0, count($this->_fixture->getFieldValues())); + $this->assertEquals(0, count($this->_fixture->getFieldBoosts())); + + // document iterator should be empty + $this->assertEquals(0, iterator_count($this->_fixture)); + } + + public function testSetAndGetField() + { + $field = 'field'; + $value = 'value'; + $boost = 0.5; + + // set the field + $this->_fixture->setField($field, $value, $boost); + + $result = $this->_fixture->getField($field); + + // check the array values + $this->assertTrue(is_array($result)); + $this->assertEquals($field, $result['name']); + $this->assertEquals($value, $result['value']); + $this->assertEquals($boost, $result['boost']); + } + + public function testGetFieldReturnsFalseForNonExistentField() + { + $this->assertFalse($this->_fixture->getField('field')); + } + + public function testMagicGetForFieldValues() + { + $field = 'field'; + $value = 'value'; + + $this->_fixture->setField($field, $value); + + // test the __get value + $this->assertEquals($value, $this->_fixture->{$field}); + } + + /** + * Added for issue #48 (http://code.google.com/p/solr-php-client/issues/detail?id=48) + */ + public function testMagicGetReturnsNullForNonExistentField() + { + $this->assertNull($this->_fixture->nonExistent); + } + + public function testMagicSetForFieldValues() + { + $field = 'field'; + $value = 'value'; + + // set field value with magic __set + $this->_fixture->{$field} = $value; + + $fieldArray = $this->_fixture->getField($field); + + // set values + $this->assertEquals($field, $fieldArray['name']); + $this->assertEquals($value, $fieldArray['value']); + $this->assertTrue($fieldArray['boost'] === false); + } + + public function testMagicIssetForNonExistentField() + { + $this->assertFalse(isset($this->_fixture->field)); + } + + public function testMagicIssetForExistingField() + { + $field = 'field'; + $this->_fixture->{$field} = 'value'; + $this->assertTrue(isset($this->_fixture->{$field})); + } + + public function testMagicUnsetForExistingField() + { + $field = 'field'; + + $this->_fixture->{$field} = 'value'; + + // now unset the field + unset($this->_fixture->{$field}); + + // now test that its unset + $this->assertFalse(isset($this->_fixture->{$field})); + } + + public function testMagicUnsetForNonExistingField() + { + $field = 'field'; + unset($this->_fixture->{$field}); + + // now test that it still does not exist + $this->assertFalse(isset($this->_fixture->{$field})); + } + + public function testSetAndGetFieldBoostWithPositiveNumberSetsBoost() + { + $field = 'field'; + $boost = 0.5; + + $this->_fixture->setFieldBoost($field, $boost); + + // test the field boost + $this->assertEquals($boost, $this->_fixture->getFieldBoost($field)); + } + + public function testSetAndGetFieldBoostWithZeroRemovesBoost() + { + $field = 'field'; + $boost = 0; + + $this->_fixture->setFieldBoost($field, $boost); + + // test the field boost + $this->assertTrue($this->_fixture->getFieldBoost($field) === false); + } + + public function testSetAndGetFieldBoostWithNegativeNumberRemovesBoost() + { + $field = 'field'; + $boost = -1; + + $this->_fixture->setFieldBoost($field, $boost); + + // test the field boost + $this->assertTrue($this->_fixture->getFieldBoost($field) === false); + } + + public function testSetAndGetFieldBoostWithNonNumberRemovesBoost() + { + $field = 'field'; + $boost = "i am not a number"; + + $this->_fixture->setFieldBoost($field, $boost); + + // test the field boost + $this->assertTrue($this->_fixture->getFieldBoost($field) === false); + } + + public function testSetAndGetBoostWithPositiveNumberSetsBoost() + { + $boost = 0.5; + $this->_fixture->setBoost($boost); + + // the boost should now be set + $this->assertEquals($boost, $this->_fixture->getBoost()); + } + + public function testSetAndGetBoostWithZeroRemovesBoost() + { + $this->_fixture->setBoost(0); + + // should be boolean false + $this->assertTrue($this->_fixture->getBoost() === false); + } + + public function testSetAndGetBoostWithNegativeNumberRemovesBoost() + { + $this->_fixture->setBoost(-1); + + // should be boolean false + $this->assertTrue($this->_fixture->getBoost() === false); + } + + public function testSetAndGetBoostWithNonNumberRemovesBoost() + { + $this->_fixture->setBoost("i am not a number"); + + // should be boolean false + $this->assertTrue($this->_fixture->getBoost() === false); + } + + public function testAddFieldCreatesMultiValueWhenFieldDoesNotExist() + { + $field = 'field'; + $value = 'value'; + + $this->_fixture->addField($field, $value); + + // check that value is an array with correct values + $fieldValue = $this->_fixture->{$field}; + + $this->assertTrue(is_array($fieldValue)); + $this->assertEquals(1, count($fieldValue)); + $this->assertEquals($value, $fieldValue[0]); + } + + /** + * setMultiValue has been deprecated and defers to addField + * + * @deprecated + */ + public function testSetMultiValueCreateMultiValueWhenFieldDoesNotExist() + { + $field = 'field'; + $value = 'value'; + + $this->_fixture->setMultiValue($field, $value); + + // check that value is an array with correct values + $fieldValue = $this->_fixture->{$field}; + + $this->assertTrue(is_array($fieldValue)); + $this->assertEquals(1, count($fieldValue)); + $this->assertEquals($value, $fieldValue[0]); + } + + public function testAddFieldCreatesMultiValueWhenFieldDoesExistAsSingleValue() + { + $field = 'field'; + $value1 = 'value1'; + $value2 = 'value2'; + + // set first value as singular value + $this->_fixture->{$field} = $value1; + + // add a second value with addField + $this->_fixture->addField($field, $value2); + + // check that value is an array with correct values + $fieldValue = $this->_fixture->{$field}; + + $this->assertTrue(is_array($fieldValue)); + $this->assertEquals(2, count($fieldValue)); + $this->assertEquals($value1, $fieldValue[0]); + $this->assertEquals($value2, $fieldValue[1]); + } + + /** + * setMultiValue has been deprecated and defers to addField + * + * @deprecated + */ + public function testSetMultiValueCreatesMultiValueWhenFieldDoesExistAsSingleValue() + { + $field = 'field'; + $value1 = 'value1'; + $value2 = 'value2'; + + // set first value as singular value + $this->_fixture->{$field} = $value1; + + // add a second value with addField + $this->_fixture->setMultiValue($field, $value2); + + // check that value is an array with correct values + $fieldValue = $this->_fixture->{$field}; + + $this->assertTrue(is_array($fieldValue)); + $this->assertEquals(2, count($fieldValue)); + $this->assertEquals($value1, $fieldValue[0]); + $this->assertEquals($value2, $fieldValue[1]); + } + + public function testAddFieldWithBoostSetsFieldBoost() + { + $field = 'field'; + $boost = 0.5; + + $this->_fixture->addField($field, 'value', $boost); + + // check the field boost + $this->assertEquals($boost, $this->_fixture->getFieldBoost($field)); + } + + public function testAddFieldWithBoostMultipliesWithAPreexistingBoost() + { + $field = 'field'; + $boost = 0.5; + + // set a field with a boost + $this->_fixture->setField($field, 'value1', $boost); + + // now add another value with the same boost + $this->_fixture->addField($field, 'value2', $boost); + + // new boost should be $boost * $boost + $this->assertEquals($boost * $boost, $this->_fixture->getFieldBoost($field)); + } + + public function testGetFieldNamesIsInitiallyEmpty() + { + $fieldNames = $this->_fixture->getFieldNames(); + + $this->assertTrue(empty($fieldNames)); + } + + public function testGetFieldNamesAfterFieldIsSetIsNotEmpty() + { + $field = 'field'; + + $this->_fixture->{$field} = 'value'; + $fieldNames = $this->_fixture->getFieldNames(); + + $this->assertTrue(!empty($fieldNames)); + $this->assertEquals(1, count($fieldNames)); + $this->assertEquals($field, $fieldNames[0]); + } + + public function testGetFieldValuesIsInitiallyEmpty() + { + $fieldValues = $this->_fixture->getFieldValues(); + + $this->assertTrue(empty($fieldValues)); + } + + public function testGetFieldValuessAfterFieldIsSetIsNotEmpty() + { + $value = 'value'; + + $this->_fixture->field = $value; + $fieldValues = $this->_fixture->getFieldValues(); + + $this->assertTrue(!empty($fieldValues)); + $this->assertEquals(1, count($fieldValues)); + $this->assertEquals($value, $fieldValues[0]); + } + + public function testGetIteratorAfterFieldValueIsSet() + { + $field = 'field'; + $value = 'value'; + + $this->_fixture->{$field} = $value; + + $itemCount = 0; + + foreach ($this->_fixture as $iteratedField => $iteratedValue) + { + ++$itemCount; + + // test field and value + $this->assertEquals($field, $iteratedField); + $this->assertEquals($value, $iteratedValue); + } + + // test number of iterations is 1 + $this->assertEquals(1, $itemCount); + } + + public function testClearReturnsDocumentToDefaultState() + { + // set the document boost + $this->_fixture->setBoost(0.5); + + // set a field + $this->_fixture->someField = "some value"; + + // clear the document to remove boost and fields + $this->_fixture->clear(); + + // document boost should now be false + $this->assertFalse($this->_fixture->getBoost()); + + // document fields should now be empty + $this->assertEquals(0, count($this->_fixture->getFieldNames())); + $this->assertEquals(0, count($this->_fixture->getFieldValues())); + $this->assertEquals(0, count($this->_fixture->getFieldBoosts())); + + // document iterator should now be empty + $this->assertEquals(0, iterator_count($this->_fixture)); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/AbstractTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/AbstractTest.php new file mode 100644 index 00000000..9fdefb1d --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/AbstractTest.php @@ -0,0 +1,208 @@ + + */ + +/** + * Apache_Solr_HttpTransport_Abstract Unit Tests + */ +abstract class Apache_Solr_HttpTransport_AbstractTest extends PHPUnit_Framework_TestCase +{ + const TIMEOUT = 2; + + // request our copyright file from googlecode for GET and HEAD + const GET_URL = "http://solr-php-client.googlecode.com/svn/trunk/COPYING"; + const GET_RESPONSE_MIME_TYPE = 'text/plain'; + const GET_RESPONSE_ENCODING = 'UTF-8'; + const GET_RESPONSE_MATCH = 'Copyright (c) '; + + // post to the issue list page with a search for 'meh' + const POST_URL = "http://code.google.com/p/solr-php-client/issues/list"; + const POST_DATA = "can=2&q=meh&colspec=ID+Type+Status+Priority+Milestone+Owner+Summary&cells=tiles"; + const POST_REQUEST_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=UTF-8'; + + const POST_RESPONSE_MIME_TYPE = 'text/html'; + const POST_RESPONSE_ENCODING = 'UTF-8'; + //const POST_RESPONSE_MATCH = 'not sure'; + + abstract public function getFixture(); + + public function testGetDefaultTimeoutWithDefaultConstructor() + { + $fixture = $this->getFixture(); + $timeout = $fixture->getDefaultTimeout(); + + $this->assertGreaterThan(0, $timeout); + } + + public function testGetDefaultTimeoutSetToSixtyForBadValues() + { + // first set our default_socket_timeout ini setting + $previousValue = ini_get('default_socket_timeout'); + ini_set('default_socket_timeout', 0); + + $fixture = $this->getFixture(); + $timeout = $fixture->getDefaultTimeout(); + + // reset timeout + ini_set('default_socket_timeout', $previousValue); + + $this->assertEquals(60, $timeout); + } + + public function testSetDefaultTimeout() + { + $newTimeout = 1234; + + $fixture = $this->getFixture(); + $fixture->setDefaultTimeout($newTimeout); + $timeout = $fixture->getDefaultTimeout(); + + $this->assertEquals($newTimeout, $timeout); + } + + public function testPerformGetRequest() + { + $fixture = $this->getFixture(); + $fixture->setDefaultTimeout(self::TIMEOUT); + + $response = $fixture->performGetRequest(self::GET_URL); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); + + $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); + $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); + $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); + $this->assertStringStartsWith(self::GET_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); + } + + public function testPerformGetRequestWithTimeout() + { + $fixture = $this->getFixture(); + $response = $fixture->performGetRequest(self::GET_URL, self::TIMEOUT); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); + + $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); + $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); + $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); + $this->assertStringStartsWith(self::GET_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); + } + + public function testPerformHeadRequest() + { + $fixture = $this->getFixture(); + $fixture->setDefaultTimeout(self::TIMEOUT); + + $response = $fixture->performHeadRequest(self::GET_URL); + + // we should get everything the same as a get, except the body + $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); + + $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); + $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); + $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); + $this->assertEquals("", $response->getBody(), 'body was not empty'); + } + + public function testPerformHeadRequestWithTimeout() + { + $fixture = $this->getFixture(); + $response = $fixture->performHeadRequest(self::GET_URL, self::TIMEOUT); + + // we should get everything the same as a get, except the body + $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); + + $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); + $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); + $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); + $this->assertEquals("", $response->getBody(), 'body was not empty'); + } + + public function testPerformPostRequest() + { + $fixture = $this->getFixture(); + $fixture->setDefaultTimeout(self::TIMEOUT); + + $response = $fixture->performPostRequest(self::POST_URL, self::POST_DATA, self::POST_REQUEST_CONTENT_TYPE); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); + + $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); + $this->assertEquals(self::POST_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); + $this->assertEquals(self::POST_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); + //$this->assertStringStartsWith(self::POST_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); + } + + public function testPerformPostRequestWithTimeout() + { + $fixture = $this->getFixture(); + $response = $fixture->performPostRequest(self::POST_URL, self::POST_DATA, self::POST_REQUEST_CONTENT_TYPE, self::TIMEOUT); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); + + $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); + $this->assertEquals(self::POST_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); + $this->assertEquals(self::POST_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); + //$this->assertStringStartsWith(self::POST_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); + } + + /** + * Test one session doing multiple requests in multiple orders + */ + public function testMultipleRequests() + { + // initial get request + $this->testPerformGetRequest(); + + // head following get + $this->testPerformHeadRequest(); + + // post following head + $this->testPerformPostRequest(); + + // get following post + $this->testPerformGetRequest(); + + // post following get + $this->testPerformPostRequest(); + + // head following post + $this->testPerformHeadRequest(); + + // get following post + $this->testPerformGetRequest(); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/CurlNoReuseTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/CurlNoReuseTest.php new file mode 100644 index 00000000..96297c35 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/CurlNoReuseTest.php @@ -0,0 +1,53 @@ + + */ + +/** + * Apache_Solr_HttpTransport_CurlNoReuse Unit Tests + */ +class Apache_Solr_HttpTransport_CurlNoReuseTest extends Apache_Solr_HttpTransport_AbstractTest +{ + public function getFixture() + { + // ensure curl is enabled + if (!extension_loaded('curl')) + { + $this->markTestSkipped("curl module is not enabled"); + } + + return new Apache_Solr_HttpTransport_CurlNoReuse(); + } +} diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/CurlTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/CurlTest.php new file mode 100644 index 00000000..0dd8bef8 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/CurlTest.php @@ -0,0 +1,53 @@ + + */ + +/** + * Apache_Solr_HttpTransport_Curl Unit Tests + */ +class Apache_Solr_HttpTransport_CurlTest extends Apache_Solr_HttpTransport_AbstractTest +{ + public function getFixture() + { + // ensure curl is enabled + if (!extension_loaded('curl')) + { + $this->markTestSkipped("curl module is not enabled"); + } + + return new Apache_Solr_HttpTransport_Curl(); + } +} diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/FileGetContentsTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/FileGetContentsTest.php new file mode 100644 index 00000000..f039741d --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/FileGetContentsTest.php @@ -0,0 +1,53 @@ + + */ + +/** + * Apache_Solr_HttpTransport_FileGetContents Unit Tests + */ +class Apache_Solr_HttpTransport_FileGetContentsTest extends Apache_Solr_HttpTransport_AbstractTest +{ + public function getFixture() + { + // make sure allow_url_fopen is on + if (!ini_get("allow_url_fopen")) + { + $this->markTestSkipped("allow_url_fopen is not enabled"); + } + + return new Apache_Solr_HttpTransport_FileGetContents(); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/ResponseTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/ResponseTest.php new file mode 100644 index 00000000..ee3729b3 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransport/ResponseTest.php @@ -0,0 +1,164 @@ + + */ + +/** + * Apache_Solr_HttpTransport_Response Unit Tests + */ +class Apache_Solr_HttpTransport_ResponseTest extends PHPUnit_Framework_TestCase +{ + // generated with the following query string: select?q=solr&wt=json + const STATUS_CODE_200 = 200; + const STATUS_MESSAGE_200 = "OK"; + const BODY_200 = '{"responseHeader":{"status":0,"QTime":0,"params":{"q":"solr","wt":"json"}},"response":{"numFound":0,"start":0,"docs":[]}}'; + const BODY_200_WITH_DOCUMENTS = '{"responseHeader":{"status":0,"QTime":0,"params":{"q":"*:*","wt":"json"}},"response":{"numFound":1,"start":0,"docs":[{"guid":"dev/2/products/45410/1236981","cit_domain":"products","cit_client":"2","cit_instance":"dev","cit_timestamp":"2010-10-06T18:16:51.573Z","product_code_t":["235784"],"product_id":[1236981],"dealer_id":[45410],"category_id":[1030],"manufacturer_id":[0],"vendor_id":[472],"catalog_id":[202]}]}}'; + const CONTENT_TYPE_200 = "text/plain; charset=utf-8"; + const MIME_TYPE_200 = "text/plain"; + const ENCODING_200 = "utf-8"; + + // generated with the following query string: select?qt=standad&q=solr&wt=json + // NOTE: the intentional mispelling of the standard in the qt parameter + const STATUS_CODE_400 = 400; + const STATUS_MESSAGE_400 = "Bad Request"; + const BODY_400 = 'Apache Tomcat/6.0.24 - Error report

HTTP Status 400 - unknown handler: standad


type Status report

message unknown handler: standad

description The request sent by the client was syntactically incorrect (unknown handler: standad).


Apache Tomcat/6.0.24

'; + const CONTENT_TYPE_400 = "text/html; charset=utf-8"; + const MIME_TYPE_400 = "text/html"; + const ENCODING_400 = "utf-8"; + + // generated with the following query string: select?q=solr&wt=json on a core that does not exist + const STATUS_CODE_404 = 404; + const STATUS_MESSAGE_404 = "Not Found"; + const BODY_404 = 'Apache Tomcat/6.0.24 - Error report

HTTP Status 404 - /solr/doesnotexist/select


type Status report

message /solr/doesnotexist/select

description The requested resource (/solr/doesnotexist/select) is not available.


Apache Tomcat/6.0.24

'; + const CONTENT_TYPE_404 = "text/html; charset=utf-8"; + const MIME_TYPE_404 = "text/html"; + const ENCODING_404 = "utf-8"; + + public static function get0Response() + { + return new Apache_Solr_HttpTransport_Response(null, null, null); + } + + public static function get200Response() + { + return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_200, self::CONTENT_TYPE_200, self::BODY_200); + } + + public static function get200ResponseWithDocuments() + { + return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_200, self::CONTENT_TYPE_200, self::BODY_200_WITH_DOCUMENTS); + } + + public static function get400Response() + { + return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_400, self::CONTENT_TYPE_400, self::BODY_400); + } + + public static function get404Response() + { + return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_404, self::CONTENT_TYPE_404, self::BODY_404); + } + + public function testGetStatusCode() + { + $fixture = self::get200Response(); + + $statusCode = $fixture->getStatusCode(); + + $this->assertEquals(self::STATUS_CODE_200, $statusCode); + } + + public function testGetStatusMessage() + { + $fixture = self::get200Response(); + + $statusMessage = $fixture->getStatusMessage(); + + $this->assertEquals(self::STATUS_MESSAGE_200, $statusMessage); + } + + public function testGetStatusMessageWithUnknownCode() + { + $fixture = new Apache_Solr_HttpTransport_Response(499, null, null); + + $statusMessage = $fixture->getStatusMessage(); + $this->assertEquals("Unknown Status", $statusMessage); + } + + public function testGetBody() + { + $fixture = self::get200Response(); + + $body = $fixture->getBody(); + + $this->assertEquals(self::BODY_200, $body); + } + + public function testGetMimeType() + { + $fixture = self::get200Response(); + + $mimeType = $fixture->getMimeType(); + + $this->assertEquals(self::MIME_TYPE_200, $mimeType); + } + + public function testGetEncoding() + { + $fixture = self::get200Response(); + + $encoding = $fixture->getEncoding(); + + $this->assertEquals(self::ENCODING_200, $encoding); + } + + public function testGetStatusMessageWhenNotProvided() + { + // test 4 of the most common status code responses, probably don't need + // to test all the codes we have + + $fixture = new Apache_Solr_HttpTransport_Response(null, null, null, null, null); + $this->assertEquals("Communication Error", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 0'); + + $fixture = new Apache_Solr_HttpTransport_Response(200, null, null, null, null); + $this->assertEquals("OK", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 200'); + + $fixture = new Apache_Solr_HttpTransport_Response(400, null, null, null, null); + $this->assertEquals("Bad Request", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 400'); + + $fixture = new Apache_Solr_HttpTransport_Response(404, null, null, null, null); + $this->assertEquals("Not Found", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 404'); + } +} diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransportExceptionTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransportExceptionTest.php new file mode 100644 index 00000000..7285e312 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/HttpTransportExceptionTest.php @@ -0,0 +1,58 @@ + + */ + +/** + * Apache_Solr_HttpTransportException Unit Tests + */ +class Apache_Solr_HttpTransportExceptionTest extends PHPUnit_Framework_TestCase +{ + /** + * @expectedException PHPUnit_Framework_Error + */ + public function testConstructorRequiresResponse() + { + $fixture = new Apache_Solr_HttpTransportException(); + } + + public function testGetResponse() + { + $response = Apache_Solr_ResponseTest::get0Response(); + $fixture = new Apache_Solr_HttpTransportException($response); + + $this->assertEquals($response, $fixture->getResponse()); + } +} diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/ResponseTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/ResponseTest.php new file mode 100644 index 00000000..2901b9bf --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/ResponseTest.php @@ -0,0 +1,194 @@ + + */ + +/** + * Apache_Solr_Response Unit Test + */ +class Apache_Solr_ResponseTest extends PHPUnit_Framework_TestCase +{ + static public function get0Response($createDocuments = true, $collapseSingleValueArrays = true) + { + return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get0Response(), $createDocuments, $collapseSingleValueArrays); + } + + static public function get200Response($createDocuments = true, $collapseSingleValueArrays = true) + { + return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get200Response(), $createDocuments, $collapseSingleValueArrays); + } + + static public function get200ResponseWithDocuments($createDocuments = true, $collapseSingleValueArrays = true) + { + return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get200ResponseWithDocuments(), $createDocuments, $collapseSingleValueArrays); + } + + static public function get400Response($createDocuments = true, $collapseSingleValueArrays = true) + { + return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get400Response(), $createDocuments, $collapseSingleValueArrays); + } + + static public function get404Response($createDocuments = true, $collapseSingleValueArrays = true) + { + return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get404Response(), $createDocuments, $collapseSingleValueArrays); + } + + public function testConstuctorWithValidBodyAndHeaders() + { + $fixture = self::get200Response(); + + // check that we parsed the HTTP status correctly + $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::STATUS_CODE_200, $fixture->getHttpStatus()); + + // check that we received the body correctly + $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::BODY_200, $fixture->getRawResponse()); + + // check that our defaults are correct + $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::ENCODING_200, $fixture->getEncoding()); + $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::MIME_TYPE_200, $fixture->getType()); + } + + public function testConstructorWithBadBodyAndHeaders() + { + $fixture = self::get0Response(); + + // check that our defaults are correct + $this->assertEquals(0, $fixture->getHttpStatus()); + $this->assertEquals("UTF-8", $fixture->getEncoding()); + $this->assertEquals("text/plain", $fixture->getType()); + } + + public function testMagicGetWithValidBodyAndHeaders() + { + $fixture = self::get200Response(); + + // test top level gets + $this->assertInstanceOf('stdClass', $fixture->responseHeader); + $this->assertEquals(0, $fixture->responseHeader->status); + $this->assertEquals(0, $fixture->responseHeader->QTime); + + $this->assertInstanceOf('stdClass', $fixture->response); + $this->assertEquals(0, $fixture->response->numFound); + + $this->assertTrue(is_array($fixture->response->docs)); + $this->assertEquals(0, count($fixture->response->docs)); + } + + /** + * @expectedException Apache_Solr_ParserException + */ + public function testMagicGetWith0Response() + { + $fixture = self::get0Response(); + + // attempting to magic get a part of the response + // should throw a ParserException + $fixture->responseHeader; + + $this->fail("Expected Apache_Solr_ParserException was not raised"); + } + + /** + * @expectedException Apache_Solr_ParserException + */ + public function testMagicGetWith400Response() + { + $fixture = self::get400Response(); + + // attempting to magic get a part of the response + // should throw a ParserException + $fixture->responseHeader; + + $this->fail("Expected Apache_Solr_ParserException was not raised"); + } + + /** + * @expectedException Apache_Solr_ParserException + */ + public function testMagicGetWith404Response() + { + $fixture = self::get404Response(); + + // attempting to magic get a part of the response + // should throw a ParserException + $fixture->responseHeader; + + $this->fail("Expected Apache_Solr_ParserException was not raised"); + } + + public function testCreateDocuments() + { + $fixture = self::get200ResponseWithDocuments(); + + $this->assertTrue(count($fixture->response->docs) > 0, 'There are not 1 or more documents, cannot test'); + $this->assertInstanceOf('Apache_Solr_Document', $fixture->response->docs[0], 'The first document is not of type Apache_Solr_Document'); + } + + public function testDontCreateDocuments() + { + $fixture = self::get200ResponseWithDocuments(false); + + $this->assertTrue(count($fixture->response->docs) > 0, 'There are not 1 or more documents, cannot test'); + $this->assertInstanceOf('stdClass', $fixture->response->docs[0], 'The first document is not of type stdClass'); + } + + public function testGetHttpStatusMessage() + { + $fixture = self::get200Response(); + + $this->assertEquals("OK", $fixture->getHttpStatusMessage()); + } + + public function testMagicGetReturnsNullForUndefinedData() + { + $fixture = self::get200Response(); + + $this->assertNull($fixture->doesnotexist); + } + + public function testMagicIssetForDefinedProperty() + { + $fixture = self::get200Response(); + + $this->assertTrue(isset($fixture->responseHeader)); + } + + public function testMagicIssetForUndefinedProperty() + { + $fixture = self::get200Response(); + + $this->assertFalse(isset($fixture->doesnotexist)); + } +} diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/Service/BalancerTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/Service/BalancerTest.php new file mode 100644 index 00000000..6b2c3523 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/Service/BalancerTest.php @@ -0,0 +1,47 @@ + + */ + +/** + * Apache_Solr_Service_Balancer Unit Tests + */ +class Apache_Solr_Service_BalancerTest extends Apache_Solr_ServiceAbstractTest +{ + public function getFixture() + { + return new Apache_Solr_Service_Balancer(); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/ServiceAbstractTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/ServiceAbstractTest.php new file mode 100644 index 00000000..50de339b --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/ServiceAbstractTest.php @@ -0,0 +1,139 @@ + + */ + +/** + * Provides base funcationality test for both Apache_Solr_Service and the + * Apache_Solr_Service_Balancer classes. + */ +abstract class Apache_Solr_ServiceAbstractTest extends PHPUnit_Framework_TestCase +{ + /** + * Method that gets the appropriate instance for testing + */ + abstract public function getFixture(); + + /** + * @dataProvider testEscapeDataProvider + */ + public function testEscape($input, $expectedOutput) + { + $fixture = $this->getFixture(); + + $this->assertEquals($expectedOutput, $fixture->escape($input)); + } + + public function testEscapeDataProvider() + { + return array( + array( + "I should look the same", + "I should look the same" + ), + + array( + "(There) are: ^lots \\ && of spec!al charaters", + "\\(There\\) are\\: \\^lots \\\\ \\&& of spec\\!al charaters" + ) + ); + } + + /** + * @dataProvider testEscapePhraseDataProvider + */ + public function testEscapePhrase($input, $expectedOutput) + { + $fixture = $this->getFixture(); + + $this->assertEquals($expectedOutput, $fixture->escapePhrase($input)); + } + + public function testEscapePhraseDataProvider() + { + return array( + array( + "I'm a simple phrase", + "I'm a simple phrase" + ), + + array( + "I have \"phrase\" characters", + 'I have \\"phrase\\" characters' + ) + ); + } + + /** + * @dataProvider testPhraseDataProvider + */ + public function testPhrase($input, $expectedOutput) + { + $fixture = $this->getFixture(); + + $this->assertEquals($expectedOutput, $fixture->phrase($input)); + } + + public function testPhraseDataProvider() + { + return array( + array( + "I'm a simple phrase", + '"I\'m a simple phrase"' + ), + + array( + "I have \"phrase\" characters", + '"I have \\"phrase\\" characters"' + ) + ); + } + + public function testGetCreateDocumentWithDefaultConstructor() + { + $fixture = $this->getFixture(); + + $this->assertTrue($fixture->getCreateDocuments()); + } + + public function testSetCreateDocuments() + { + $fixture = $this->getFixture(); + + $fixture->setCreateDocuments(false); + + $this->assertFalse($fixture->getCreateDocuments()); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/Apache/Solr/ServiceTest.php b/thirdparty/solr-php-client/tests/Apache/Solr/ServiceTest.php new file mode 100644 index 00000000..fd3f5680 --- /dev/null +++ b/thirdparty/solr-php-client/tests/Apache/Solr/ServiceTest.php @@ -0,0 +1,1180 @@ + + */ + +/** + * Apache_Solr_Service Unit Test + */ +class Apache_Solr_ServiceTest extends Apache_Solr_ServiceAbstractTest +{ + public function getFixture() + { + return new Apache_Solr_Service(); + } + + public function getMockHttpTransportInterface() + { + return $this->getMock( + 'Apache_Solr_HttpTransport_Interface', + array( + 'getDefaultTimeout', + 'setDefaultTimeout', + 'setAuthenticationCredentials', + 'performGetRequest', + 'performHeadRequest', + 'performPostRequest', + ) + ); + } + + //================================================================// + // ATTEMPT TO MOVE THESE TO ServiceAbstractTest AT SOME POINT // + // Apache_Solr_Service_Balancer will need functions added // + //================================================================// + public function testGetHttpTransportWithDefaultConstructor() + { + $fixture = new Apache_Solr_Service(); + + $httpTransport = $fixture->getHttpTransport(); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Interface', $httpTransport, 'Default http transport does not implement interface'); + $this->assertInstanceOf('Apache_Solr_HttpTransport_FileGetContents', $httpTransport, 'Default http transport is not URL Wrapper implementation'); + } + + + public function testSetHttpTransport() + { + $newTransport = new Apache_Solr_HttpTransport_Curl(); + $fixture = new Apache_Solr_Service(); + + $fixture->setHttpTransport($newTransport); + $httpTransport = $fixture->getHttpTransport(); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Interface', $httpTransport); + $this->assertInstanceOf('Apache_Solr_HttpTransport_Curl', $httpTransport); + $this->assertEquals($newTransport, $httpTransport); + + } + + public function testSetHttpTransportWithConstructor() + { + $newTransport = new Apache_Solr_HttpTransport_Curl(); + + $fixture = new Apache_Solr_Service('localhost', 8180, '/solr/', $newTransport); + + $fixture->setHttpTransport($newTransport); + $httpTransport = $fixture->getHttpTransport(); + + $this->assertInstanceOf('Apache_Solr_HttpTransport_Interface', $httpTransport); + $this->assertInstanceOf('Apache_Solr_HttpTransport_Curl', $httpTransport); + $this->assertEquals($newTransport, $httpTransport); + } + + public function testGetCollapseSingleValueArraysWithDefaultConstructor() + { + $fixture = $this->getFixture(); + + $this->assertTrue($fixture->getCollapseSingleValueArrays()); + } + + public function testSetCollapseSingleValueArrays() + { + $fixture = $this->getFixture(); + + $fixture->setCollapseSingleValueArrays(false); + $this->assertFalse($fixture->getCollapseSingleValueArrays()); + } + + public function testGetNamedListTreatmetnWithDefaultConstructor() + { + $fixture = $this->getFixture(); + + $this->assertEquals(Apache_Solr_Service::NAMED_LIST_MAP, $fixture->getNamedListTreatment()); + } + + public function testSetNamedListTreatment() + { + $fixture = $this->getFixture(); + + $fixture->setNamedListTreatment(Apache_Solr_Service::NAMED_LIST_FLAT); + $this->assertEquals(Apache_Solr_Service::NAMED_LIST_FLAT, $fixture->getNamedListTreatment()); + + $fixture->setNamedListTreatment(Apache_Solr_Service::NAMED_LIST_MAP); + $this->assertEquals(Apache_Solr_Service::NAMED_LIST_MAP, $fixture->getNamedListTreatment()); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testSetNamedListTreatmentInvalidArgumentException() + { + $fixture = $this->getFixture(); + + $fixture->setNamedListTreatment("broken"); + } + + //================================================================// + // END SECTION OF CODE THAT SHOULD BE MOVED // + // Apache_Solr_Service_Balancer will need functions added // + //================================================================// + + + public function testConstructorDefaultArguments() + { + $fixture = new Apache_Solr_Service(); + + $this->assertInstanceOf('Apache_Solr_Service', $fixture); + } + + public function testGetHostWithDefaultConstructor() + { + $fixture = new Apache_Solr_Service(); + $host = $fixture->getHost(); + + $this->assertEquals("localhost", $host); + } + + public function testSetHost() + { + $newHost = "example.com"; + + $fixture = new Apache_Solr_Service(); + $fixture->setHost($newHost); + $host = $fixture->getHost(); + + $this->assertEquals($newHost, $host); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testSetEmptyHost() + { + $fixture = new Apache_Solr_Service(); + + // should throw an invalid argument exception + $fixture->setHost(""); + } + + public function testSetHostWithConstructor() + { + $newHost = "example.com"; + + $fixture = new Apache_Solr_Service($newHost); + $host = $fixture->getHost(); + + $this->assertEquals($newHost, $host); + } + + public function testGetPortWithDefaultConstructor() + { + $fixture = new Apache_Solr_Service(); + $port = $fixture->getPort(); + + $this->assertEquals(8180, $port); + } + + public function testSetPort() + { + $newPort = 12345; + + $fixture = new Apache_Solr_Service(); + $fixture->setPort($newPort); + $port = $fixture->getPort(); + + $this->assertEquals($newPort, $port); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testSetPortWithInvalidArgument() + { + $fixture = new Apache_Solr_Service(); + + $fixture->setPort("broken"); + } + + public function testSetPortWithConstructor() + { + $newPort = 12345; + + $fixture = new Apache_Solr_Service('locahost', $newPort); + $port = $fixture->getPort(); + + $this->assertEquals($newPort, $port); + } + + public function testGetPathWithDefaultConstructor() + { + $fixture = new Apache_Solr_Service(); + $path = $fixture->getPath(); + + $this->assertEquals("/solr/", $path); + } + + public function testSetPath() + { + $newPath = "/new/path/"; + + $fixture = new Apache_Solr_Service(); + $fixture->setPath($newPath); + $path = $fixture->getPath(); + + $this->assertEquals($path, $newPath); + } + + public function testSetPathWillAddContainingSlashes() + { + $newPath = "new/path"; + $containedPath = "/{$newPath}/"; + + $fixture = new Apache_Solr_Service(); + $fixture->setPath($newPath); + $path = $fixture->getPath(); + + $this->assertEquals($containedPath, $path, 'setPath did not ensure propertly wrapped with slashes'); + } + + public function testSetPathWithConstructor() + { + $newPath = "/new/path/"; + + $fixture = new Apache_Solr_Service('localhost', 8180, $newPath); + $path = $fixture->getPath(); + + $this->assertEquals($newPath, $path); + } + + + public function testGetDefaultTimeoutCallsThroughToTransport() + { + $fixture = new Apache_Solr_Service(); + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call + $mockTransport->expects($this->once())->method('getDefaultTimeout'); + + $fixture->setHttpTransport($mockTransport); + + $fixture->getDefaultTimeout(); + } + + public function testSetDefaultTimeoutCallsThroughToTransport() + { + $timeout = 12345; + $fixture = new Apache_Solr_Service(); + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call + $mockTransport->expects($this->once())->method('setDefaultTimeout')->with($this->equalTo($timeout)); + + $fixture->setHttpTransport($mockTransport); + $fixture->setDefaultTimeout($timeout); + } + + public function testSetAuthenticationCredentialsCallsThroughToTransport() + { + $username = "user"; + $password = "password"; + + $fixture = new Apache_Solr_Service(); + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call + $mockTransport->expects($this->once())->method('setAuthenticationCredentials')->with($this->equalTo($username), $this->equalTo($password)); + + $fixture->setHttpTransport($mockTransport); + $fixture->setAuthenticationCredentials($username, $password); + } + + public function testPing() + { + $expectedUrl = "http://localhost:8180/solr/admin/ping"; + $expectedTimeout = 2; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performHeadRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + // call ping + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $time = $fixture->ping(); + + $this->assertGreaterThan(0, $time); + } + + public function testPingReturnsFalse() + { + $expectedUrl = "http://localhost:8180/solr/admin/ping"; + $expectedTimeout = 2; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performHeadRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get0Response())); + + // call ping + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $this->assertFalse($fixture->ping()); + } + + public function testSystem() + { + $expectedUrl = "http://localhost:8180/solr/admin/system?wt=json"; + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + // call system + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->system(); + } + + /** + * @expectedException Apache_Solr_HttpTransportException + */ + public function testSystem404() + { + $expectedUrl = "http://localhost:8180/solr/admin/system?wt=json"; + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get404Response())); + + // call system + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->system(); + } + + public function testThreads() + { + $expectedUrl = "http://localhost:8180/solr/admin/threads?wt=json"; + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + // call threads + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->threads(); + } + + /** + * @expectedException Apache_Solr_HttpTransportException + */ + public function testThreads404() + { + $expectedUrl = "http://localhost:8180/solr/admin/threads?wt=json"; + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get404Response())); + + // call threads + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->threads(); + } + + public function testAdd() + { + $postData = "does not have to be valid"; + + $expectedUrl = "http://localhost:8180/solr/update?wt=json"; + $expectedTimeout = false; + $expectedPostData = $postData; + $expectedContentType = "text/xml; charset=UTF-8"; // default for _sendRawPost + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($postData), $this->equalTo($expectedContentType), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + // call add + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->add($postData); + } + + /** + * @expectedException Apache_Solr_HttpTransportException + */ + public function testAdd400() + { + $postData = "does not have to be valid"; + + $expectedUrl = "http://localhost:8180/solr/update?wt=json"; + $expectedTimeout = false; + $expectedPostData = $postData; + $expectedContentType = "text/xml; charset=UTF-8"; // default for _sendRawPost + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($postData), $this->equalTo($expectedContentType), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get400Response())); + + // call add + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->add($postData); + } + + public function testAddDocument() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo(''), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $document = new Apache_Solr_Document(); + + $fixture->addDocument($document); + } + + public function testAddDocumentWithNonDefaultParameters() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo(''), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $document = new Apache_Solr_Document(); + + $fixture->addDocument($document, true, false, false, 3600); + } + + public function testAddDocumentWithFields() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo('global unique idvaluevalue 1value 2'), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $document = new Apache_Solr_Document(); + + $document->guid = "global unique id"; + $document->field = "value"; + $document->multivalue = array("value 1", "value 2"); + + $fixture->addDocument($document); + } + + public function testAddDocumentWithFieldBoost() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo('global unique idvaluevalue 1value 2'), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $document = new Apache_Solr_Document(); + + $document->guid = "global unique id"; + + $document->field = "value"; + $document->setFieldBoost('field', 2); + + $document->multivalue = array("value 1", "value 2"); + $document->setFieldBoost('multivalue', 3); + + $fixture->addDocument($document); + } + + public function testAddDocumentWithDocumentBoost() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo('global unique idvalue'), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $document = new Apache_Solr_Document(); + $document->setBoost(2); + + $document->guid = "global unique id"; + $document->field = "value"; + + $fixture->addDocument($document); + } + + public function testAddDocuments() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo(''), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $documents = array( + new Apache_Solr_Document(), + new Apache_Solr_Document() + ); + + $fixture->addDocuments($documents); + } + + public function testAddDocumentsWithNonDefaultParameters() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo(''), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(false) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $documents = array( + new Apache_Solr_Document(), + new Apache_Solr_Document() + ); + + $fixture->addDocuments($documents, true, false, false, 3600); + } + + public function testCommit() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo(''), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(3600) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->commit(); + } + + public function testCommitWithNonDefaultParameters() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + // url + $this->equalTo('http://localhost:8180/solr/update?wt=json'), + + // raw post + $this->equalTo(''), + + // content type + $this->equalTo('text/xml; charset=UTF-8'), + + // timeout + $this->equalTo(7200) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->commit(true, false, false, 7200); + } + + public function testDelete() + { + $postData = "does not have to be valid"; + + $expectedUrl = "http://localhost:8180/solr/update?wt=json"; + $expectedTimeout = 3600; // default for delete + $expectedPostData = $postData; + $expectedContentType = "text/xml; charset=UTF-8"; // default for _sendRawPost + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($postData), $this->equalTo($expectedContentType), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + // call add + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + $fixture->delete($postData); + } + + public function testDeleteById() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->deleteById("does not exist"); + } + + public function testDeleteByMultipleIds() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->deleteByMultipleIds(array(1, 2, 3)); + } + + public function testDeleteByQuery() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->deleteByQuery("*:*"); + } + + public function testExtracts() + { + $extractFile = __FILE__; + + $expectedUrl = "http://localhost:8180/solr/update/extract?resource.name=ServiceTest.php&wt=json&json.nl=map"; + $expectedPostData = file_get_contents($extractFile); + $expectedContentType = 'application/octet-stream'; // default for extract + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedPostData), $this->equalTo($expectedContentType), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extract($extractFile); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testExtractWithInvalidParams() + { + $extractFile = __FILE__; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extract($extractFile, "invalid"); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testExtractFromStringWithInvalidParams() + { + $extractFileData = "does not matter what it is"; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extractFromString($extractFileData, "invalid"); + } + + public function testExtractsWithNullParams() + { + $extractFile = __FILE__; + + $expectedUrl = "http://localhost:8180/solr/update/extract?resource.name=ServiceTest.php&wt=json&json.nl=map"; + $expectedPostData = file_get_contents($extractFile); + $expectedContentType = 'application/octet-stream'; // default for extract + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedPostData), $this->equalTo($expectedContentType), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extract($extractFile, null); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testExtractWithEmptyFile() + { + $extractFile = "iDontExist.txt"; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extract($extractFile); + } + + public function testExtractsWithDocument() + { + $extractFile = __FILE__; + + $expectedUrl = "http://localhost:8180/solr/update/extract?resource.name=ServiceTest.php&wt=json&json.nl=map&boost.field=2&literal.field=literal+value"; + $expectedPostData = file_get_contents($extractFile); + $expectedContentType = 'application/octet-stream'; // default for extract + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with($this->equalTo($expectedUrl), $this->equalTo($expectedPostData), $this->equalTo($expectedContentType), $this->equalTo($expectedTimeout)) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $literals = new Apache_Solr_Document(); + $literals->field = "literal value"; + $literals->setFieldBoost('field', 2); + + $fixture->extract($extractFile, null, $literals); + } + + public function testExtractWithUrlDefers() + { + $extractUrl = "http://example.com"; + + $expectedUrl = "http://localhost:8180/solr/update/extract?resource.name=http%3A%2F%2Fexample.com&wt=json&json.nl=map"; + $expectedPostData = Apache_Solr_HttpTransport_ResponseTest::BODY_200; + $expectedContentType = 'application/octet-stream'; // default for extract + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->with( + $this->equalTo($extractUrl) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + $this->equalTo($expectedUrl), + $this->equalTo($expectedPostData), + $this->equalTo($expectedContentType), + $this->equalTo($expectedTimeout) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extract($extractUrl); + } + + public function testExtractFromUrl() + { + $extractUrl = "http://example.com"; + + $expectedUrl = "http://localhost:8180/solr/update/extract?resource.name=http%3A%2F%2Fexample.com&wt=json&json.nl=map"; + $expectedPostData = Apache_Solr_HttpTransport_ResponseTest::BODY_200; + $expectedContentType = 'application/octet-stream'; // default for extract + $expectedTimeout = false; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->with( + $this->equalTo($extractUrl) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->with( + $this->equalTo($expectedUrl), + $this->equalTo($expectedPostData), + $this->equalTo($expectedContentType), + $this->equalTo($expectedTimeout) + ) + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extractFromUrl($extractUrl); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testExtractFromUrlWithInvalidParams() + { + $extractUrl = "http://example.com"; + + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->extractFromUrl($extractUrl, "invalid"); + } + + public function testOptimize() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_Service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->optimize(); + } + + public function testSearch() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->search("solr"); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testSearchWithInvalidParams() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->search("solr", 0, 10, "invalid"); + + $this->fail("Should have through InvalidArgumentException"); + } + + public function testSearchWithEmptyParams() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performGetRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->search("solr", 0, 10, null); + } + + public function testSearchWithPostMethod() + { + // set a mock transport + $mockTransport = $this->getMockHttpTransportInterface(); + + // setup expected call and response + $mockTransport->expects($this->once()) + ->method('performPostRequest') + ->will($this->returnValue(Apache_Solr_HttpTransport_ResponseTest::get200Response())); + + $fixture = new Apache_Solr_service(); + $fixture->setHttpTransport($mockTransport); + + $fixture->search("solr", 0, 10, array(), Apache_Solr_Service::METHOD_POST); + } + + /** + * @expectedException Apache_Solr_InvalidArgumentException + */ + public function testSearchWithInvalidMethod() + { + $fixture = new Apache_Solr_service(); + + $fixture->search("solr", 0, 10, array(), "INVALID METHOD"); + } +} \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/README b/thirdparty/solr-php-client/tests/README new file mode 100644 index 00000000..39ede2e9 --- /dev/null +++ b/thirdparty/solr-php-client/tests/README @@ -0,0 +1,20 @@ +Use the run.php script included in this directory to run all unit tests for the +Solr PHP Client library. Your system will require phpunit PEAR package - which +you can get install instructions for at: + +http://www.phpunit.de/ + +To generate the code coverage report, you will also need the XDebug pecl package +installed, typically this can be done with a simple: + +pecl install xdebug + +If you need more information on installation, then please see the official website: + +http://www.xdebug.org + +The scripts, configuration, and test files in this directory have been confirmed to +work with the following versions: + +phpunit: 3.3.16 +xdebug: 2.0.4 \ No newline at end of file diff --git a/thirdparty/solr-php-client/tests/phpunit.bootstrap.inc b/thirdparty/solr-php-client/tests/phpunit.bootstrap.inc new file mode 100644 index 00000000..237a0ec1 --- /dev/null +++ b/thirdparty/solr-php-client/tests/phpunit.bootstrap.inc @@ -0,0 +1,28 @@ + + + + + + + + + ../Apache + + ./run.php + + + + diff --git a/thirdparty/solr-php-client/tests/run.php b/thirdparty/solr-php-client/tests/run.php new file mode 100755 index 00000000..9dfbf6ae --- /dev/null +++ b/thirdparty/solr-php-client/tests/run.php @@ -0,0 +1,42 @@ +#! /usr/bin/php +