├── .gitignore ├── .travis.yml ├── Apache └── Solr │ ├── Compatibility │ ├── AddDocumentXmlCreator.php │ ├── CompatibilityLayer.php │ ├── Solr3CompatibilityLayer.php │ └── Solr4CompatibilityLayer.php │ ├── Document.php │ ├── Exception.php │ ├── HttpTransport │ ├── Abstract.php │ ├── Curl.php │ ├── CurlNoReuse.php │ ├── FileGetContents.php │ ├── Interface.php │ └── Response.php │ ├── HttpTransportException.php │ ├── InvalidArgumentException.php │ ├── NoServiceAvailableException.php │ ├── ParserException.php │ ├── Response.php │ ├── Service.php │ └── Service │ └── Balancer.php ├── COPYING ├── README.md ├── composer.json ├── composer.lock └── tests ├── Apache └── Solr │ ├── DocumentTest.php │ ├── HttpTransport │ ├── AbstractTest.php │ ├── CurlNoReuseTest.php │ ├── CurlTest.php │ ├── FileGetContentsTest.php │ └── ResponseTest.php │ ├── HttpTransportExceptionTest.php │ ├── ResponseTest.php │ ├── Service │ └── BalancerTest.php │ ├── ServiceAbstractTest.php │ └── ServiceTest.php ├── README ├── phpunit.bootstrap.inc └── phpunit.xml /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | vendor 3 | /.idea/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.2 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | - hhvm 9 | matrix: 10 | allow_failures: 11 | - php: hhvm 12 | script: phpunit --verbose -c tests/phpunit.xml tests 13 | notifications: 14 | email: false 15 | -------------------------------------------------------------------------------- /Apache/Solr/Compatibility/AddDocumentXmlCreator.php: -------------------------------------------------------------------------------- 1 | '; 22 | 23 | return $rawPost; 24 | } 25 | 26 | /** 27 | * Creates an optimize command XML string. 28 | * 29 | * @param boolean $waitFlush 30 | * @param boolean $waitSearcher 31 | * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception) 32 | * @return string An XML string 33 | */ 34 | public function createOptimizeXml($waitFlush = true, $waitSearcher = true) 35 | { 36 | $flushValue = $waitFlush ? 'true' : 'false'; 37 | $searcherValue = $waitSearcher ? 'true' : 'false'; 38 | 39 | $rawPost = ''; 40 | 41 | return $rawPost; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Apache/Solr/Compatibility/Solr4CompatibilityLayer.php: -------------------------------------------------------------------------------- 1 | '; 24 | 25 | return $rawPost; 26 | } 27 | 28 | /** 29 | * Creates an optimize command XML string. 30 | * 31 | * @param boolean $waitFlush Is ignored. 32 | * @param boolean $waitSearcher 33 | * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception) 34 | * @return string An XML string 35 | */ 36 | public function createOptimizeXml($waitFlush = true, $waitSearcher = true) 37 | { 38 | $searcherValue = $waitSearcher ? 'true' : 'false'; 39 | 40 | $rawPost = ''; 41 | 42 | return $rawPost; 43 | } 44 | 45 | /** 46 | * Creates an add command XML string 47 | * 48 | * @param string $rawDocuments String containing XML representation of documents. 49 | * @param boolean $allowDups 50 | * @param boolean $overwritePending 51 | * @param boolean $overwriteCommitted 52 | * @param integer $commitWithin The number of milliseconds that a document must be committed within, 53 | * see @{link http://wiki.apache.org/solr/UpdateXmlMessages#The_Update_Schema} for details. If left empty 54 | * this property will not be set in the request. 55 | * @return string An XML string 56 | */ 57 | public function createAddDocumentXmlFragment( 58 | $rawDocuments, 59 | $allowDups = false, 60 | $overwritePending = true, 61 | $overwriteCommitted = true, 62 | $commitWithin = 0 63 | ) { 64 | $dupValue = !$allowDups ? 'true' : 'false'; 65 | 66 | $commitWithin = (int) $commitWithin; 67 | $commitWithinString = $commitWithin > 0 ? " commitWithin=\"{$commitWithin}\"" : ''; 68 | 69 | $addXmlFragment = ""; 70 | $addXmlFragment .= $rawDocuments; 71 | $addXmlFragment .= ''; 72 | 73 | return $addXmlFragment; 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /Apache/Solr/Document.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | /** 40 | * Holds Key / Value pairs that represent a Solr Document along with any associated boost 41 | * values. Field values can be accessed by direct dereferencing such as: 42 | * 43 | * ... 44 | * $document->title = 'Something'; 45 | * echo $document->title; 46 | * ... 47 | * 48 | * 49 | * Additionally, the field values can be iterated with foreach 50 | * 51 | * 52 | * foreach ($document as $fieldName => $fieldValue) 53 | * { 54 | * ... 55 | * } 56 | * 57 | */ 58 | class Apache_Solr_Document implements IteratorAggregate 59 | { 60 | /** 61 | * SVN Revision meta data for this class 62 | */ 63 | const SVN_REVISION = '$Revision$'; 64 | 65 | /** 66 | * SVN ID meta data for this class 67 | */ 68 | const SVN_ID = '$Id$'; 69 | 70 | /** 71 | * Document boost value 72 | * 73 | * @var float 74 | */ 75 | protected $_documentBoost = false; 76 | 77 | /** 78 | * Document field values, indexed by name 79 | * 80 | * @var array 81 | */ 82 | protected $_fields = array(); 83 | 84 | /** 85 | * Document field boost values, indexed by name 86 | * 87 | * @var array array of floats 88 | */ 89 | protected $_fieldBoosts = array(); 90 | 91 | /** 92 | * Clear all boosts and fields from this document 93 | */ 94 | public function clear() 95 | { 96 | $this->_documentBoost = false; 97 | 98 | $this->_fields = array(); 99 | $this->_fieldBoosts = array(); 100 | } 101 | 102 | /** 103 | * Get current document boost 104 | * 105 | * @return mixed will be false for default, or else a float 106 | */ 107 | public function getBoost() 108 | { 109 | return $this->_documentBoost; 110 | } 111 | 112 | /** 113 | * Set document boost factor 114 | * 115 | * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false 116 | */ 117 | public function setBoost($boost) 118 | { 119 | $boost = (float) $boost; 120 | 121 | if ($boost > 0.0) 122 | { 123 | $this->_documentBoost = $boost; 124 | } 125 | else 126 | { 127 | $this->_documentBoost = false; 128 | } 129 | } 130 | 131 | /** 132 | * Add a value to a multi-valued field 133 | * 134 | * NOTE: the solr XML format allows you to specify boosts 135 | * PER value even though the underlying Lucene implementation 136 | * only allows a boost per field. To remedy this, the final 137 | * field boost value will be the product of all specified boosts 138 | * on field values - this is similar to SolrJ's functionality. 139 | * 140 | * 141 | * $doc = new Apache_Solr_Document(); 142 | * 143 | * $doc->addField('foo', 'bar', 2.0); 144 | * $doc->addField('foo', 'baz', 3.0); 145 | * 146 | * // resultant field boost will be 6! 147 | * echo $doc->getFieldBoost('foo'); 148 | * 149 | * 150 | * @param string $key 151 | * @param mixed $value 152 | * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false 153 | */ 154 | public function addField($key, $value, $boost = false) 155 | { 156 | if (!isset($this->_fields[$key])) 157 | { 158 | // create holding array if this is the first value 159 | $this->_fields[$key] = array(); 160 | } 161 | else if (!is_array($this->_fields[$key])) 162 | { 163 | // move existing value into array if it is not already an array 164 | $this->_fields[$key] = array($this->_fields[$key]); 165 | } 166 | 167 | if ($this->getFieldBoost($key) === false) 168 | { 169 | // boost not already set, set it now 170 | $this->setFieldBoost($key, $boost); 171 | } 172 | else if ((float) $boost > 0.0) 173 | { 174 | // multiply passed boost with current field boost - similar to SolrJ implementation 175 | $this->_fieldBoosts[$key] *= (float) $boost; 176 | } 177 | 178 | // add value to array 179 | $this->_fields[$key][] = $value; 180 | } 181 | 182 | /** 183 | * Handle the array manipulation for a multi-valued field 184 | * 185 | * @param string $key 186 | * @param string $value 187 | * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false 188 | * 189 | * @deprecated Use addField(...) instead 190 | */ 191 | public function setMultiValue($key, $value, $boost = false) 192 | { 193 | $this->addField($key, $value, $boost); 194 | } 195 | 196 | /** 197 | * Get field information 198 | * 199 | * @param string $key 200 | * @return mixed associative array of info if field exists, false otherwise 201 | */ 202 | public function getField($key) 203 | { 204 | if (isset($this->_fields[$key])) 205 | { 206 | return array( 207 | 'name' => $key, 208 | 'value' => $this->_fields[$key], 209 | 'boost' => $this->getFieldBoost($key) 210 | ); 211 | } 212 | 213 | return false; 214 | } 215 | 216 | /** 217 | * Set a field value. Multi-valued fields should be set as arrays 218 | * or instead use the addField(...) function which will automatically 219 | * make sure the field is an array. 220 | * 221 | * @param string $key 222 | * @param mixed $value 223 | * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false 224 | */ 225 | public function setField($key, $value, $boost = false) 226 | { 227 | $this->_fields[$key] = $value; 228 | $this->setFieldBoost($key, $boost); 229 | } 230 | 231 | /** 232 | * Get the currently set field boost for a document field 233 | * 234 | * @param string $key 235 | * @return float currently set field boost, false if one is not set 236 | */ 237 | public function getFieldBoost($key) 238 | { 239 | return isset($this->_fieldBoosts[$key]) ? $this->_fieldBoosts[$key] : false; 240 | } 241 | 242 | /** 243 | * Set the field boost for a document field 244 | * 245 | * @param string $key field name for the boost 246 | * @param mixed $boost Use false for default boost, else cast to float that should be > 0 or will be treated as false 247 | */ 248 | public function setFieldBoost($key, $boost) 249 | { 250 | $boost = (float) $boost; 251 | 252 | if ($boost > 0.0) 253 | { 254 | $this->_fieldBoosts[$key] = $boost; 255 | } 256 | else 257 | { 258 | $this->_fieldBoosts[$key] = false; 259 | } 260 | } 261 | 262 | /** 263 | * Return current field boosts, indexed by field name 264 | * 265 | * @return array 266 | */ 267 | public function getFieldBoosts() 268 | { 269 | return $this->_fieldBoosts; 270 | } 271 | 272 | /** 273 | * Get the names of all fields in this document 274 | * 275 | * @return array 276 | */ 277 | public function getFieldNames() 278 | { 279 | return array_keys($this->_fields); 280 | } 281 | 282 | /** 283 | * Get the values of all fields in this document 284 | * 285 | * @return array 286 | */ 287 | public function getFieldValues() 288 | { 289 | return array_values($this->_fields); 290 | } 291 | 292 | /** 293 | * IteratorAggregate implementation function. Allows usage: 294 | * 295 | * 296 | * foreach ($document as $key => $value) 297 | * { 298 | * ... 299 | * } 300 | * 301 | */ 302 | public function getIterator() 303 | { 304 | $arrayObject = new ArrayObject($this->_fields); 305 | 306 | return $arrayObject->getIterator(); 307 | } 308 | 309 | /** 310 | * Magic get for field values 311 | * 312 | * @param string $key 313 | * @return mixed 314 | */ 315 | public function __get($key) 316 | { 317 | if (isset($this->_fields[$key])) 318 | { 319 | return $this->_fields[$key]; 320 | } 321 | 322 | return null; 323 | } 324 | 325 | /** 326 | * Magic set for field values. Multi-valued fields should be set as arrays 327 | * or instead use the addField(...) function which will automatically 328 | * make sure the field is an array. 329 | * 330 | * @param string $key 331 | * @param mixed $value 332 | */ 333 | public function __set($key, $value) 334 | { 335 | $this->setField($key, $value); 336 | } 337 | 338 | /** 339 | * Magic isset for fields values. Do not call directly. Allows usage: 340 | * 341 | * 342 | * isset($document->some_field); 343 | * 344 | * 345 | * @param string $key 346 | * @return boolean 347 | */ 348 | public function __isset($key) 349 | { 350 | return isset($this->_fields[$key]); 351 | } 352 | 353 | /** 354 | * Magic unset for field values. Do not call directly. Allows usage: 355 | * 356 | * 357 | * unset($document->some_field); 358 | * 359 | * 360 | * @param string $key 361 | */ 362 | public function __unset($key) 363 | { 364 | unset($this->_fields[$key]); 365 | unset($this->_fieldBoosts[$key]); 366 | } 367 | } -------------------------------------------------------------------------------- /Apache/Solr/Exception.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_Exception extends Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /Apache/Solr/HttpTransport/Abstract.php: -------------------------------------------------------------------------------- 1 | , Donovan Jimenez 37 | */ 38 | 39 | /** 40 | * Convenience class that implements the transport implementation. Can be extended by 41 | * real implementations to do some of the common book keeping 42 | */ 43 | abstract class Apache_Solr_HttpTransport_Abstract implements Apache_Solr_HttpTransport_Interface 44 | { 45 | /** 46 | * Our default timeout value for requests that don't specify a timeout 47 | * 48 | * @var float 49 | */ 50 | private $_defaultTimeout = false; 51 | 52 | /** 53 | * Get the current default timeout setting (initially the default_socket_timeout ini setting) 54 | * in seconds 55 | * 56 | * @return float 57 | */ 58 | public function getDefaultTimeout() 59 | { 60 | // lazy load the default timeout from the ini settings 61 | if ($this->_defaultTimeout === false) 62 | { 63 | $this->_defaultTimeout = (int) ini_get('default_socket_timeout'); 64 | 65 | // double check we didn't get 0 for a timeout 66 | if ($this->_defaultTimeout <= 0) 67 | { 68 | $this->_defaultTimeout = 60; 69 | } 70 | } 71 | 72 | return $this->_defaultTimeout; 73 | } 74 | 75 | /** 76 | * Set the current default timeout for all HTTP requests 77 | * 78 | * @param float $timeout 79 | */ 80 | public function setDefaultTimeout($timeout) 81 | { 82 | $timeout = (float) $timeout; 83 | 84 | if ($timeout >= 0) 85 | { 86 | $this->_defaultTimeout = $timeout; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Apache/Solr/HttpTransport/Curl.php: -------------------------------------------------------------------------------- 1 | , Donovan Jimenez 37 | */ 38 | 39 | /** 40 | * A Curl based HTTP transport. Uses a single curl session for all requests. 41 | */ 42 | class Apache_Solr_HttpTransport_Curl extends Apache_Solr_HttpTransport_Abstract 43 | { 44 | /** 45 | * SVN Revision meta data for this class 46 | */ 47 | const SVN_REVISION = '$Revision:$'; 48 | 49 | /** 50 | * SVN ID meta data for this class 51 | */ 52 | const SVN_ID = '$Id:$'; 53 | 54 | /** 55 | * Curl Session Handle 56 | * 57 | * @var resource 58 | */ 59 | private $_curl; 60 | 61 | /** 62 | * Initializes a curl session 63 | */ 64 | public function __construct() 65 | { 66 | // initialize a CURL session 67 | $this->_curl = curl_init(); 68 | 69 | // set common options that will not be changed during the session 70 | curl_setopt_array($this->_curl, array( 71 | // return the response body from curl_exec 72 | CURLOPT_RETURNTRANSFER => true, 73 | 74 | // get the output as binary data 75 | CURLOPT_BINARYTRANSFER => true, 76 | 77 | // we do not need the headers in the output, we get everything we need from curl_getinfo 78 | CURLOPT_HEADER => false 79 | )); 80 | } 81 | 82 | /** 83 | * Closes a curl session 84 | */ 85 | function __destruct() 86 | { 87 | // close our curl session 88 | curl_close($this->_curl); 89 | } 90 | 91 | public function setAuthenticationCredentials($username, $password) 92 | { 93 | // add the options to our curl handle 94 | curl_setopt_array($this->_curl, array( 95 | CURLOPT_USERPWD => $username . ":" . $password, 96 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC 97 | )); 98 | } 99 | 100 | public function performGetRequest($url, $timeout = false) 101 | { 102 | // check the timeout value 103 | if ($timeout === false || $timeout <= 0.0) 104 | { 105 | // use the default timeout 106 | $timeout = $this->getDefaultTimeout(); 107 | } 108 | 109 | // set curl GET options 110 | curl_setopt_array($this->_curl, array( 111 | // make sure we're returning the body 112 | CURLOPT_NOBODY => false, 113 | 114 | // make sure we're GET 115 | CURLOPT_HTTPGET => true, 116 | 117 | // set the URL 118 | CURLOPT_URL => $url, 119 | 120 | // set the timeout 121 | CURLOPT_TIMEOUT => $timeout 122 | )); 123 | 124 | // make the request 125 | $responseBody = curl_exec($this->_curl); 126 | 127 | // get info from the transfer 128 | $statusCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE); 129 | $contentType = curl_getinfo($this->_curl, CURLINFO_CONTENT_TYPE); 130 | 131 | return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); 132 | } 133 | 134 | public function performHeadRequest($url, $timeout = false) 135 | { 136 | // check the timeout value 137 | if ($timeout === false || $timeout <= 0.0) 138 | { 139 | // use the default timeout 140 | $timeout = $this->getDefaultTimeout(); 141 | } 142 | 143 | // set curl HEAD options 144 | curl_setopt_array($this->_curl, array( 145 | // this both sets the method to HEAD and says not to return a body 146 | CURLOPT_NOBODY => true, 147 | 148 | // set the URL 149 | CURLOPT_URL => $url, 150 | 151 | // set the timeout 152 | CURLOPT_TIMEOUT => $timeout 153 | )); 154 | 155 | // make the request 156 | $responseBody = curl_exec($this->_curl); 157 | 158 | // get info from the transfer 159 | $statusCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE); 160 | $contentType = curl_getinfo($this->_curl, CURLINFO_CONTENT_TYPE); 161 | 162 | return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); 163 | } 164 | 165 | public function performPostRequest($url, $postData, $contentType, $timeout = false) 166 | { 167 | // check the timeout value 168 | if ($timeout === false || $timeout <= 0.0) 169 | { 170 | // use the default timeout 171 | $timeout = $this->getDefaultTimeout(); 172 | } 173 | 174 | // set curl POST options 175 | curl_setopt_array($this->_curl, array( 176 | // make sure we're returning the body 177 | CURLOPT_NOBODY => false, 178 | 179 | // make sure we're POST 180 | CURLOPT_POST => true, 181 | 182 | // set the URL 183 | CURLOPT_URL => $url, 184 | 185 | // set the post data 186 | CURLOPT_POSTFIELDS => $postData, 187 | 188 | // set the content type 189 | CURLOPT_HTTPHEADER => array("Content-Type: {$contentType}"), 190 | 191 | // set the timeout 192 | CURLOPT_TIMEOUT => $timeout 193 | )); 194 | 195 | // make the request 196 | $responseBody = curl_exec($this->_curl); 197 | 198 | // get info from the transfer 199 | $statusCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE); 200 | $contentType = curl_getinfo($this->_curl, CURLINFO_CONTENT_TYPE); 201 | 202 | return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); 203 | } 204 | } -------------------------------------------------------------------------------- /Apache/Solr/HttpTransport/CurlNoReuse.php: -------------------------------------------------------------------------------- 1 | , Donovan Jimenez 37 | */ 38 | 39 | /** 40 | * An alternative Curl HTTP transport that opens and closes a curl session for 41 | * every request. This isn't the recommended way to use curl, but some version of 42 | * PHP have memory issues. 43 | */ 44 | class Apache_Solr_HttpTransport_CurlNoReuse extends Apache_Solr_HttpTransport_Abstract 45 | { 46 | /** 47 | * SVN Revision meta data for this class 48 | */ 49 | const SVN_REVISION = '$Revision:$'; 50 | 51 | /** 52 | * SVN ID meta data for this class 53 | */ 54 | const SVN_ID = '$Id:$'; 55 | 56 | private $_authString = false; 57 | 58 | public function setAuthenticationCredentials($username, $password) 59 | { 60 | // this is how curl wants it for the CURLOPT_USERPWD 61 | $this->_authString = $username . ":" . $password; 62 | } 63 | 64 | public function performGetRequest($url, $timeout = false) 65 | { 66 | // check the timeout value 67 | if ($timeout === false || $timeout <= 0.0) 68 | { 69 | // use the default timeout 70 | $timeout = $this->getDefaultTimeout(); 71 | } 72 | 73 | $curl = curl_init(); 74 | 75 | // set curl GET options 76 | curl_setopt_array($curl, array( 77 | // return the response body from curl_exec 78 | CURLOPT_RETURNTRANSFER => true, 79 | 80 | // get the output as binary data 81 | CURLOPT_BINARYTRANSFER => true, 82 | 83 | // we do not need the headers in the output, we get everything we need from curl_getinfo 84 | CURLOPT_HEADER => false, 85 | 86 | // set the URL 87 | CURLOPT_URL => $url, 88 | 89 | // set the timeout 90 | CURLOPT_TIMEOUT => $timeout 91 | )); 92 | 93 | // set auth if appropriate 94 | if ($this->_authString !== false) 95 | { 96 | curl_setopt_array($curl, array( 97 | CURLOPT_USERPWD => $this->_authString, 98 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC 99 | )); 100 | } 101 | 102 | // make the request 103 | $responseBody = curl_exec($curl); 104 | 105 | // get info from the transfer 106 | $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); 107 | $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); 108 | 109 | // close our curl session - we're done with it 110 | curl_close($curl); 111 | 112 | return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); 113 | } 114 | 115 | public function performHeadRequest($url, $timeout = false) 116 | { 117 | // check the timeout value 118 | if ($timeout === false || $timeout <= 0.0) 119 | { 120 | // use the default timeout 121 | $timeout = $this->getDefaultTimeout(); 122 | } 123 | 124 | $curl = curl_init(); 125 | 126 | // set curl HEAD options 127 | curl_setopt_array($curl, array( 128 | // return the response body from curl_exec 129 | CURLOPT_RETURNTRANSFER => true, 130 | 131 | // get the output as binary data 132 | CURLOPT_BINARYTRANSFER => true, 133 | 134 | // we do not need the headers in the output, we get everything we need from curl_getinfo 135 | CURLOPT_HEADER => false, 136 | 137 | // this both sets the method to HEAD and says not to return a body 138 | CURLOPT_NOBODY => true, 139 | 140 | // set the URL 141 | CURLOPT_URL => $url, 142 | 143 | // set the timeout 144 | CURLOPT_TIMEOUT => $timeout 145 | )); 146 | 147 | // set auth if appropriate 148 | if ($this->_authString !== false) 149 | { 150 | curl_setopt_array($curl, array( 151 | CURLOPT_USERPWD => $this->_authString, 152 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC 153 | )); 154 | } 155 | 156 | // make the request 157 | $responseBody = curl_exec($curl); 158 | 159 | // get info from the transfer 160 | $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); 161 | $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); 162 | 163 | // close our curl session - we're done with it 164 | curl_close($curl); 165 | 166 | return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); 167 | } 168 | 169 | public function performPostRequest($url, $postData, $contentType, $timeout = false) 170 | { 171 | // check the timeout value 172 | if ($timeout === false || $timeout <= 0.0) 173 | { 174 | // use the default timeout 175 | $timeout = $this->getDefaultTimeout(); 176 | } 177 | 178 | $curl = curl_init(); 179 | 180 | // set curl POST options 181 | curl_setopt_array($curl, array( 182 | // return the response body from curl_exec 183 | CURLOPT_RETURNTRANSFER => true, 184 | 185 | // get the output as binary data 186 | CURLOPT_BINARYTRANSFER => true, 187 | 188 | // we do not need the headers in the output, we get everything we need from curl_getinfo 189 | CURLOPT_HEADER => false, 190 | 191 | // make sure we're POST 192 | CURLOPT_POST => true, 193 | 194 | // set the URL 195 | CURLOPT_URL => $url, 196 | 197 | // set the post data 198 | CURLOPT_POSTFIELDS => $postData, 199 | 200 | // set the content type 201 | CURLOPT_HTTPHEADER => array("Content-Type: {$contentType}"), 202 | 203 | // set the timeout 204 | CURLOPT_TIMEOUT => $timeout 205 | )); 206 | 207 | // set auth if appropriate 208 | if ($this->_authString !== false) 209 | { 210 | curl_setopt_array($curl, array( 211 | CURLOPT_USERPWD => $this->_authString, 212 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC 213 | )); 214 | } 215 | 216 | // make the request 217 | $responseBody = curl_exec($curl); 218 | 219 | // get info from the transfer 220 | $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); 221 | $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); 222 | 223 | // close our curl session - we're done with it 224 | curl_close($curl); 225 | 226 | return new Apache_Solr_HttpTransport_Response($statusCode, $contentType, $responseBody); 227 | } 228 | } -------------------------------------------------------------------------------- /Apache/Solr/HttpTransport/FileGetContents.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | /** 40 | * HTTP Transport implemenation that uses the builtin http URL wrappers and file_get_contents 41 | */ 42 | class Apache_Solr_HttpTransport_FileGetContents extends Apache_Solr_HttpTransport_Abstract 43 | { 44 | /** 45 | * SVN Revision meta data for this class 46 | */ 47 | const SVN_REVISION = '$Revision: $'; 48 | 49 | /** 50 | * SVN ID meta data for this class 51 | */ 52 | const SVN_ID = '$Id: $'; 53 | 54 | /** 55 | * Reusable stream context resources for GET and POST operations 56 | * 57 | * @var resource 58 | */ 59 | private $_getContext, $_headContext, $_postContext; 60 | 61 | /** 62 | * For POST operations, we're already using the Header context value for 63 | * specifying the content type too, so we have to keep our computed 64 | * authorization header around 65 | * 66 | * @var string 67 | */ 68 | private $_authHeader = ""; 69 | 70 | /** 71 | * Initializes our reuseable get and post stream contexts 72 | */ 73 | public function __construct() 74 | { 75 | $this->_getContext = stream_context_create(); 76 | $this->_headContext = stream_context_create(); 77 | $this->_postContext = stream_context_create(); 78 | } 79 | 80 | public function setAuthenticationCredentials($username, $password) 81 | { 82 | // compute the Authorization header 83 | $this->_authHeader = "Authorization: Basic " . base64_encode($username . ":" . $password); 84 | 85 | // set it now for get and head contexts 86 | stream_context_set_option($this->_getContext, 'http', 'header', $this->_authHeader); 87 | stream_context_set_option($this->_headContext, 'http', 'header', $this->_authHeader); 88 | 89 | // for post, it'll be set each time, so add an \r\n so it can be concatenated 90 | // with the Content-Type 91 | $this->_authHeader .= "\r\n"; 92 | } 93 | 94 | public function performGetRequest($url, $timeout = false) 95 | { 96 | // set the timeout if specified 97 | if ($timeout !== FALSE && $timeout > 0.0) 98 | { 99 | // timeouts with file_get_contents seem to need 100 | // to be halved to work as expected 101 | $timeout = (float) $timeout / 2; 102 | 103 | stream_context_set_option($this->_getContext, 'http', 'timeout', $timeout); 104 | } 105 | else 106 | { 107 | // use the default timeout pulled from default_socket_timeout otherwise 108 | stream_context_set_option($this->_getContext, 'http', 'timeout', $this->getDefaultTimeout()); 109 | } 110 | 111 | // $http_response_headers will be updated by the call to file_get_contents later 112 | // see http://us.php.net/manual/en/wrappers.http.php for documentation 113 | // Unfortunately, it will still create a notice in analyzers if we don't set it here 114 | $http_response_header = null; 115 | $responseBody = @file_get_contents($url, false, $this->_getContext); 116 | 117 | return $this->_getResponseFromParts($responseBody, $http_response_header); 118 | } 119 | 120 | public function performHeadRequest($url, $timeout = false) 121 | { 122 | stream_context_set_option($this->_headContext, array( 123 | 'http' => array( 124 | // set HTTP method 125 | 'method' => 'HEAD', 126 | 127 | // default timeout 128 | 'timeout' => $this->getDefaultTimeout() 129 | ) 130 | ) 131 | ); 132 | 133 | // set the timeout if specified 134 | if ($timeout !== FALSE && $timeout > 0.0) 135 | { 136 | // timeouts with file_get_contents seem to need 137 | // to be halved to work as expected 138 | $timeout = (float) $timeout / 2; 139 | 140 | stream_context_set_option($this->_headContext, 'http', 'timeout', $timeout); 141 | } 142 | 143 | // $http_response_headers will be updated by the call to file_get_contents later 144 | // see http://us.php.net/manual/en/wrappers.http.php for documentation 145 | // Unfortunately, it will still create a notice in analyzers if we don't set it here 146 | $http_response_header = null; 147 | $responseBody = @file_get_contents($url, false, $this->_headContext); 148 | 149 | return $this->_getResponseFromParts($responseBody, $http_response_header); 150 | } 151 | 152 | public function performPostRequest($url, $rawPost, $contentType, $timeout = false) 153 | { 154 | stream_context_set_option($this->_postContext, array( 155 | 'http' => array( 156 | // set HTTP method 157 | 'method' => 'POST', 158 | 159 | // Add our posted content type (and auth header - see setAuthentication) 160 | 'header' => "{$this->_authHeader}Content-Type: {$contentType}", 161 | 162 | // the posted content 163 | 'content' => $rawPost, 164 | 165 | // default timeout 166 | 'timeout' => $this->getDefaultTimeout() 167 | ) 168 | ) 169 | ); 170 | 171 | // set the timeout if specified 172 | if ($timeout !== FALSE && $timeout > 0.0) 173 | { 174 | // timeouts with file_get_contents seem to need 175 | // to be halved to work as expected 176 | $timeout = (float) $timeout / 2; 177 | 178 | stream_context_set_option($this->_postContext, 'http', 'timeout', $timeout); 179 | } 180 | 181 | // $http_response_header will be updated by the call to file_get_contents later 182 | // see http://us.php.net/manual/en/wrappers.http.php for documentation 183 | // Unfortunately, it will still create a notice in analyzers if we don't set it here 184 | $http_response_header = null; 185 | $responseBody = @file_get_contents($url, false, $this->_postContext); 186 | 187 | // reset content of post context to reclaim memory 188 | stream_context_set_option($this->_postContext, 'http', 'content', ''); 189 | 190 | return $this->_getResponseFromParts($responseBody, $http_response_header); 191 | } 192 | 193 | private function _getResponseFromParts($rawResponse, $httpHeaders) 194 | { 195 | //Assume 0, false as defaults 196 | $status = 0; 197 | $contentType = false; 198 | 199 | //iterate through headers for real status, type, and encoding 200 | if (is_array($httpHeaders) && count($httpHeaders) > 0) 201 | { 202 | //look at the first headers for the HTTP status code 203 | //and message (errors are usually returned this way) 204 | // 205 | //HTTP 100 Continue response can also be returned before 206 | //the REAL status header, so we need look until we find 207 | //the last header starting with HTTP 208 | // 209 | //the spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1 210 | // 211 | //Thanks to Daniel Andersson for pointing out this oversight 212 | while (isset($httpHeaders[0]) && substr($httpHeaders[0], 0, 4) == 'HTTP') 213 | { 214 | // we can do a intval on status line without the "HTTP/1.X " to get the code 215 | $status = intval(substr($httpHeaders[0], 9)); 216 | 217 | // remove this from the headers so we can check for more 218 | array_shift($httpHeaders); 219 | } 220 | 221 | //Look for the Content-Type response header and determine type 222 | //and encoding from it (if possible - such as 'Content-Type: text/plain; charset=UTF-8') 223 | foreach ($httpHeaders as $header) 224 | { 225 | // look for the header that starts appropriately 226 | if (strncasecmp($header, 'Content-Type:', 13) == 0) 227 | { 228 | $contentType = substr($header, 13); 229 | break; 230 | } 231 | } 232 | } 233 | 234 | return new Apache_Solr_HttpTransport_Response($status, $contentType, $rawResponse); 235 | } 236 | } -------------------------------------------------------------------------------- /Apache/Solr/HttpTransport/Interface.php: -------------------------------------------------------------------------------- 1 | , Donovan Jimenez 37 | */ 38 | 39 | /** 40 | * Interface that all Transport (HTTP Requester) implementations must implement. These 41 | * Implementations can then be plugged into the Service instance in order to user their 42 | * the desired method for making HTTP requests 43 | */ 44 | interface Apache_Solr_HttpTransport_Interface 45 | { 46 | /** 47 | * Get the current default timeout for all HTTP requests 48 | * 49 | * @return float 50 | */ 51 | public function getDefaultTimeout(); 52 | 53 | /** 54 | * Set the current default timeout for all HTTP requests 55 | * 56 | * @param float $timeout 57 | */ 58 | public function setDefaultTimeout($timeout); 59 | 60 | /** 61 | * Set authentication credentials to pass along with the requests. 62 | * 63 | * These will be used to perform HTTP Basic authentication. 64 | * 65 | * @param string $username 66 | * @param string $password 67 | */ 68 | public function setAuthenticationCredentials($username, $password); 69 | 70 | /** 71 | * Perform a GET HTTP operation with an optional timeout and return the response 72 | * contents, use getLastResponseHeaders to retrieve HTTP headers 73 | * 74 | * @param string $url 75 | * @param float $timeout 76 | * @return Apache_Solr_HttpTransport_Response HTTP response 77 | */ 78 | public function performGetRequest($url, $timeout = false); 79 | 80 | /** 81 | * Perform a HEAD HTTP operation with an optional timeout and return the response 82 | * headers - NOTE: head requests have no response body 83 | * 84 | * @param string $url 85 | * @param float $timeout 86 | * @return Apache_Solr_HttpTransport_Response HTTP response 87 | */ 88 | public function performHeadRequest($url, $timeout = false); 89 | 90 | /** 91 | * Perform a POST HTTP operation with an optional timeout and return the response 92 | * contents, use getLastResponseHeaders to retrieve HTTP headers 93 | * 94 | * @param string $url 95 | * @param string $rawPost 96 | * @param string $contentType 97 | * @param float $timeout 98 | * @return Apache_Solr_HttpTransport_Response HTTP response 99 | */ 100 | public function performPostRequest($url, $rawPost, $contentType, $timeout = false); 101 | } -------------------------------------------------------------------------------- /Apache/Solr/HttpTransport/Response.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | /** 40 | * Represents the required pieces of an HTTP response provided by HTTP transport 41 | * implementations and then consumed by the Apache_Solr_Response class which provides 42 | * decoding 43 | */ 44 | class Apache_Solr_HttpTransport_Response 45 | { 46 | /** 47 | * Status Messages indexed by Status Code 48 | * Obtained from: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 49 | * 50 | * @var array 51 | */ 52 | static private $_defaultStatusMessages = array( 53 | // Specific to PHP Solr Client 54 | 0 => "Communication Error", 55 | 56 | // Informational 1XX 57 | 100 => "Continue", 58 | 101 => "Switching Protocols", 59 | 60 | // Successful 2XX 61 | 200 => "OK", 62 | 201 => "Created", 63 | 202 => "Accepted", 64 | 203 => "Non-Authoritative Information", 65 | 204 => "No Content", 66 | 205 => "Reset Content", 67 | 206 => "Partial Content", 68 | 69 | // Redirection 3XX 70 | 300 => "Multiple Choices", 71 | 301 => "Moved Permanently", 72 | 302 => "Found", 73 | 303 => "See Other", 74 | 304 => "Not Modified", 75 | 305 => "Use Proxy", 76 | 307 => "Temporary Redirect", 77 | 78 | // Client Error 4XX 79 | 400 => "Bad Request", 80 | 401 => "Unauthorized", 81 | 402 => "Payment Required", 82 | 403 => "Forbidden", 83 | 404 => "Not Found", 84 | 405 => "Method Not Allowed", 85 | 406 => "Not Acceptable", 86 | 407 => "Proxy Authentication Required", 87 | 408 => "Request Timeout", 88 | 409 => "Conflict", 89 | 410 => "Gone", 90 | 411 => "Length Required", 91 | 412 => "Precondition Failed", 92 | 413 => "Request Entity Too Large", 93 | 414 => "Request-URI Too Long", 94 | 415 => "Unsupported Media Type", 95 | 416 => "Request Range Not Satisfiable", 96 | 417 => "Expectation Failed", 97 | 98 | // Server Error 5XX 99 | 500 => "Internal Server Error", 100 | 501 => "Not Implemented", 101 | 502 => "Bad Gateway", 102 | 503 => "Service Unavailable", 103 | 504 => "Gateway Timeout", 104 | 505 => "HTTP Version Not Supported" 105 | ); 106 | 107 | /** 108 | * Get the HTTP status message based on status code 109 | * 110 | * @return string 111 | */ 112 | public static function getDefaultStatusMessage($statusCode) 113 | { 114 | $statusCode = (int) $statusCode; 115 | 116 | if (isset(self::$_defaultStatusMessages[$statusCode])) 117 | { 118 | return self::$_defaultStatusMessages[$statusCode]; 119 | } 120 | 121 | return "Unknown Status"; 122 | } 123 | 124 | /** 125 | * The response's HTTP status code 126 | * 127 | * @var integer 128 | */ 129 | private $_statusCode; 130 | 131 | /** 132 | * The response's HTTP status message 133 | * 134 | * @var string 135 | */ 136 | private $_statusMessage; 137 | 138 | /** 139 | * The response's mime type 140 | * 141 | * @var string 142 | */ 143 | private $_mimeType; 144 | 145 | /** 146 | * The response's character encoding 147 | * 148 | * @var string 149 | */ 150 | private $_encoding; 151 | 152 | /** 153 | * The response's data 154 | * 155 | * @var string 156 | */ 157 | private $_responseBody; 158 | 159 | /** 160 | * Construct a HTTP transport response 161 | * 162 | * @param integer $statusCode The HTTP status code 163 | * @param string $contentType The VALUE of the Content-Type HTTP header 164 | * @param string $responseBody The body of the HTTP response 165 | */ 166 | public function __construct($statusCode, $contentType, $responseBody) 167 | { 168 | // set the status code, make sure its an integer 169 | $this->_statusCode = (int) $statusCode; 170 | 171 | // lookup up status message based on code 172 | $this->_statusMessage = self::getDefaultStatusMessage($this->_statusCode); 173 | 174 | // set the response body, it should always be a string 175 | $this->_responseBody = (string) $responseBody; 176 | 177 | // parse the content type header value for mimetype and encoding 178 | // first set default values that will remain if we can't find 179 | // what we're looking for in the content type 180 | $this->_mimeType = "text/plain"; 181 | $this->_encoding = "UTF-8"; 182 | 183 | if ($contentType) 184 | { 185 | // now break apart the header to see if there's character encoding 186 | $contentTypeParts = explode(';', $contentType, 2); 187 | 188 | if (isset($contentTypeParts[0])) 189 | { 190 | $this->_mimeType = trim($contentTypeParts[0]); 191 | } 192 | 193 | if (isset($contentTypeParts[1])) 194 | { 195 | // we have a second part, split it further 196 | $contentTypeParts = explode('=', $contentTypeParts[1]); 197 | 198 | if (isset($contentTypeParts[1])) 199 | { 200 | $this->_encoding = trim($contentTypeParts[1]); 201 | } 202 | } 203 | } 204 | } 205 | 206 | /** 207 | * Get the status code of the response 208 | * 209 | * @return integer 210 | */ 211 | public function getStatusCode() 212 | { 213 | return $this->_statusCode; 214 | } 215 | 216 | /** 217 | * Get the status message of the response 218 | * 219 | * @return string 220 | */ 221 | public function getStatusMessage() 222 | { 223 | return $this->_statusMessage; 224 | } 225 | 226 | /** 227 | * Get the mimetype of the response body 228 | * 229 | * @return string 230 | */ 231 | public function getMimeType() 232 | { 233 | return $this->_mimeType; 234 | } 235 | 236 | /** 237 | * Get the charset encoding of the response body. 238 | * 239 | * @return string 240 | */ 241 | public function getEncoding() 242 | { 243 | return $this->_encoding; 244 | } 245 | 246 | /** 247 | * Get the raw response body 248 | * 249 | * @return string 250 | */ 251 | public function getBody() 252 | { 253 | return $this->_responseBody; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /Apache/Solr/HttpTransportException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_HttpTransportException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | 51 | /** 52 | * Response for which exception was generated 53 | * 54 | * @var Apache_Solr_Response 55 | */ 56 | private $_response; 57 | 58 | /** 59 | * HttpTransportException Constructor 60 | * 61 | * @param Apache_Solr_Response $response 62 | */ 63 | public function __construct(Apache_Solr_Response $response) 64 | { 65 | parent::__construct("'{$response->getHttpStatus()}' Status: {$response->getHttpStatusMessage()}", $response->getHttpStatus()); 66 | 67 | $this->_response = $response; 68 | } 69 | 70 | /** 71 | * Get the response for which this exception was generated 72 | * 73 | * @return Apache_Solr_Response 74 | */ 75 | public function getResponse() 76 | { 77 | return $this->_response; 78 | } 79 | } -------------------------------------------------------------------------------- /Apache/Solr/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_InvalidArgumentException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /Apache/Solr/NoServiceAvailableException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_NoServiceAvailableException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /Apache/Solr/ParserException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_ParserException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /Apache/Solr/Response.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | /** 40 | * Represents a Solr response. Parses the raw response into a set of stdClass objects 41 | * and associative arrays for easy access. 42 | * 43 | * Currently requires json_decode which is bundled with PHP >= 5.2.0, Alternatively can be 44 | * installed with PECL. Zend Framework also includes a purely PHP solution. 45 | */ 46 | class Apache_Solr_Response 47 | { 48 | /** 49 | * SVN Revision meta data for this class 50 | */ 51 | const SVN_REVISION = '$Revision$'; 52 | 53 | /** 54 | * SVN ID meta data for this class 55 | */ 56 | const SVN_ID = '$Id$'; 57 | 58 | /** 59 | * Holds the raw response used in construction 60 | * 61 | * @var Apache_Solr_HttpTransport_Response HTTP response 62 | */ 63 | protected $_response; 64 | 65 | /** 66 | * Whether the raw response has been parsed 67 | * 68 | * @var boolean 69 | */ 70 | protected $_isParsed = false; 71 | 72 | /** 73 | * Parsed representation of the data 74 | * 75 | * @var mixed 76 | */ 77 | protected $_parsedData; 78 | 79 | /** 80 | * Data parsing flags. Determines what extra processing should be done 81 | * after the data is initially converted to a data structure. 82 | * 83 | * @var boolean 84 | */ 85 | protected $_createDocuments = true, 86 | $_collapseSingleValueArrays = true; 87 | 88 | /** 89 | * Constructor. Takes the raw HTTP response body and the exploded HTTP headers 90 | * 91 | * @return Apache_Solr_HttpTransport_Response HTTP response 92 | * @param boolean $createDocuments Whether to convert the documents json_decoded as stdClass instances to Apache_Solr_Document instances 93 | * @param boolean $collapseSingleValueArrays Whether to make multivalued fields appear as single values 94 | */ 95 | public function __construct(Apache_Solr_HttpTransport_Response $response, $createDocuments = true, $collapseSingleValueArrays = true) 96 | { 97 | $this->_response = $response; 98 | $this->_createDocuments = (bool) $createDocuments; 99 | $this->_collapseSingleValueArrays = (bool) $collapseSingleValueArrays; 100 | } 101 | 102 | /** 103 | * Get the HTTP status code 104 | * 105 | * @return integer 106 | */ 107 | public function getHttpStatus() 108 | { 109 | return $this->_response->getStatusCode(); 110 | } 111 | 112 | /** 113 | * Get the HTTP status message of the response 114 | * 115 | * @return string 116 | */ 117 | public function getHttpStatusMessage() 118 | { 119 | return $this->_response->getStatusMessage(); 120 | } 121 | 122 | /** 123 | * Get content type of this Solr response 124 | * 125 | * @return string 126 | */ 127 | public function getType() 128 | { 129 | return $this->_response->getMimeType(); 130 | } 131 | 132 | /** 133 | * Get character encoding of this response. Should usually be utf-8, but just in case 134 | * 135 | * @return string 136 | */ 137 | public function getEncoding() 138 | { 139 | return $this->_response->getEncoding(); 140 | } 141 | 142 | /** 143 | * Get the raw response as it was given to this object 144 | * 145 | * @return string 146 | */ 147 | public function getRawResponse() 148 | { 149 | return $this->_response->getBody(); 150 | } 151 | 152 | /** 153 | * Magic get to expose the parsed data and to lazily load it 154 | * 155 | * @param string $key 156 | * @return mixed 157 | */ 158 | public function __get($key) 159 | { 160 | if (!$this->_isParsed) 161 | { 162 | $this->_parseData(); 163 | $this->_isParsed = true; 164 | } 165 | 166 | if (isset($this->_parsedData->$key)) 167 | { 168 | return $this->_parsedData->$key; 169 | } 170 | 171 | return null; 172 | } 173 | 174 | /** 175 | * Magic function for isset function on parsed data 176 | * 177 | * @param string $key 178 | * @return boolean 179 | */ 180 | public function __isset($key) 181 | { 182 | if (!$this->_isParsed) 183 | { 184 | $this->_parseData(); 185 | $this->_isParsed = true; 186 | } 187 | 188 | return isset($this->_parsedData->$key); 189 | } 190 | 191 | /** 192 | * Parse the raw response into the parsed_data array for access 193 | * 194 | * @throws Apache_Solr_ParserException If the data could not be parsed 195 | */ 196 | protected function _parseData() 197 | { 198 | //An alternative would be to use Zend_Json::decode(...) 199 | $data = json_decode($this->_response->getBody()); 200 | 201 | // check that we receive a valid JSON response - we should never receive a null 202 | if ($data === null) 203 | { 204 | throw new Apache_Solr_ParserException('Solr response does not appear to be valid JSON, please examine the raw response with getRawResponse() method'); 205 | } 206 | 207 | //if we're configured to collapse single valued arrays or to convert them to Apache_Solr_Document objects 208 | //and we have response documents, then try to collapse the values and / or convert them now 209 | if (($this->_createDocuments || $this->_collapseSingleValueArrays) && isset($data->response) && is_array($data->response->docs)) 210 | { 211 | $documents = array(); 212 | 213 | foreach ($data->response->docs as $originalDocument) 214 | { 215 | if ($this->_createDocuments) 216 | { 217 | $document = new Apache_Solr_Document(); 218 | } 219 | else 220 | { 221 | $document = $originalDocument; 222 | } 223 | 224 | foreach ($originalDocument as $key => $value) 225 | { 226 | //If a result is an array with only a single 227 | //value then its nice to be able to access 228 | //it as if it were always a single value 229 | if ($this->_collapseSingleValueArrays && is_array($value) && count($value) <= 1) 230 | { 231 | $value = array_shift($value); 232 | } 233 | 234 | $document->$key = $value; 235 | } 236 | 237 | $documents[] = $document; 238 | } 239 | 240 | $data->response->docs = $documents; 241 | } 242 | 243 | $this->_parsedData = $data; 244 | } 245 | } -------------------------------------------------------------------------------- /Apache/Solr/Service/Balancer.php: -------------------------------------------------------------------------------- 1 | , Dan Wolfe 37 | */ 38 | 39 | /** 40 | * Reference Implementation for using multiple Solr services in a distribution. Functionality 41 | * includes: 42 | * routing of read / write operations 43 | * failover (on selection) for multiple read servers 44 | */ 45 | class Apache_Solr_Service_Balancer 46 | { 47 | /** 48 | * SVN Revision meta data for this class 49 | */ 50 | const SVN_REVISION = '$Revision$'; 51 | 52 | /** 53 | * SVN ID meta data for this class 54 | */ 55 | const SVN_ID = '$Id$'; 56 | 57 | protected $_createDocuments = true; 58 | 59 | protected $_readableServices = array(); 60 | protected $_writeableServices = array(); 61 | 62 | protected $_currentReadService = null; 63 | protected $_currentWriteService = null; 64 | 65 | protected $_readPingTimeout = 2; 66 | protected $_writePingTimeout = 4; 67 | 68 | // Configuration for server selection backoff intervals 69 | protected $_useBackoff = false; // Set to true to use more resillient write server selection 70 | protected $_backoffLimit = 600; // 10 minute default maximum 71 | protected $_backoffEscalation = 2.0; // Rate at which to increase backoff period 72 | protected $_defaultBackoff = 2.0; // Default backoff interval 73 | 74 | /** 75 | * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. 76 | * 77 | * NOTE: inside a phrase fewer characters need escaped, use {@link Apache_Solr_Service::escapePhrase()} instead 78 | * 79 | * @param string $value 80 | * @return string 81 | */ 82 | static public function escape($value) 83 | { 84 | return Apache_Solr_Service::escape($value); 85 | } 86 | 87 | /** 88 | * Escape a value meant to be contained in a phrase for special query characters 89 | * 90 | * @param string $value 91 | * @return string 92 | */ 93 | static public function escapePhrase($value) 94 | { 95 | return Apache_Solr_Service::escapePhrase($value); 96 | } 97 | 98 | /** 99 | * Convenience function for creating phrase syntax from a value 100 | * 101 | * @param string $value 102 | * @return string 103 | */ 104 | static public function phrase($value) 105 | { 106 | return Apache_Solr_Service::phrase($value); 107 | } 108 | 109 | /** 110 | * Constructor. Takes arrays of read and write service instances or descriptions 111 | * 112 | * @param array $readableServices 113 | * @param array $writeableServices 114 | */ 115 | public function __construct($readableServices = array(), $writeableServices = array()) 116 | { 117 | //setup readable services 118 | foreach ($readableServices as $service) 119 | { 120 | $this->addReadService($service); 121 | } 122 | 123 | //setup writeable services 124 | foreach ($writeableServices as $service) 125 | { 126 | $this->addWriteService($service); 127 | } 128 | } 129 | 130 | public function setReadPingTimeout($timeout) 131 | { 132 | $this->_readPingTimeout = $timeout; 133 | } 134 | 135 | public function setWritePingTimeout($timeout) 136 | { 137 | $this->_writePingTimeout = $timeout; 138 | } 139 | 140 | public function setUseBackoff($enable) 141 | { 142 | $this->_useBackoff = $enable; 143 | } 144 | 145 | /** 146 | * Generates a service ID 147 | * 148 | * @param string $host 149 | * @param integer $port 150 | * @param string $path 151 | * @return string 152 | */ 153 | protected function _getServiceId($host, $port, $path) 154 | { 155 | return $host . ':' . $port . $path; 156 | } 157 | 158 | /** 159 | * Adds a service instance or service descriptor (if it is already 160 | * not added) 161 | * 162 | * @param mixed $service 163 | * 164 | * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid 165 | */ 166 | public function addReadService($service) 167 | { 168 | if ($service instanceof Apache_Solr_Service) 169 | { 170 | $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); 171 | 172 | $this->_readableServices[$id] = $service; 173 | } 174 | else if (is_array($service)) 175 | { 176 | if (isset($service['host']) && isset($service['port']) && isset($service['path'])) 177 | { 178 | $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); 179 | 180 | $this->_readableServices[$id] = $service; 181 | } 182 | else 183 | { 184 | throw new Apache_Solr_InvalidArgumentException('A Readable Service description array does not have all required elements of host, port, and path'); 185 | } 186 | } 187 | } 188 | 189 | /** 190 | * Removes a service instance or descriptor from the available services 191 | * 192 | * @param mixed $service 193 | * 194 | * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid 195 | */ 196 | public function removeReadService($service) 197 | { 198 | $id = ''; 199 | 200 | if ($service instanceof Apache_Solr_Service) 201 | { 202 | $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); 203 | } 204 | else if (is_array($service)) 205 | { 206 | if (isset($service['host']) && isset($service['port']) && isset($service['path'])) 207 | { 208 | $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); 209 | } 210 | else 211 | { 212 | throw new Apache_Solr_InvalidArgumentException('A Readable Service description array does not have all required elements of host, port, and path'); 213 | } 214 | } 215 | else if (is_string($service)) 216 | { 217 | $id = $service; 218 | } 219 | 220 | if ($id && isset($this->_readableServices[$id])) 221 | { 222 | unset($this->_readableServices[$id]); 223 | } 224 | } 225 | 226 | /** 227 | * Adds a service instance or service descriptor (if it is already 228 | * not added) 229 | * 230 | * @param mixed $service 231 | * 232 | * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid 233 | */ 234 | public function addWriteService($service) 235 | { 236 | if ($service instanceof Apache_Solr_Service) 237 | { 238 | $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); 239 | 240 | $this->_writeableServices[$id] = $service; 241 | } 242 | else if (is_array($service)) 243 | { 244 | if (isset($service['host']) && isset($service['port']) && isset($service['path'])) 245 | { 246 | $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); 247 | 248 | $this->_writeableServices[$id] = $service; 249 | } 250 | else 251 | { 252 | throw new Apache_Solr_InvalidArgumentException('A Writeable Service description array does not have all required elements of host, port, and path'); 253 | } 254 | } 255 | } 256 | 257 | /** 258 | * Removes a service instance or descriptor from the available services 259 | * 260 | * @param mixed $service 261 | * 262 | * @throws Apache_Solr_InvalidArgumentException If service descriptor is not valid 263 | */ 264 | public function removeWriteService($service) 265 | { 266 | $id = ''; 267 | 268 | if ($service instanceof Apache_Solr_Service) 269 | { 270 | $id = $this->_getServiceId($service->getHost(), $service->getPort(), $service->getPath()); 271 | } 272 | else if (is_array($service)) 273 | { 274 | if (isset($service['host']) && isset($service['port']) && isset($service['path'])) 275 | { 276 | $id = $this->_getServiceId((string)$service['host'], (int)$service['port'], (string)$service['path']); 277 | } 278 | else 279 | { 280 | throw new Apache_Solr_InvalidArgumentException('A Readable Service description array does not have all required elements of host, port, and path'); 281 | } 282 | } 283 | else if (is_string($service)) 284 | { 285 | $id = $service; 286 | } 287 | 288 | if ($id && isset($this->_writeableServices[$id])) 289 | { 290 | unset($this->_writeableServices[$id]); 291 | } 292 | } 293 | 294 | /** 295 | * Iterate through available read services and select the first with a ping 296 | * that satisfies configured timeout restrictions (or the default) 297 | * 298 | * @return Apache_Solr_Service 299 | * 300 | * @throws Apache_Solr_NoServiceAvailableException If there are no read services that meet requirements 301 | */ 302 | protected function _selectReadService($forceSelect = false) 303 | { 304 | if (!$this->_currentReadService || !isset($this->_readableServices[$this->_currentReadService]) || $forceSelect) 305 | { 306 | if ($this->_currentReadService && isset($this->_readableServices[$this->_currentReadService]) && $forceSelect) 307 | { 308 | // we probably had a communication error, ping the current read service, remove it if it times out 309 | if ($this->_readableServices[$this->_currentReadService]->ping($this->_readPingTimeout) === false) 310 | { 311 | $this->removeReadService($this->_currentReadService); 312 | } 313 | } 314 | 315 | if (count($this->_readableServices)) 316 | { 317 | // select one of the read services at random 318 | $ids = array_keys($this->_readableServices); 319 | 320 | $id = $ids[rand(0, count($ids) - 1)]; 321 | $service = $this->_readableServices[$id]; 322 | 323 | if (is_array($service)) 324 | { 325 | //convert the array definition to a client object 326 | $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']); 327 | $this->_readableServices[$id] = $service; 328 | } 329 | 330 | $service->setCreateDocuments($this->_createDocuments); 331 | $this->_currentReadService = $id; 332 | } 333 | else 334 | { 335 | throw new Apache_Solr_NoServiceAvailableException('No read services were available'); 336 | } 337 | } 338 | 339 | return $this->_readableServices[$this->_currentReadService]; 340 | } 341 | 342 | /** 343 | * Iterate through available write services and select the first with a ping 344 | * that satisfies configured timeout restrictions (or the default) 345 | * 346 | * @return Apache_Solr_Service 347 | * 348 | * @throws Apache_Solr_NoServiceAvailableException If there are no write services that meet requirements 349 | */ 350 | protected function _selectWriteService($forceSelect = false) 351 | { 352 | if($this->_useBackoff) 353 | { 354 | return $this->_selectWriteServiceSafe($forceSelect); 355 | } 356 | 357 | if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect) 358 | { 359 | if ($this->_currentWriteService && isset($this->_writeableServices[$this->_currentWriteService]) && $forceSelect) 360 | { 361 | // we probably had a communication error, ping the current read service, remove it if it times out 362 | if ($this->_writeableServices[$this->_currentWriteService]->ping($this->_writePingTimeout) === false) 363 | { 364 | $this->removeWriteService($this->_currentWriteService); 365 | } 366 | } 367 | 368 | if (count($this->_writeableServices)) 369 | { 370 | // select one of the read services at random 371 | $ids = array_keys($this->_writeableServices); 372 | 373 | $id = $ids[rand(0, count($ids) - 1)]; 374 | $service = $this->_writeableServices[$id]; 375 | 376 | if (is_array($service)) 377 | { 378 | //convert the array definition to a client object 379 | $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']); 380 | $this->_writeableServices[$id] = $service; 381 | } 382 | 383 | $this->_currentWriteService = $id; 384 | } 385 | else 386 | { 387 | throw new Apache_Solr_NoServiceAvailableException('No write services were available'); 388 | } 389 | } 390 | 391 | return $this->_writeableServices[$this->_currentWriteService]; 392 | } 393 | 394 | /** 395 | * Iterate through available write services and select the first with a ping 396 | * that satisfies configured timeout restrictions (or the default). The 397 | * timeout period will increase until a connection is made or the limit is 398 | * reached. This will allow for increased reliability with heavily loaded 399 | * server(s). 400 | * 401 | * @return Apache_Solr_Service 402 | * 403 | * @throws Apache_Solr_NoServiceAvailableException If there are no write services that meet requirements 404 | */ 405 | 406 | protected function _selectWriteServiceSafe($forceSelect = false) 407 | { 408 | if (!$this->_currentWriteService || !isset($this->_writeableServices[$this->_currentWriteService]) || $forceSelect) 409 | { 410 | if (count($this->_writeableServices)) 411 | { 412 | $backoff = $this->_defaultBackoff; 413 | 414 | do { 415 | // select one of the read services at random 416 | $ids = array_keys($this->_writeableServices); 417 | 418 | $id = $ids[rand(0, count($ids) - 1)]; 419 | $service = $this->_writeableServices[$id]; 420 | 421 | if (is_array($service)) 422 | { 423 | //convert the array definition to a client object 424 | $service = new Apache_Solr_Service($service['host'], $service['port'], $service['path']); 425 | $this->_writeableServices[$id] = $service; 426 | } 427 | 428 | $this->_currentWriteService = $id; 429 | 430 | $backoff *= $this->_backoffEscalation; 431 | 432 | if($backoff > $this->_backoffLimit) 433 | { 434 | throw new Apache_Solr_NoServiceAvailableException('No write services were available. All timeouts exceeded.'); 435 | } 436 | 437 | } while($this->_writeableServices[$this->_currentWriteService]->ping($backoff) === false); 438 | } 439 | else 440 | { 441 | throw new Apache_Solr_NoServiceAvailableException('No write services were available'); 442 | } 443 | } 444 | 445 | return $this->_writeableServices[$this->_currentWriteService]; 446 | } 447 | 448 | /** 449 | * Set the create documents flag. This determines whether {@link Apache_Solr_Response} objects will 450 | * parse the response and create {@link Apache_Solr_Document} instances in place. 451 | * 452 | * @param boolean $createDocuments 453 | */ 454 | public function setCreateDocuments($createDocuments) 455 | { 456 | $this->_createDocuments = (bool) $createDocuments; 457 | 458 | // set on current read service 459 | if ($this->_currentReadService) 460 | { 461 | $service = $this->_selectReadService(); 462 | $service->setCreateDocuments($createDocuments); 463 | } 464 | } 465 | 466 | /** 467 | * Get the current state of teh create documents flag. 468 | * 469 | * @return boolean 470 | */ 471 | public function getCreateDocuments() 472 | { 473 | return $this->_createDocuments; 474 | } 475 | 476 | /** 477 | * Raw Add Method. Takes a raw post body and sends it to the update service. Post body 478 | * should be a complete and well formed "add" xml document. 479 | * 480 | * @param string $rawPost 481 | * @return Apache_Solr_Response 482 | * 483 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 484 | */ 485 | public function add($rawPost) 486 | { 487 | $service = $this->_selectWriteService(); 488 | 489 | do 490 | { 491 | try 492 | { 493 | return $service->add($rawPost); 494 | } 495 | catch (Apache_Solr_HttpTransportException $e) 496 | { 497 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 498 | { 499 | throw $e; 500 | } 501 | } 502 | 503 | $service = $this->_selectWriteService(true); 504 | } while ($service); 505 | 506 | return false; 507 | } 508 | 509 | /** 510 | * Add a Solr Document to the index 511 | * 512 | * @param Apache_Solr_Document $document 513 | * @param boolean $allowDups 514 | * @param boolean $overwritePending 515 | * @param boolean $overwriteCommitted 516 | * @return Apache_Solr_Response 517 | * 518 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 519 | */ 520 | public function addDocument(Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true) 521 | { 522 | $service = $this->_selectWriteService(); 523 | 524 | do 525 | { 526 | try 527 | { 528 | return $service->addDocument($document, $allowDups, $overwritePending, $overwriteCommitted); 529 | } 530 | catch (Apache_Solr_HttpTransportException $e) 531 | { 532 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 533 | { 534 | throw $e; 535 | } 536 | } 537 | 538 | $service = $this->_selectWriteService(true); 539 | } while ($service); 540 | 541 | return false; 542 | } 543 | 544 | /** 545 | * Add an array of Solr Documents to the index all at once 546 | * 547 | * @param array $documents Should be an array of Apache_Solr_Document instances 548 | * @param boolean $allowDups 549 | * @param boolean $overwritePending 550 | * @param boolean $overwriteCommitted 551 | * @return Apache_Solr_Response 552 | * 553 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 554 | */ 555 | public function addDocuments($documents, $allowDups = false, $overwritePending = true, $overwriteCommitted = true) 556 | { 557 | $service = $this->_selectWriteService(); 558 | 559 | do 560 | { 561 | try 562 | { 563 | return $service->addDocuments($documents, $allowDups, $overwritePending, $overwriteCommitted); 564 | } 565 | catch (Apache_Solr_HttpTransportException $e) 566 | { 567 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 568 | { 569 | throw $e; 570 | } 571 | } 572 | 573 | $service = $this->_selectWriteService(true); 574 | } while ($service); 575 | 576 | return false; 577 | } 578 | 579 | /** 580 | * Send a commit command. Will be synchronous unless both wait parameters are set 581 | * to false. 582 | * 583 | * @param boolean $waitFlush 584 | * @param boolean $waitSearcher 585 | * @return Apache_Solr_Response 586 | * 587 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 588 | */ 589 | public function commit($optimize = true, $waitFlush = true, $waitSearcher = true, $timeout = 3600) 590 | { 591 | $service = $this->_selectWriteService(); 592 | 593 | do 594 | { 595 | try 596 | { 597 | return $service->commit($optimize, $waitFlush, $waitSearcher, $timeout); 598 | } 599 | catch (Apache_Solr_HttpTransportException $e) 600 | { 601 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 602 | { 603 | throw $e; 604 | } 605 | } 606 | 607 | $service = $this->_selectWriteService(true); 608 | } while ($service); 609 | 610 | return false; 611 | } 612 | 613 | /** 614 | * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be 615 | * a complete and well formed "delete" xml document 616 | * 617 | * @param string $rawPost 618 | * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) 619 | * @return Apache_Solr_Response 620 | * 621 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 622 | */ 623 | public function delete($rawPost, $timeout = 3600) 624 | { 625 | $service = $this->_selectWriteService(); 626 | 627 | do 628 | { 629 | try 630 | { 631 | return $service->delete($rawPost, $timeout); 632 | } 633 | catch (Apache_Solr_HttpTransportException $e) 634 | { 635 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 636 | { 637 | throw $e; 638 | } 639 | } 640 | 641 | $service = $this->_selectWriteService(true); 642 | } while ($service); 643 | 644 | return false; 645 | } 646 | 647 | /** 648 | * Create a delete document based on document ID 649 | * 650 | * @param string $id 651 | * @param boolean $fromPending 652 | * @param boolean $fromCommitted 653 | * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) 654 | * @return Apache_Solr_Response 655 | * 656 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 657 | */ 658 | public function deleteById($id, $fromPending = true, $fromCommitted = true, $timeout = 3600) 659 | { 660 | $service = $this->_selectWriteService(); 661 | 662 | do 663 | { 664 | try 665 | { 666 | return $service->deleteById($id, $fromPending, $fromCommitted, $timeout); 667 | } 668 | catch (Apache_Solr_HttpTransportException $e) 669 | { 670 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 671 | { 672 | throw $e; 673 | } 674 | } 675 | 676 | $service = $this->_selectWriteService(true); 677 | } while ($service); 678 | 679 | return false; 680 | } 681 | 682 | /** 683 | * Create and post a delete document based on multiple document IDs. 684 | * 685 | * @param array $ids Expected to be utf-8 encoded strings 686 | * @param boolean $fromPending 687 | * @param boolean $fromCommitted 688 | * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) 689 | * @return Apache_Solr_Response 690 | * 691 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 692 | */ 693 | public function deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600) 694 | { 695 | $service = $this->_selectWriteService(); 696 | 697 | do 698 | { 699 | try 700 | { 701 | return $service->deleteByMultipleId($ids, $fromPending, $fromCommitted, $timeout); 702 | } 703 | catch (Apache_Solr_HttpTransportException $e) 704 | { 705 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 706 | { 707 | throw $e; 708 | } 709 | } 710 | 711 | $service = $this->_selectWriteService(true); 712 | } while ($service); 713 | 714 | return false; 715 | } 716 | 717 | /** 718 | * Create a delete document based on a query and submit it 719 | * 720 | * @param string $rawQuery 721 | * @param boolean $fromPending 722 | * @param boolean $fromCommitted 723 | * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) 724 | * @return Apache_Solr_Response 725 | * 726 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 727 | */ 728 | public function deleteByQuery($rawQuery, $fromPending = true, $fromCommitted = true, $timeout = 3600) 729 | { 730 | $service = $this->_selectWriteService(); 731 | 732 | do 733 | { 734 | try 735 | { 736 | return $service->deleteByQuery($rawQuery, $fromPending, $fromCommitted, $timeout); 737 | } 738 | catch (Apache_Solr_HttpTransportException $e) 739 | { 740 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 741 | { 742 | throw $e; 743 | } 744 | } 745 | 746 | $service = $this->_selectWriteService(true); 747 | } while ($service); 748 | 749 | return false; 750 | } 751 | 752 | /** 753 | * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how 754 | * to use Solr Cell and what parameters are available. 755 | * 756 | * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." 757 | * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value 758 | * 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 759 | * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). 760 | * 761 | * @param string $file Path to file to extract data from 762 | * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) 763 | * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) 764 | * @param string $mimetype optional mimetype specification (for the file being extracted) 765 | * 766 | * @return Apache_Solr_Response 767 | * 768 | * @throws Apache_Solr_InvalidArgumentException if $file, $params, or $document are invalid. 769 | */ 770 | public function extract($file, $params = array(), $document = null, $mimetype = 'application/octet-stream') 771 | { 772 | $service = $this->_selectWriteService(); 773 | 774 | do 775 | { 776 | try 777 | { 778 | return $service->extract($file, $params, $document, $mimetype); 779 | } 780 | catch (Apache_Solr_HttpTransportException $e) 781 | { 782 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 783 | { 784 | throw $e; 785 | } 786 | } 787 | 788 | $service = $this->_selectWriteService(true); 789 | } while ($service); 790 | 791 | return false; 792 | } 793 | 794 | /** 795 | * Use Solr Cell to extract document contents. See {@link http://wiki.apache.org/solr/ExtractingRequestHandler} for information on how 796 | * to use Solr Cell and what parameters are available. 797 | * 798 | * NOTE: when passing an Apache_Solr_Document instance, field names and boosts will automatically be prepended by "literal." and "boost." 799 | * as appropriate. Any keys from the $params array will NOT be treated this way. Any mappings from the document will overwrite key / value 800 | * 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 801 | * pass in a document isntance with an "id" field" - the document's value(s) will take precedence). 802 | * 803 | * @param string $data Data that will be passed to Solr Cell 804 | * @param array $params optional array of key value pairs that will be sent with the post (see Solr Cell documentation) 805 | * @param Apache_Solr_Document $document optional document that will be used to generate post parameters (literal.* and boost.* params) 806 | * @param string $mimetype optional mimetype specification (for the file being extracted) 807 | * 808 | * @return Apache_Solr_Response 809 | * 810 | * @throws Apache_Solr_InvalidArgumentException if $file, $params, or $document are invalid. 811 | * 812 | * @todo Should be using multipart/form-data to post parameter values, but I could not get my implementation to work. Needs revisisted. 813 | */ 814 | public function extractFromString($data, $params = array(), $document = null, $mimetype = 'application/octet-stream') 815 | { 816 | $service = $this->_selectWriteService(); 817 | 818 | do 819 | { 820 | try 821 | { 822 | return $service->extractFromString($data, $params, $document, $mimetype); 823 | } 824 | catch (Apache_Solr_HttpTransportException $e) 825 | { 826 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 827 | { 828 | throw $e; 829 | } 830 | } 831 | 832 | $service = $this->_selectWriteService(true); 833 | } while ($service); 834 | 835 | return false; 836 | } 837 | 838 | /** 839 | * Send an optimize command. Will be synchronous unless both wait parameters are set 840 | * to false. 841 | * 842 | * @param boolean $waitFlush 843 | * @param boolean $waitSearcher 844 | * @param float $timeout Maximum expected duration of the optimize operation on the server (otherwise, will throw a communication exception) 845 | * @return Apache_Solr_Response 846 | * 847 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 848 | */ 849 | public function optimize($waitFlush = true, $waitSearcher = true, $timeout = 3600) 850 | { 851 | $service = $this->_selectWriteService(); 852 | 853 | do 854 | { 855 | try 856 | { 857 | return $service->optimize($waitFlush, $waitSearcher, $timeout); 858 | } 859 | catch (Apache_Solr_HttpTransportException $e) 860 | { 861 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 862 | { 863 | throw $e; 864 | } 865 | } 866 | 867 | $service = $this->_selectWriteService(true); 868 | } while ($service); 869 | 870 | return false; 871 | } 872 | 873 | /** 874 | * Simple Search interface 875 | * 876 | * @param string $query The raw query string 877 | * @param int $offset The starting offset for result documents 878 | * @param int $limit The maximum number of result documents to return 879 | * @param array $params key / value pairs for query parameters, use arrays for multivalued parameters 880 | * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST) 881 | * @return Apache_Solr_Response 882 | * 883 | * @throws Apache_Solr_HttpTransportException If an error occurs during the service call 884 | */ 885 | public function search($query, $offset = 0, $limit = 10, $params = array(), $method = Apache_Solr_Service::METHOD_GET) 886 | { 887 | $service = $this->_selectReadService(); 888 | 889 | do 890 | { 891 | try 892 | { 893 | return $service->search($query, $offset, $limit, $params, $method); 894 | } 895 | catch (Apache_Solr_HttpTransportException $e) 896 | { 897 | if ($e->getCode() != 0) //IF NOT COMMUNICATION ERROR 898 | { 899 | throw $e; 900 | } 901 | } 902 | 903 | $service = $this->_selectReadService(true); 904 | } while ($service); 905 | 906 | return false; 907 | } 908 | } 909 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2011, Servigistics, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Servigistics, Inc. nor the names of 13 | its contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apache Solr PHP client 2 | [![Build Status](https://travis-ci.org/reprovinci/solr-php-client.png)](https://travis-ci.org/reprovinci/solr-php-client) 3 | 4 | A PHP library for indexing and searching documents within an [Apache Solr](http://lucene.apache.org/solr/) 5 | installation. 6 | 7 | This client library was forked from Donovan Jiménez' Google Code project: http://groups.google.com/group/php-solr-client 8 | Before that, it was hosted in Solr's issue tracker: https://issues.apache.org/jira/browse/SOLR-341 9 | 10 | ## What is Apache Solr? 11 | > Solr is an open source enterprise search server based on the Lucene Java search library, with XML/HTTP 12 | and JSON APIs, hit highlighting, faceted search, caching, replication, a web administration interface 13 | and many more features. It runs in a Java servlet container such as Tomcat. 14 | > For more information about Solr, please see the [Solr Web Page](http://lucene.apache.org/solr/). The 15 | project's [Wiki](http://wiki.apache.org/solr/) is the de facto starting point for information on how 16 | to install and configure Solr for your individual needs. 17 | 18 | ## Features 19 | * Quering, deleting, optimizing. 20 | * Support for both Solr 3 and Solr 4 through compatibility layers. 21 | * Support for soft commit with Solr 4. 22 | 23 | ## Installation 24 | 25 | ### Composer (PHP 5 >= 5.3.0) 26 | * [Getting started](http://getcomposer.org/doc/00-intro.md) with Composer 27 | * `reprovinci/solr-php-client` on [Packagist](http://packagist.org/packages/reprovinci/solr-php-client) 28 | 29 | Just `require` `reprovinci/solr-php-client`: 30 | 31 | ```json 32 | { 33 | "require": { 34 | "reprovinci/solr-php-client": "1.0.x" 35 | } 36 | } 37 | ``` 38 | 39 | ## Testing 40 | The code is automatically tested by [Travis](travis-ci.org/reprovinci/solr-php-client) after each push. 41 | Travis currently reports the following build status: 42 | [![Build Status](https://travis-ci.org/reprovinci/solr-php-client.png)](https://travis-ci.org/reprovinci/solr-php-client). 43 | 44 | If you want to test the code yourself, follow the following instructions: 45 | 46 | ### Composer (PHP 5 >= 5.3.0) 47 | Just install dev dependencies and run PHPUnit: 48 | 49 | ```sh 50 | composer install --dev 51 | bin/phpunit tests 52 | ``` 53 | 54 | ## Credits 55 | 56 | * Thanks to [@GZyl](https://github.com/GZyl) for fixing ```` compatibility between Solr versions. [#7](https://github.com/reprovinci/solr-php-client/issues/7) 57 | 58 | ## Further Information 59 | Please see [Frequently Asked Questions](http://code.google.com/p/solr-php-client/wiki/FAQ) 60 | 61 | Or, if you have usage questions, try posting to the 62 | [USER DISCUSSION GROUP](http://groups.google.com/group/php-solr-client). 63 | 64 | ## Disclaimer 65 | This PHP library is not part of the Apache Solr project, nor is it maintained by the Apache Software Foundation. 66 | All linked Apache Solr documentation or logos remain the sole property of the Apache Solr project, its authors, 67 | and the Apache Software Foundation. 68 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reprovinci/solr-php-client", 3 | "description": "A PHP library for indexing and searching documents within an Apache Solr installation.", 4 | "license": "BSD-3-Clause", 5 | "authors": [ 6 | { 7 | "name": "Donovan Jiménez", 8 | "email": "donovan.jimenez@gmail.com", 9 | "role": "Developer" 10 | }, 11 | { 12 | "name": "Reinier Kip", 13 | "email": "github@reinierkip.nl", 14 | "role": "Developer" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-0": { 19 | "Apache_Solr_": ["", "tests"] 20 | } 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "3.7.*" 24 | }, 25 | "config": { 26 | "bin-dir": "bin/" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "422be6f08003663225b1b5c913ab3bbe", 3 | "packages": [ 4 | 5 | ], 6 | "packages-dev": [ 7 | { 8 | "name": "phpunit/php-code-coverage", 9 | "version": "1.2.3", 10 | "source": { 11 | "type": "git", 12 | "url": "git://github.com/sebastianbergmann/php-code-coverage.git", 13 | "reference": "1.2.3" 14 | }, 15 | "dist": { 16 | "type": "zip", 17 | "url": "https://github.com/sebastianbergmann/php-code-coverage/zipball/1.2.3", 18 | "reference": "1.2.3", 19 | "shasum": "" 20 | }, 21 | "require": { 22 | "php": ">=5.3.3", 23 | "phpunit/php-file-iterator": ">=1.3.0@stable", 24 | "phpunit/php-token-stream": ">=1.1.3@stable", 25 | "phpunit/php-text-template": ">=1.1.1@stable" 26 | }, 27 | "suggest": { 28 | "ext-dom": "*", 29 | "ext-reflection": "*", 30 | "ext-spl": "*", 31 | "ext-xdebug": ">=2.0.5" 32 | }, 33 | "time": "2012-10-05 00:00:00", 34 | "type": "library", 35 | "installation-source": "dist", 36 | "autoload": { 37 | "classmap": [ 38 | "PHP/" 39 | ] 40 | }, 41 | "include-path": [ 42 | "" 43 | ], 44 | "license": [ 45 | "BSD-3-Clause" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Sebastian Bergmann", 50 | "email": "sb@sebastian-bergmann.de", 51 | "role": "lead" 52 | } 53 | ], 54 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 55 | "homepage": "http://www.phpunit.de/", 56 | "keywords": [ 57 | "testing", 58 | "coverage", 59 | "xunit" 60 | ] 61 | }, 62 | { 63 | "name": "phpunit/php-file-iterator", 64 | "version": "1.3.3", 65 | "source": { 66 | "type": "git", 67 | "url": "git://github.com/sebastianbergmann/php-file-iterator.git", 68 | "reference": "1.3.3" 69 | }, 70 | "dist": { 71 | "type": "zip", 72 | "url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3", 73 | "reference": "1.3.3", 74 | "shasum": "" 75 | }, 76 | "require": { 77 | "php": ">=5.2.7" 78 | }, 79 | "time": "2012-10-05 09:06:25", 80 | "type": "library", 81 | "installation-source": "dist", 82 | "autoload": { 83 | "classmap": [ 84 | "File/" 85 | ] 86 | }, 87 | "include-path": [ 88 | "" 89 | ], 90 | "license": [ 91 | "BSD-3-Clause" 92 | ], 93 | "authors": [ 94 | { 95 | "name": "Sebastian Bergmann", 96 | "email": "sb@sebastian-bergmann.de", 97 | "role": "lead" 98 | } 99 | ], 100 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 101 | "homepage": "http://www.phpunit.de/", 102 | "keywords": [ 103 | "filesystem", 104 | "iterator" 105 | ] 106 | }, 107 | { 108 | "name": "phpunit/php-text-template", 109 | "version": "1.1.3", 110 | "source": { 111 | "type": "git", 112 | "url": "git://github.com/sebastianbergmann/php-text-template.git", 113 | "reference": "1.1.3" 114 | }, 115 | "dist": { 116 | "type": "zip", 117 | "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.3", 118 | "reference": "1.1.3", 119 | "shasum": "" 120 | }, 121 | "require": { 122 | "php": ">=5.2.7" 123 | }, 124 | "time": "2012-10-05 09:17:21", 125 | "type": "library", 126 | "installation-source": "dist", 127 | "autoload": { 128 | "classmap": [ 129 | "Text/" 130 | ] 131 | }, 132 | "include-path": [ 133 | "" 134 | ], 135 | "license": [ 136 | "BSD-3-Clause" 137 | ], 138 | "authors": [ 139 | { 140 | "name": "Sebastian Bergmann", 141 | "email": "sb@sebastian-bergmann.de", 142 | "role": "lead" 143 | } 144 | ], 145 | "description": "Simple template engine.", 146 | "homepage": "http://www.phpunit.de/", 147 | "keywords": [ 148 | "template" 149 | ] 150 | }, 151 | { 152 | "name": "phpunit/php-timer", 153 | "version": "1.0.4", 154 | "source": { 155 | "type": "git", 156 | "url": "git://github.com/sebastianbergmann/php-timer.git", 157 | "reference": "1.0.4" 158 | }, 159 | "dist": { 160 | "type": "zip", 161 | "url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4", 162 | "reference": "1.0.4", 163 | "shasum": "" 164 | }, 165 | "require": { 166 | "php": ">=5.2.7" 167 | }, 168 | "time": "2012-10-05 09:09:13", 169 | "type": "library", 170 | "installation-source": "dist", 171 | "autoload": { 172 | "classmap": [ 173 | "PHP/" 174 | ] 175 | }, 176 | "include-path": [ 177 | "" 178 | ], 179 | "license": [ 180 | "BSD-3-Clause" 181 | ], 182 | "authors": [ 183 | { 184 | "name": "Sebastian Bergmann", 185 | "email": "sb@sebastian-bergmann.de", 186 | "role": "lead" 187 | } 188 | ], 189 | "description": "Utility class for timing", 190 | "homepage": "http://www.phpunit.de/", 191 | "keywords": [ 192 | "timer" 193 | ] 194 | }, 195 | { 196 | "name": "phpunit/php-token-stream", 197 | "version": "1.1.5", 198 | "source": { 199 | "type": "git", 200 | "url": "git://github.com/sebastianbergmann/php-token-stream.git", 201 | "reference": "1.1.5" 202 | }, 203 | "dist": { 204 | "type": "zip", 205 | "url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5", 206 | "reference": "1.1.5", 207 | "shasum": "" 208 | }, 209 | "require": { 210 | "php": ">=5.2.7", 211 | "ext-tokenizer": "*" 212 | }, 213 | "time": "2012-10-05 09:17:52", 214 | "type": "library", 215 | "installation-source": "dist", 216 | "autoload": { 217 | "classmap": [ 218 | "PHP/" 219 | ] 220 | }, 221 | "include-path": [ 222 | "" 223 | ], 224 | "license": [ 225 | "BSD-3-Clause" 226 | ], 227 | "authors": [ 228 | { 229 | "name": "Sebastian Bergmann", 230 | "email": "sb@sebastian-bergmann.de", 231 | "role": "lead" 232 | } 233 | ], 234 | "description": "Wrapper around PHP's tokenizer extension.", 235 | "homepage": "http://www.phpunit.de/", 236 | "keywords": [ 237 | "tokenizer" 238 | ] 239 | }, 240 | { 241 | "name": "phpunit/phpunit", 242 | "version": "3.7.6", 243 | "source": { 244 | "type": "git", 245 | "url": "git://github.com/sebastianbergmann/phpunit.git", 246 | "reference": "3.7.6" 247 | }, 248 | "dist": { 249 | "type": "zip", 250 | "url": "https://github.com/sebastianbergmann/phpunit/zipball/3.7.6", 251 | "reference": "3.7.6", 252 | "shasum": "" 253 | }, 254 | "require": { 255 | "php": ">=5.3.3", 256 | "phpunit/php-file-iterator": ">=1.3.1", 257 | "phpunit/php-text-template": ">=1.1.1", 258 | "phpunit/php-code-coverage": ">=1.2.1", 259 | "phpunit/php-timer": ">=1.0.2", 260 | "phpunit/phpunit-mock-objects": ">=1.2.0", 261 | "symfony/yaml": ">=2.1.0", 262 | "ext-dom": "*", 263 | "ext-pcre": "*", 264 | "ext-reflection": "*", 265 | "ext-spl": "*" 266 | }, 267 | "suggest": { 268 | "phpunit/php-invoker": ">=1.1.0", 269 | "ext-json": "*", 270 | "ext-simplexml": "*", 271 | "ext-tokenizer": "*" 272 | }, 273 | "time": "2012-10-06 23:46:25", 274 | "bin": [ 275 | "composer/bin/phpunit" 276 | ], 277 | "type": "library", 278 | "extra": { 279 | "branch-alias": { 280 | "dev-master": "3.7.x-dev" 281 | } 282 | }, 283 | "installation-source": "dist", 284 | "autoload": { 285 | "classmap": [ 286 | "PHPUnit/" 287 | ] 288 | }, 289 | "include-path": [ 290 | "", 291 | "../../symfony/yaml/" 292 | ], 293 | "license": [ 294 | "BSD-3-Clause" 295 | ], 296 | "authors": [ 297 | { 298 | "name": "Sebastian Bergmann", 299 | "email": "sebastian@phpunit.de", 300 | "role": "lead" 301 | } 302 | ], 303 | "description": "The PHP Unit Testing framework.", 304 | "homepage": "http://www.phpunit.de/", 305 | "keywords": [ 306 | "testing", 307 | "phpunit", 308 | "xunit" 309 | ] 310 | }, 311 | { 312 | "name": "phpunit/phpunit-mock-objects", 313 | "version": "1.2.1", 314 | "source": { 315 | "type": "git", 316 | "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", 317 | "reference": "1.2.1" 318 | }, 319 | "dist": { 320 | "type": "zip", 321 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/zipball/1.2.1", 322 | "reference": "1.2.1", 323 | "shasum": "" 324 | }, 325 | "require": { 326 | "php": ">=5.3.3", 327 | "phpunit/php-text-template": ">=1.1.1@stable", 328 | "ext-reflection": "*", 329 | "ext-spl": "*" 330 | }, 331 | "suggest": { 332 | "ext-soap": "*" 333 | }, 334 | "time": "2012-10-05 00:00:00", 335 | "type": "library", 336 | "installation-source": "dist", 337 | "autoload": { 338 | "classmap": [ 339 | "PHPUnit/" 340 | ] 341 | }, 342 | "include-path": [ 343 | "" 344 | ], 345 | "license": [ 346 | "BSD-3-Clause" 347 | ], 348 | "authors": [ 349 | { 350 | "name": "Sebastian Bergmann", 351 | "email": "sb@sebastian-bergmann.de", 352 | "role": "lead" 353 | } 354 | ], 355 | "description": "Mock Object library for PHPUnit", 356 | "homepage": "http://www.phpunit.de/", 357 | "keywords": [ 358 | "mock", 359 | "xunit" 360 | ] 361 | }, 362 | { 363 | "name": "symfony/yaml", 364 | "version": "v2.1.2", 365 | "target-dir": "Symfony/Component/Yaml", 366 | "source": { 367 | "type": "git", 368 | "url": "https://github.com/symfony/Yaml", 369 | "reference": "v2.1.0-RC2" 370 | }, 371 | "dist": { 372 | "type": "zip", 373 | "url": "https://github.com/symfony/Yaml/zipball/v2.1.0-RC2", 374 | "reference": "v2.1.0-RC2", 375 | "shasum": "" 376 | }, 377 | "require": { 378 | "php": ">=5.3.3" 379 | }, 380 | "time": "2012-08-22 13:48:41", 381 | "type": "library", 382 | "extra": { 383 | "branch-alias": { 384 | "dev-master": "2.1-dev" 385 | } 386 | }, 387 | "installation-source": "dist", 388 | "autoload": { 389 | "psr-0": { 390 | "Symfony\\Component\\Yaml": "" 391 | } 392 | }, 393 | "license": [ 394 | "MIT" 395 | ], 396 | "authors": [ 397 | { 398 | "name": "Fabien Potencier", 399 | "email": "fabien@symfony.com" 400 | }, 401 | { 402 | "name": "Symfony Community", 403 | "homepage": "http://symfony.com/contributors" 404 | } 405 | ], 406 | "description": "Symfony Yaml Component", 407 | "homepage": "http://symfony.com" 408 | } 409 | ], 410 | "aliases": [ 411 | 412 | ], 413 | "minimum-stability": "stable", 414 | "stability-flags": [ 415 | 416 | ] 417 | } 418 | -------------------------------------------------------------------------------- /tests/Apache/Solr/DocumentTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_Document Unit Test 40 | */ 41 | class Apache_Solr_DocumentTest extends PHPUnit_Framework_TestCase 42 | { 43 | /** 44 | * Fixture used for testing 45 | * 46 | * @var Apache_Solr_Document 47 | */ 48 | private $_fixture; 49 | 50 | /** 51 | * Setup for the fixture before each unit test - part of test case API 52 | */ 53 | protected function setup() 54 | { 55 | $this->_fixture = new Apache_Solr_Document(); 56 | } 57 | 58 | /** 59 | * Teardown after each unit test - part of test case API 60 | */ 61 | protected function tearDown() 62 | { 63 | unset($this->_fixture); 64 | } 65 | 66 | public function testDefaultStateAfterConstructor() 67 | { 68 | // document boost should be false 69 | $this->assertFalse($this->_fixture->getBoost()); 70 | 71 | // document fields should be empty 72 | $this->assertEquals(0, count($this->_fixture->getFieldNames())); 73 | $this->assertEquals(0, count($this->_fixture->getFieldValues())); 74 | $this->assertEquals(0, count($this->_fixture->getFieldBoosts())); 75 | 76 | // document iterator should be empty 77 | $this->assertEquals(0, iterator_count($this->_fixture)); 78 | } 79 | 80 | public function testSetAndGetField() 81 | { 82 | $field = 'field'; 83 | $value = 'value'; 84 | $boost = 0.5; 85 | 86 | // set the field 87 | $this->_fixture->setField($field, $value, $boost); 88 | 89 | $result = $this->_fixture->getField($field); 90 | 91 | // check the array values 92 | $this->assertTrue(is_array($result)); 93 | $this->assertEquals($field, $result['name']); 94 | $this->assertEquals($value, $result['value']); 95 | $this->assertEquals($boost, $result['boost']); 96 | } 97 | 98 | public function testGetFieldReturnsFalseForNonExistentField() 99 | { 100 | $this->assertFalse($this->_fixture->getField('field')); 101 | } 102 | 103 | public function testMagicGetForFieldValues() 104 | { 105 | $field = 'field'; 106 | $value = 'value'; 107 | 108 | $this->_fixture->setField($field, $value); 109 | 110 | // test the __get value 111 | $this->assertEquals($value, $this->_fixture->{$field}); 112 | } 113 | 114 | /** 115 | * Added for issue #48 (http://code.google.com/p/solr-php-client/issues/detail?id=48) 116 | */ 117 | public function testMagicGetReturnsNullForNonExistentField() 118 | { 119 | $this->assertNull($this->_fixture->nonExistent); 120 | } 121 | 122 | public function testMagicSetForFieldValues() 123 | { 124 | $field = 'field'; 125 | $value = 'value'; 126 | 127 | // set field value with magic __set 128 | $this->_fixture->{$field} = $value; 129 | 130 | $fieldArray = $this->_fixture->getField($field); 131 | 132 | // set values 133 | $this->assertEquals($field, $fieldArray['name']); 134 | $this->assertEquals($value, $fieldArray['value']); 135 | $this->assertTrue($fieldArray['boost'] === false); 136 | } 137 | 138 | public function testMagicIssetForNonExistentField() 139 | { 140 | $this->assertFalse(isset($this->_fixture->field)); 141 | } 142 | 143 | public function testMagicIssetForExistingField() 144 | { 145 | $field = 'field'; 146 | $this->_fixture->{$field} = 'value'; 147 | $this->assertTrue(isset($this->_fixture->{$field})); 148 | } 149 | 150 | public function testMagicUnsetForExistingField() 151 | { 152 | $field = 'field'; 153 | 154 | $this->_fixture->{$field} = 'value'; 155 | 156 | // now unset the field 157 | unset($this->_fixture->{$field}); 158 | 159 | // now test that its unset 160 | $this->assertFalse(isset($this->_fixture->{$field})); 161 | } 162 | 163 | public function testMagicUnsetForNonExistingField() 164 | { 165 | $field = 'field'; 166 | unset($this->_fixture->{$field}); 167 | 168 | // now test that it still does not exist 169 | $this->assertFalse(isset($this->_fixture->{$field})); 170 | } 171 | 172 | public function testSetAndGetFieldBoostWithPositiveNumberSetsBoost() 173 | { 174 | $field = 'field'; 175 | $boost = 0.5; 176 | 177 | $this->_fixture->setFieldBoost($field, $boost); 178 | 179 | // test the field boost 180 | $this->assertEquals($boost, $this->_fixture->getFieldBoost($field)); 181 | } 182 | 183 | public function testSetAndGetFieldBoostWithZeroRemovesBoost() 184 | { 185 | $field = 'field'; 186 | $boost = 0; 187 | 188 | $this->_fixture->setFieldBoost($field, $boost); 189 | 190 | // test the field boost 191 | $this->assertTrue($this->_fixture->getFieldBoost($field) === false); 192 | } 193 | 194 | public function testSetAndGetFieldBoostWithNegativeNumberRemovesBoost() 195 | { 196 | $field = 'field'; 197 | $boost = -1; 198 | 199 | $this->_fixture->setFieldBoost($field, $boost); 200 | 201 | // test the field boost 202 | $this->assertTrue($this->_fixture->getFieldBoost($field) === false); 203 | } 204 | 205 | public function testSetAndGetFieldBoostWithNonNumberRemovesBoost() 206 | { 207 | $field = 'field'; 208 | $boost = "i am not a number"; 209 | 210 | $this->_fixture->setFieldBoost($field, $boost); 211 | 212 | // test the field boost 213 | $this->assertTrue($this->_fixture->getFieldBoost($field) === false); 214 | } 215 | 216 | public function testSetAndGetBoostWithPositiveNumberSetsBoost() 217 | { 218 | $boost = 0.5; 219 | $this->_fixture->setBoost($boost); 220 | 221 | // the boost should now be set 222 | $this->assertEquals($boost, $this->_fixture->getBoost()); 223 | } 224 | 225 | public function testSetAndGetBoostWithZeroRemovesBoost() 226 | { 227 | $this->_fixture->setBoost(0); 228 | 229 | // should be boolean false 230 | $this->assertTrue($this->_fixture->getBoost() === false); 231 | } 232 | 233 | public function testSetAndGetBoostWithNegativeNumberRemovesBoost() 234 | { 235 | $this->_fixture->setBoost(-1); 236 | 237 | // should be boolean false 238 | $this->assertTrue($this->_fixture->getBoost() === false); 239 | } 240 | 241 | public function testSetAndGetBoostWithNonNumberRemovesBoost() 242 | { 243 | $this->_fixture->setBoost("i am not a number"); 244 | 245 | // should be boolean false 246 | $this->assertTrue($this->_fixture->getBoost() === false); 247 | } 248 | 249 | public function testAddFieldCreatesMultiValueWhenFieldDoesNotExist() 250 | { 251 | $field = 'field'; 252 | $value = 'value'; 253 | 254 | $this->_fixture->addField($field, $value); 255 | 256 | // check that value is an array with correct values 257 | $fieldValue = $this->_fixture->{$field}; 258 | 259 | $this->assertTrue(is_array($fieldValue)); 260 | $this->assertEquals(1, count($fieldValue)); 261 | $this->assertEquals($value, $fieldValue[0]); 262 | } 263 | 264 | /** 265 | * setMultiValue has been deprecated and defers to addField 266 | * 267 | * @deprecated 268 | */ 269 | public function testSetMultiValueCreateMultiValueWhenFieldDoesNotExist() 270 | { 271 | $field = 'field'; 272 | $value = 'value'; 273 | 274 | $this->_fixture->setMultiValue($field, $value); 275 | 276 | // check that value is an array with correct values 277 | $fieldValue = $this->_fixture->{$field}; 278 | 279 | $this->assertTrue(is_array($fieldValue)); 280 | $this->assertEquals(1, count($fieldValue)); 281 | $this->assertEquals($value, $fieldValue[0]); 282 | } 283 | 284 | public function testAddFieldCreatesMultiValueWhenFieldDoesExistAsSingleValue() 285 | { 286 | $field = 'field'; 287 | $value1 = 'value1'; 288 | $value2 = 'value2'; 289 | 290 | // set first value as singular value 291 | $this->_fixture->{$field} = $value1; 292 | 293 | // add a second value with addField 294 | $this->_fixture->addField($field, $value2); 295 | 296 | // check that value is an array with correct values 297 | $fieldValue = $this->_fixture->{$field}; 298 | 299 | $this->assertTrue(is_array($fieldValue)); 300 | $this->assertEquals(2, count($fieldValue)); 301 | $this->assertEquals($value1, $fieldValue[0]); 302 | $this->assertEquals($value2, $fieldValue[1]); 303 | } 304 | 305 | /** 306 | * setMultiValue has been deprecated and defers to addField 307 | * 308 | * @deprecated 309 | */ 310 | public function testSetMultiValueCreatesMultiValueWhenFieldDoesExistAsSingleValue() 311 | { 312 | $field = 'field'; 313 | $value1 = 'value1'; 314 | $value2 = 'value2'; 315 | 316 | // set first value as singular value 317 | $this->_fixture->{$field} = $value1; 318 | 319 | // add a second value with addField 320 | $this->_fixture->setMultiValue($field, $value2); 321 | 322 | // check that value is an array with correct values 323 | $fieldValue = $this->_fixture->{$field}; 324 | 325 | $this->assertTrue(is_array($fieldValue)); 326 | $this->assertEquals(2, count($fieldValue)); 327 | $this->assertEquals($value1, $fieldValue[0]); 328 | $this->assertEquals($value2, $fieldValue[1]); 329 | } 330 | 331 | public function testAddFieldWithBoostSetsFieldBoost() 332 | { 333 | $field = 'field'; 334 | $boost = 0.5; 335 | 336 | $this->_fixture->addField($field, 'value', $boost); 337 | 338 | // check the field boost 339 | $this->assertEquals($boost, $this->_fixture->getFieldBoost($field)); 340 | } 341 | 342 | public function testAddFieldWithBoostMultipliesWithAPreexistingBoost() 343 | { 344 | $field = 'field'; 345 | $boost = 0.5; 346 | 347 | // set a field with a boost 348 | $this->_fixture->setField($field, 'value1', $boost); 349 | 350 | // now add another value with the same boost 351 | $this->_fixture->addField($field, 'value2', $boost); 352 | 353 | // new boost should be $boost * $boost 354 | $this->assertEquals($boost * $boost, $this->_fixture->getFieldBoost($field)); 355 | } 356 | 357 | public function testGetFieldNamesIsInitiallyEmpty() 358 | { 359 | $fieldNames = $this->_fixture->getFieldNames(); 360 | 361 | $this->assertTrue(empty($fieldNames)); 362 | } 363 | 364 | public function testGetFieldNamesAfterFieldIsSetIsNotEmpty() 365 | { 366 | $field = 'field'; 367 | 368 | $this->_fixture->{$field} = 'value'; 369 | $fieldNames = $this->_fixture->getFieldNames(); 370 | 371 | $this->assertTrue(!empty($fieldNames)); 372 | $this->assertEquals(1, count($fieldNames)); 373 | $this->assertEquals($field, $fieldNames[0]); 374 | } 375 | 376 | public function testGetFieldValuesIsInitiallyEmpty() 377 | { 378 | $fieldValues = $this->_fixture->getFieldValues(); 379 | 380 | $this->assertTrue(empty($fieldValues)); 381 | } 382 | 383 | public function testGetFieldValuessAfterFieldIsSetIsNotEmpty() 384 | { 385 | $value = 'value'; 386 | 387 | $this->_fixture->field = $value; 388 | $fieldValues = $this->_fixture->getFieldValues(); 389 | 390 | $this->assertTrue(!empty($fieldValues)); 391 | $this->assertEquals(1, count($fieldValues)); 392 | $this->assertEquals($value, $fieldValues[0]); 393 | } 394 | 395 | public function testGetIteratorAfterFieldValueIsSet() 396 | { 397 | $field = 'field'; 398 | $value = 'value'; 399 | 400 | $this->_fixture->{$field} = $value; 401 | 402 | $itemCount = 0; 403 | 404 | foreach ($this->_fixture as $iteratedField => $iteratedValue) 405 | { 406 | ++$itemCount; 407 | 408 | // test field and value 409 | $this->assertEquals($field, $iteratedField); 410 | $this->assertEquals($value, $iteratedValue); 411 | } 412 | 413 | // test number of iterations is 1 414 | $this->assertEquals(1, $itemCount); 415 | } 416 | 417 | public function testClearReturnsDocumentToDefaultState() 418 | { 419 | // set the document boost 420 | $this->_fixture->setBoost(0.5); 421 | 422 | // set a field 423 | $this->_fixture->someField = "some value"; 424 | 425 | // clear the document to remove boost and fields 426 | $this->_fixture->clear(); 427 | 428 | // document boost should now be false 429 | $this->assertFalse($this->_fixture->getBoost()); 430 | 431 | // document fields should now be empty 432 | $this->assertEquals(0, count($this->_fixture->getFieldNames())); 433 | $this->assertEquals(0, count($this->_fixture->getFieldValues())); 434 | $this->assertEquals(0, count($this->_fixture->getFieldBoosts())); 435 | 436 | // document iterator should now be empty 437 | $this->assertEquals(0, iterator_count($this->_fixture)); 438 | } 439 | } -------------------------------------------------------------------------------- /tests/Apache/Solr/HttpTransport/AbstractTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_HttpTransport_Abstract Unit Tests 40 | */ 41 | abstract class Apache_Solr_HttpTransport_AbstractTest extends PHPUnit_Framework_TestCase 42 | { 43 | const TIMEOUT = 10; 44 | 45 | // request our copyright file from googlecode for GET and HEAD 46 | const GET_URL = "http://reinierkip.nl/echo/"; 47 | const GET_RESPONSE_MIME_TYPE = 'application/json'; 48 | const GET_RESPONSE_ENCODING = 'UTF-8'; 49 | const GET_RESPONSE_MATCH = '{"method":"GET"}'; 50 | 51 | // post to the issue list page with a search for 'meh' 52 | const POST_URL = "http://reinierkip.nl/echo/"; 53 | const POST_DATA = "ok=true"; 54 | const POST_REQUEST_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=UTF-8'; 55 | 56 | const POST_RESPONSE_MIME_TYPE = 'application/json'; 57 | const POST_RESPONSE_ENCODING = 'UTF-8'; 58 | const POST_RESPONSE_MATCH = '{"method":"POST","data":{"ok":"true"}}'; 59 | 60 | abstract public function getFixture(); 61 | 62 | public function testGetDefaultTimeoutWithDefaultConstructor() 63 | { 64 | $fixture = $this->getFixture(); 65 | $timeout = $fixture->getDefaultTimeout(); 66 | 67 | $this->assertGreaterThan(0, $timeout); 68 | } 69 | 70 | public function testGetDefaultTimeoutSetToSixtyForBadValues() 71 | { 72 | // first set our default_socket_timeout ini setting 73 | $previousValue = ini_get('default_socket_timeout'); 74 | ini_set('default_socket_timeout', 0); 75 | 76 | $fixture = $this->getFixture(); 77 | $timeout = $fixture->getDefaultTimeout(); 78 | 79 | // reset timeout 80 | ini_set('default_socket_timeout', $previousValue); 81 | 82 | $this->assertEquals(60, $timeout); 83 | } 84 | 85 | public function testSetDefaultTimeout() 86 | { 87 | $newTimeout = 1234; 88 | 89 | $fixture = $this->getFixture(); 90 | $fixture->setDefaultTimeout($newTimeout); 91 | $timeout = $fixture->getDefaultTimeout(); 92 | 93 | $this->assertEquals($newTimeout, $timeout); 94 | } 95 | 96 | public function testPerformGetRequest() 97 | { 98 | $fixture = $this->getFixture(); 99 | $fixture->setDefaultTimeout(self::TIMEOUT); 100 | 101 | $response = $fixture->performGetRequest(self::GET_URL); 102 | 103 | $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); 104 | 105 | $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); 106 | $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); 107 | $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); 108 | $this->assertStringStartsWith(self::GET_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); 109 | } 110 | 111 | public function testPerformGetRequestWithTimeout() 112 | { 113 | $fixture = $this->getFixture(); 114 | $response = $fixture->performGetRequest(self::GET_URL, self::TIMEOUT); 115 | 116 | $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); 117 | 118 | $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); 119 | $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); 120 | $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); 121 | $this->assertStringStartsWith(self::GET_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); 122 | } 123 | 124 | public function testPerformHeadRequest() 125 | { 126 | $fixture = $this->getFixture(); 127 | $fixture->setDefaultTimeout(self::TIMEOUT); 128 | 129 | $response = $fixture->performHeadRequest(self::GET_URL); 130 | 131 | // we should get everything the same as a get, except the body 132 | $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); 133 | 134 | $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); 135 | $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); 136 | $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); 137 | $this->assertEquals("", $response->getBody(), 'body was not empty'); 138 | } 139 | 140 | public function testPerformHeadRequestWithTimeout() 141 | { 142 | $fixture = $this->getFixture(); 143 | $response = $fixture->performHeadRequest(self::GET_URL, self::TIMEOUT); 144 | 145 | // we should get everything the same as a get, except the body 146 | $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); 147 | 148 | $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); 149 | $this->assertEquals(self::GET_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); 150 | $this->assertEquals(self::GET_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); 151 | $this->assertEquals("", $response->getBody(), 'body was not empty'); 152 | } 153 | 154 | public function testPerformPostRequest() 155 | { 156 | $fixture = $this->getFixture(); 157 | $fixture->setDefaultTimeout(self::TIMEOUT); 158 | 159 | $response = $fixture->performPostRequest(self::POST_URL, self::POST_DATA, self::POST_REQUEST_CONTENT_TYPE); 160 | 161 | $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); 162 | 163 | $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); 164 | $this->assertEquals(self::POST_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); 165 | $this->assertEquals(self::POST_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); 166 | $this->assertStringStartsWith(self::POST_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); 167 | } 168 | 169 | public function testPerformPostRequestWithTimeout() 170 | { 171 | $fixture = $this->getFixture(); 172 | $response = $fixture->performPostRequest(self::POST_URL, self::POST_DATA, self::POST_REQUEST_CONTENT_TYPE, self::TIMEOUT); 173 | 174 | $this->assertInstanceOf('Apache_Solr_HttpTransport_Response', $response); 175 | 176 | $this->assertEquals(200, $response->getStatusCode(), 'Status code was not 200'); 177 | $this->assertEquals(self::POST_RESPONSE_MIME_TYPE, $response->getMimeType(), 'mimetype was not correct'); 178 | $this->assertEquals(self::POST_RESPONSE_ENCODING, $response->getEncoding(), 'character encoding was not correct'); 179 | $this->assertStringStartsWith(self::POST_RESPONSE_MATCH, $response->getBody(), 'body did not start with match text'); 180 | } 181 | 182 | /** 183 | * Test one session doing multiple requests in multiple orders 184 | */ 185 | public function testMultipleRequests() 186 | { 187 | // initial get request 188 | $this->testPerformGetRequest(); 189 | 190 | // head following get 191 | $this->testPerformHeadRequest(); 192 | 193 | // post following head 194 | $this->testPerformPostRequest(); 195 | 196 | // get following post 197 | $this->testPerformGetRequest(); 198 | 199 | // post following get 200 | $this->testPerformPostRequest(); 201 | 202 | // head following post 203 | $this->testPerformHeadRequest(); 204 | 205 | // get following post 206 | $this->testPerformGetRequest(); 207 | } 208 | } -------------------------------------------------------------------------------- /tests/Apache/Solr/HttpTransport/CurlNoReuseTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_HttpTransport_CurlNoReuse Unit Tests 40 | */ 41 | class Apache_Solr_HttpTransport_CurlNoReuseTest extends Apache_Solr_HttpTransport_AbstractTest 42 | { 43 | public function getFixture() 44 | { 45 | // ensure curl is enabled 46 | if (!extension_loaded('curl')) 47 | { 48 | $this->markTestSkipped("curl module is not enabled"); 49 | } 50 | 51 | return new Apache_Solr_HttpTransport_CurlNoReuse(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Apache/Solr/HttpTransport/CurlTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_HttpTransport_Curl Unit Tests 40 | */ 41 | class Apache_Solr_HttpTransport_CurlTest extends Apache_Solr_HttpTransport_AbstractTest 42 | { 43 | public function getFixture() 44 | { 45 | // ensure curl is enabled 46 | if (!extension_loaded('curl')) 47 | { 48 | $this->markTestSkipped("curl module is not enabled"); 49 | } 50 | 51 | return new Apache_Solr_HttpTransport_Curl(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Apache/Solr/HttpTransport/FileGetContentsTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_HttpTransport_FileGetContents Unit Tests 40 | */ 41 | class Apache_Solr_HttpTransport_FileGetContentsTest extends Apache_Solr_HttpTransport_AbstractTest 42 | { 43 | public function getFixture() 44 | { 45 | // make sure allow_url_fopen is on 46 | if (!ini_get("allow_url_fopen")) 47 | { 48 | $this->markTestSkipped("allow_url_fopen is not enabled"); 49 | } 50 | 51 | return new Apache_Solr_HttpTransport_FileGetContents(); 52 | } 53 | } -------------------------------------------------------------------------------- /tests/Apache/Solr/HttpTransport/ResponseTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_HttpTransport_Response Unit Tests 40 | */ 41 | class Apache_Solr_HttpTransport_ResponseTest extends PHPUnit_Framework_TestCase 42 | { 43 | // generated with the following query string: select?q=solr&wt=json 44 | const STATUS_CODE_200 = 200; 45 | const STATUS_MESSAGE_200 = "OK"; 46 | const BODY_200 = '{"responseHeader":{"status":0,"QTime":0,"params":{"q":"solr","wt":"json"}},"response":{"numFound":0,"start":0,"docs":[]}}'; 47 | 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]}]}}'; 48 | const CONTENT_TYPE_200 = "text/plain; charset=utf-8"; 49 | const MIME_TYPE_200 = "text/plain"; 50 | const ENCODING_200 = "utf-8"; 51 | 52 | // generated with the following query string: select?qt=standad&q=solr&wt=json 53 | // NOTE: the intentional mispelling of the standard in the qt parameter 54 | const STATUS_CODE_400 = 400; 55 | const STATUS_MESSAGE_400 = "Bad Request"; 56 | 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

'; 57 | const CONTENT_TYPE_400 = "text/html; charset=utf-8"; 58 | const MIME_TYPE_400 = "text/html"; 59 | const ENCODING_400 = "utf-8"; 60 | 61 | // generated with the following query string: select?q=solr&wt=json on a core that does not exist 62 | const STATUS_CODE_404 = 404; 63 | const STATUS_MESSAGE_404 = "Not Found"; 64 | 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

'; 65 | const CONTENT_TYPE_404 = "text/html; charset=utf-8"; 66 | const MIME_TYPE_404 = "text/html"; 67 | const ENCODING_404 = "utf-8"; 68 | 69 | public static function get0Response() 70 | { 71 | return new Apache_Solr_HttpTransport_Response(null, null, null); 72 | } 73 | 74 | public static function get200Response() 75 | { 76 | return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_200, self::CONTENT_TYPE_200, self::BODY_200); 77 | } 78 | 79 | public static function get200ResponseWithDocuments() 80 | { 81 | return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_200, self::CONTENT_TYPE_200, self::BODY_200_WITH_DOCUMENTS); 82 | } 83 | 84 | public static function get400Response() 85 | { 86 | return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_400, self::CONTENT_TYPE_400, self::BODY_400); 87 | } 88 | 89 | public static function get404Response() 90 | { 91 | return new Apache_Solr_HttpTransport_Response(self::STATUS_CODE_404, self::CONTENT_TYPE_404, self::BODY_404); 92 | } 93 | 94 | public function testGetStatusCode() 95 | { 96 | $fixture = self::get200Response(); 97 | 98 | $statusCode = $fixture->getStatusCode(); 99 | 100 | $this->assertEquals(self::STATUS_CODE_200, $statusCode); 101 | } 102 | 103 | public function testGetStatusMessage() 104 | { 105 | $fixture = self::get200Response(); 106 | 107 | $statusMessage = $fixture->getStatusMessage(); 108 | 109 | $this->assertEquals(self::STATUS_MESSAGE_200, $statusMessage); 110 | } 111 | 112 | public function testGetStatusMessageWithUnknownCode() 113 | { 114 | $fixture = new Apache_Solr_HttpTransport_Response(499, null, null); 115 | 116 | $statusMessage = $fixture->getStatusMessage(); 117 | $this->assertEquals("Unknown Status", $statusMessage); 118 | } 119 | 120 | public function testGetBody() 121 | { 122 | $fixture = self::get200Response(); 123 | 124 | $body = $fixture->getBody(); 125 | 126 | $this->assertEquals(self::BODY_200, $body); 127 | } 128 | 129 | public function testGetMimeType() 130 | { 131 | $fixture = self::get200Response(); 132 | 133 | $mimeType = $fixture->getMimeType(); 134 | 135 | $this->assertEquals(self::MIME_TYPE_200, $mimeType); 136 | } 137 | 138 | public function testGetEncoding() 139 | { 140 | $fixture = self::get200Response(); 141 | 142 | $encoding = $fixture->getEncoding(); 143 | 144 | $this->assertEquals(self::ENCODING_200, $encoding); 145 | } 146 | 147 | public function testGetStatusMessageWhenNotProvided() 148 | { 149 | // test 4 of the most common status code responses, probably don't need 150 | // to test all the codes we have 151 | 152 | $fixture = new Apache_Solr_HttpTransport_Response(null, null, null, null, null); 153 | $this->assertEquals("Communication Error", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 0'); 154 | 155 | $fixture = new Apache_Solr_HttpTransport_Response(200, null, null, null, null); 156 | $this->assertEquals("OK", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 200'); 157 | 158 | $fixture = new Apache_Solr_HttpTransport_Response(400, null, null, null, null); 159 | $this->assertEquals("Bad Request", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 400'); 160 | 161 | $fixture = new Apache_Solr_HttpTransport_Response(404, null, null, null, null); 162 | $this->assertEquals("Not Found", $fixture->getStatusMessage(), 'Did not get correct default status message for status code 404'); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/Apache/Solr/HttpTransportExceptionTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_HttpTransportException Unit Tests 40 | */ 41 | class Apache_Solr_HttpTransportExceptionTest extends PHPUnit_Framework_TestCase 42 | { 43 | /** 44 | * @expectedException PHPUnit_Framework_Error 45 | */ 46 | public function testConstructorRequiresResponse() 47 | { 48 | $fixture = new Apache_Solr_HttpTransportException(); 49 | } 50 | 51 | public function testGetResponse() 52 | { 53 | $response = Apache_Solr_ResponseTest::get0Response(); 54 | $fixture = new Apache_Solr_HttpTransportException($response); 55 | 56 | $this->assertEquals($response, $fixture->getResponse()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Apache/Solr/ResponseTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_Response Unit Test 40 | */ 41 | class Apache_Solr_ResponseTest extends PHPUnit_Framework_TestCase 42 | { 43 | static public function get0Response($createDocuments = true, $collapseSingleValueArrays = true) 44 | { 45 | return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get0Response(), $createDocuments, $collapseSingleValueArrays); 46 | } 47 | 48 | static public function get200Response($createDocuments = true, $collapseSingleValueArrays = true) 49 | { 50 | return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get200Response(), $createDocuments, $collapseSingleValueArrays); 51 | } 52 | 53 | static public function get200ResponseWithDocuments($createDocuments = true, $collapseSingleValueArrays = true) 54 | { 55 | return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get200ResponseWithDocuments(), $createDocuments, $collapseSingleValueArrays); 56 | } 57 | 58 | static public function get400Response($createDocuments = true, $collapseSingleValueArrays = true) 59 | { 60 | return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get400Response(), $createDocuments, $collapseSingleValueArrays); 61 | } 62 | 63 | static public function get404Response($createDocuments = true, $collapseSingleValueArrays = true) 64 | { 65 | return new Apache_Solr_Response(Apache_Solr_HttpTransport_ResponseTest::get404Response(), $createDocuments, $collapseSingleValueArrays); 66 | } 67 | 68 | public function testConstuctorWithValidBodyAndHeaders() 69 | { 70 | $fixture = self::get200Response(); 71 | 72 | // check that we parsed the HTTP status correctly 73 | $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::STATUS_CODE_200, $fixture->getHttpStatus()); 74 | 75 | // check that we received the body correctly 76 | $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::BODY_200, $fixture->getRawResponse()); 77 | 78 | // check that our defaults are correct 79 | $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::ENCODING_200, $fixture->getEncoding()); 80 | $this->assertEquals(Apache_Solr_HttpTransport_ResponseTest::MIME_TYPE_200, $fixture->getType()); 81 | } 82 | 83 | public function testConstructorWithBadBodyAndHeaders() 84 | { 85 | $fixture = self::get0Response(); 86 | 87 | // check that our defaults are correct 88 | $this->assertEquals(0, $fixture->getHttpStatus()); 89 | $this->assertEquals("UTF-8", $fixture->getEncoding()); 90 | $this->assertEquals("text/plain", $fixture->getType()); 91 | } 92 | 93 | public function testMagicGetWithValidBodyAndHeaders() 94 | { 95 | $fixture = self::get200Response(); 96 | 97 | // test top level gets 98 | $this->assertInstanceOf('stdClass', $fixture->responseHeader); 99 | $this->assertEquals(0, $fixture->responseHeader->status); 100 | $this->assertEquals(0, $fixture->responseHeader->QTime); 101 | 102 | $this->assertInstanceOf('stdClass', $fixture->response); 103 | $this->assertEquals(0, $fixture->response->numFound); 104 | 105 | $this->assertTrue(is_array($fixture->response->docs)); 106 | $this->assertEquals(0, count($fixture->response->docs)); 107 | } 108 | 109 | /** 110 | * @expectedException Apache_Solr_ParserException 111 | */ 112 | public function testMagicGetWith0Response() 113 | { 114 | $fixture = self::get0Response(); 115 | 116 | // attempting to magic get a part of the response 117 | // should throw a ParserException 118 | $fixture->responseHeader; 119 | 120 | $this->fail("Expected Apache_Solr_ParserException was not raised"); 121 | } 122 | 123 | /** 124 | * @expectedException Apache_Solr_ParserException 125 | */ 126 | public function testMagicGetWith400Response() 127 | { 128 | $fixture = self::get400Response(); 129 | 130 | // attempting to magic get a part of the response 131 | // should throw a ParserException 132 | $fixture->responseHeader; 133 | 134 | $this->fail("Expected Apache_Solr_ParserException was not raised"); 135 | } 136 | 137 | /** 138 | * @expectedException Apache_Solr_ParserException 139 | */ 140 | public function testMagicGetWith404Response() 141 | { 142 | $fixture = self::get404Response(); 143 | 144 | // attempting to magic get a part of the response 145 | // should throw a ParserException 146 | $fixture->responseHeader; 147 | 148 | $this->fail("Expected Apache_Solr_ParserException was not raised"); 149 | } 150 | 151 | public function testCreateDocuments() 152 | { 153 | $fixture = self::get200ResponseWithDocuments(); 154 | 155 | $this->assertTrue(count($fixture->response->docs) > 0, 'There are not 1 or more documents, cannot test'); 156 | $this->assertInstanceOf('Apache_Solr_Document', $fixture->response->docs[0], 'The first document is not of type Apache_Solr_Document'); 157 | } 158 | 159 | public function testDontCreateDocuments() 160 | { 161 | $fixture = self::get200ResponseWithDocuments(false); 162 | 163 | $this->assertTrue(count($fixture->response->docs) > 0, 'There are not 1 or more documents, cannot test'); 164 | $this->assertInstanceOf('stdClass', $fixture->response->docs[0], 'The first document is not of type stdClass'); 165 | } 166 | 167 | public function testGetHttpStatusMessage() 168 | { 169 | $fixture = self::get200Response(); 170 | 171 | $this->assertEquals("OK", $fixture->getHttpStatusMessage()); 172 | } 173 | 174 | public function testMagicGetReturnsNullForUndefinedData() 175 | { 176 | $fixture = self::get200Response(); 177 | 178 | $this->assertNull($fixture->doesnotexist); 179 | } 180 | 181 | public function testMagicIssetForDefinedProperty() 182 | { 183 | $fixture = self::get200Response(); 184 | 185 | $this->assertTrue(isset($fixture->responseHeader)); 186 | } 187 | 188 | public function testMagicIssetForUndefinedProperty() 189 | { 190 | $fixture = self::get200Response(); 191 | 192 | $this->assertFalse(isset($fixture->doesnotexist)); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /tests/Apache/Solr/Service/BalancerTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Apache_Solr_Service_Balancer Unit Tests 40 | */ 41 | class Apache_Solr_Service_BalancerTest extends Apache_Solr_ServiceAbstractTest 42 | { 43 | public function getFixture() 44 | { 45 | return new Apache_Solr_Service_Balancer(); 46 | } 47 | } -------------------------------------------------------------------------------- /tests/Apache/Solr/ServiceAbstractTest.php: -------------------------------------------------------------------------------- 1 | 36 | */ 37 | 38 | /** 39 | * Provides base funcationality test for both Apache_Solr_Service and the 40 | * Apache_Solr_Service_Balancer classes. 41 | */ 42 | abstract class Apache_Solr_ServiceAbstractTest extends PHPUnit_Framework_TestCase 43 | { 44 | /** 45 | * Method that gets the appropriate instance for testing 46 | */ 47 | abstract public function getFixture(); 48 | 49 | /** 50 | * @dataProvider testEscapeDataProvider 51 | */ 52 | public function testEscape($input, $expectedOutput) 53 | { 54 | $fixture = $this->getFixture(); 55 | 56 | $this->assertEquals($expectedOutput, $fixture->escape($input)); 57 | } 58 | 59 | public function testEscapeDataProvider() 60 | { 61 | return array( 62 | array( 63 | "I should look the same", 64 | "I should look the same" 65 | ), 66 | 67 | array( 68 | "(There) are: ^lots \\ && of spec!al charaters", 69 | "\\(There\\) are\\: \\^lots \\\\ \\&& of spec\\!al charaters" 70 | ) 71 | ); 72 | } 73 | 74 | /** 75 | * @dataProvider testEscapePhraseDataProvider 76 | */ 77 | public function testEscapePhrase($input, $expectedOutput) 78 | { 79 | $fixture = $this->getFixture(); 80 | 81 | $this->assertEquals($expectedOutput, $fixture->escapePhrase($input)); 82 | } 83 | 84 | public function testEscapePhraseDataProvider() 85 | { 86 | return array( 87 | array( 88 | "I'm a simple phrase", 89 | "I'm a simple phrase" 90 | ), 91 | 92 | array( 93 | "I have \"phrase\" characters", 94 | 'I have \\"phrase\\" characters' 95 | ) 96 | ); 97 | } 98 | 99 | /** 100 | * @dataProvider testPhraseDataProvider 101 | */ 102 | public function testPhrase($input, $expectedOutput) 103 | { 104 | $fixture = $this->getFixture(); 105 | 106 | $this->assertEquals($expectedOutput, $fixture->phrase($input)); 107 | } 108 | 109 | public function testPhraseDataProvider() 110 | { 111 | return array( 112 | array( 113 | "I'm a simple phrase", 114 | '"I\'m a simple phrase"' 115 | ), 116 | 117 | array( 118 | "I have \"phrase\" characters", 119 | '"I have \\"phrase\\" characters"' 120 | ) 121 | ); 122 | } 123 | 124 | public function testGetCreateDocumentWithDefaultConstructor() 125 | { 126 | $fixture = $this->getFixture(); 127 | 128 | $this->assertTrue($fixture->getCreateDocuments()); 129 | } 130 | 131 | public function testSetCreateDocuments() 132 | { 133 | $fixture = $this->getFixture(); 134 | 135 | $fixture->setCreateDocuments(false); 136 | 137 | $this->assertFalse($fixture->getCreateDocuments()); 138 | } 139 | } -------------------------------------------------------------------------------- /tests/README: -------------------------------------------------------------------------------- 1 | Use the run.php script included in this directory to run all unit tests for the 2 | Solr PHP Client library. Your system will require phpunit PEAR package - which 3 | you can get install instructions for at: 4 | 5 | http://www.phpunit.de/ 6 | 7 | To generate the code coverage report, you will also need the XDebug pecl package 8 | installed, typically this can be done with a simple: 9 | 10 | pecl install xdebug 11 | 12 | If you need more information on installation, then please see the official website: 13 | 14 | http://www.xdebug.org 15 | 16 | The scripts, configuration, and test files in this directory have been confirmed to 17 | work with the following versions: 18 | 19 | phpunit: 3.3.16 20 | xdebug: 2.0.4 -------------------------------------------------------------------------------- /tests/phpunit.bootstrap.inc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ../Apache 10 | 11 | ./run.php 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------