Passed
Pull Request — master (#1228)
by Timo
18:39
created

SolrService::_sendRawPost()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.7691

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 7
cts 11
cp 0.6364
rs 9.2
c 0
b 0
f 0
cc 4
eloc 11
nc 4
nop 4
crap 4.7691
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
28
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
29
use ApacheSolrForTypo3\Solr\System\Solr\Parser\SchemaParser;
30
use ApacheSolrForTypo3\Solr\System\Solr\Parser\StopWordParser;
31
use ApacheSolrForTypo3\Solr\System\Solr\Parser\SynonymParser;
32
use ApacheSolrForTypo3\Solr\System\Solr\Schema\Schema;
33
use TYPO3\CMS\Core\Utility\GeneralUtility;
34
use Apache_Solr_HttpTransportException;
35
36
/**
37
 * Solr Service Access
38
 *
39
 * @author Ingo Renner <[email protected]>
40
 */
41
class SolrService extends \Apache_Solr_Service
42
{
43
    const LUKE_SERVLET = 'admin/luke';
44
    const SYSTEM_SERVLET = 'admin/system';
45
    const PLUGINS_SERVLET = 'admin/plugins';
46
    const CORES_SERVLET = 'admin/cores';
47
    const SCHEMA_SERVLET = 'schema';
48
    const SYNONYMS_SERVLET = 'schema/analysis/synonyms/';
49
    const STOPWORDS_SERVLET = 'schema/analysis/stopwords/';
50
51
    const SCHEME_HTTP = 'http';
52
    const SCHEME_HTTPS = 'https';
53
54
    /**
55
     * Server connection scheme. http or https.
56
     *
57
     * @var string
58
     */
59
    protected $_scheme = self::SCHEME_HTTP;
60
61
    /**
62
     * Constructed servlet URL for Luke
63
     *
64
     * @var string
65
     */
66
    protected $_lukeUrl;
67
68
    /**
69
     * Constructed servlet URL for plugin information
70
     *
71
     * @var string
72
     */
73
    protected $_pluginsUrl;
74
75
    /**
76
     * @var string
77
     */
78
    protected $_coresUrl;
79
80
    /**
81
     * @var string
82
     */
83
    protected $_extractUrl;
84
85
    /**
86
     * @var string
87
     */
88
    protected $_synonymsUrl;
89
90
    /**
91
     * @var string
92
     */
93
    protected $_stopWordsUrl;
94
95
    /**
96
     * @var string
97
     */
98
    protected $_schemaUrl;
99
100
    /**
101
     * @var bool
102
     */
103
    protected $debug = false;
104
105
    /**
106
     * @var \Apache_Solr_Response
107
     */
108
    protected $responseCache = null;
109
110
    /**
111
     * @var bool
112
     */
113
    protected $hasSearched = false;
114
115
    /**
116
     * @var array
117
     */
118
    protected $lukeData = [];
119
120
    protected $systemData = null;
121
    protected $pluginsData = null;
122
123
    protected $solrconfigName = null;
124
125
    /**
126
     * @var TypoScriptConfiguration
127
     */
128
    protected $configuration;
129
130
    /**
131
     * @var array
132
     */
133
    protected static $pingCache = [];
134
135
    /**
136
     * @var SynonymParser
137
     */
138
    protected $synonymParser = null;
139
140
    /**
141
     * @var StopWordParser
142
     */
143
    protected $stopWordParser = null;
144
145
    /**
146
     * @var SchemaParser
147
     */
148
    protected $schemaParser = null;
149
150
    /**
151
     * @var Schema
152
     */
153
    protected $schema;
154
155
    /**
156
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
157
     */
158
    protected $logger = null;
159
160
    /**
161
     * Constructor
162
     *
163
     * @param string $host Solr host
164
     * @param string $port Solr port
165
     * @param string $path Solr path
166
     * @param string $scheme Scheme, defaults to http, can be https
167
     * @param TypoScriptConfiguration $typoScriptConfiguration
168
     * @param SynonymParser $synonymParser
169
     * @param StopWordParser $stopWordParser
170
     * @param SchemaParser $schemaParser
171
     */
172 94
    public function __construct(
173
        $host = '',
174
        $port = '8983',
175
        $path = '/solr/',
176
        $scheme = 'http',
177
        TypoScriptConfiguration $typoScriptConfiguration = null,
178
        SynonymParser $synonymParser = null,
179
        StopWordParser $stopWordParser = null,
180
        SchemaParser $schemaParser = null
181
    ) {
182 94
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
183
184 94
        $this->setScheme($scheme);
185 93
        $this->configuration = is_null($typoScriptConfiguration) ? Util::getSolrConfiguration() : $typoScriptConfiguration;
186 93
        $this->synonymParser = is_null($synonymParser) ? GeneralUtility::makeInstance(SynonymParser::class) : $synonymParser;
187 93
        $this->stopWordParser = is_null($stopWordParser) ? GeneralUtility::makeInstance(StopWordParser::class) : $stopWordParser;
188 93
        $this->schemaParser = is_null($schemaParser) ? GeneralUtility::makeInstance(SchemaParser::class) : $schemaParser;
189
190 93
        $this->initializeTimeoutFromConfiguration();
191
192 93
        parent::__construct($host, $port, $path);
193 93
    }
194
195
    /**
196
     * Initializes the timeout from TypoScript when configuration is present.
197
     *
198
     * @return void
199
     */
200 93
    protected function initializeTimeoutFromConfiguration()
201
    {
202 93
        $timeout = $this->configuration->getSolrTimeout();
203 93
        if ($timeout > 0) {
204 1
            $this->getHttpTransport()->setDefaultTimeout($timeout);
205 1
        }
206 93
    }
207
208
    /**
209
     * Creates a string representation of the Solr connection. Specifically
210
     * will return the Solr URL.
211
     *
212
     * @return string The Solr URL.
213
     */
214 56
    public function __toString()
215
    {
216 56
        return $this->_scheme . '://' . $this->_host . ':' . $this->_port . $this->_path;
217
    }
218
219
    /**
220
     * Returns the current time in milliseconds.
221
     *
222
     * @return double
223
     */
224 3
    protected function getMilliseconds()
225
    {
226 3
        return GeneralUtility::milliseconds();
227
    }
228
229
    /**
230
     * Performs a search.
231
     *
232
     * @param string $query query string / search term
233
     * @param int $offset result offset for pagination
234
     * @param int $limit number of results to retrieve
235
     * @param array $params additional HTTP GET parameters
236
     * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST)
237
     * @return \Apache_Solr_Response Solr response
238
     * @throws \RuntimeException if Solr returns a HTTP status code other than 200
239
     */
240 27
    public function search($query, $offset = 0, $limit = 10, $params = [], $method = self::METHOD_GET)
241
    {
242 27
        $response = parent::search($query, $offset, $limit, $params, $method);
243 27
        $this->hasSearched = true;
244
245 27
        $this->responseCache = $response;
246
247 27
        if ($response->getHttpStatus() != 200) {
248 1
            throw new \RuntimeException(
249
                'Invalid query. Solr returned an error: '
250 1
                . $response->getHttpStatus() . ' '
251 1
                . $response->getHttpStatusMessage(),
252
                1293109870
253 1
            );
254
        }
255
256 26
        return $response;
257
    }
258
259
    /**
260
     * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
261
     * server is available.
262
     *
263
     * Simply overrides the SolrPhpClient implementation, changing ping from a
264
     * HEAD to a GET request, see http://forge.typo3.org/issues/44167
265
     *
266
     * Also does not report the time, see https://forge.typo3.org/issues/64551
267
     *
268
     * @param int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
269
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
270
     * @return bool TRUE if Solr can be reached, FALSE if not
271
     */
272 51
    public function ping($timeout = 2, $useCache = true)
273
    {
274 51
        $httpResponse = $this->performPingRequest($timeout, $useCache);
275 51
        return ($httpResponse->getStatusCode() === 200);
276
    }
277
278
    /**
279
     * Call the /admin/ping servlet, can be used to get the runtime of a ping request.
280
     *
281
     * @param int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
282
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
283
     * @return double runtime in milliseconds
284
     * @throws \ApacheSolrForTypo3\Solr\PingFailedException
285
     */
286 3
    public function getPingRoundTripRuntime($timeout = 2, $useCache = true)
287
    {
288 3
        $start = $this->getMilliseconds();
289 3
        $httpResponse = $this->performPingRequest($timeout, $useCache);
290 3
        $end = $this->getMilliseconds();
291
292 3
        if ($httpResponse->getStatusCode() !== 200) {
293 1
            $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getStatusCode();
294
            /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */
295 1
            $exception = GeneralUtility::makeInstance(PingFailedException::class, $message);
296 1
            $exception->setHttpResponse($httpResponse);
297 1
            throw $exception;
298
        }
299
300 2
        return $end - $start;
301
    }
302
303
    /**
304
     * Performs a ping request and returns the result.
305
     *
306
     * @param int $timeout
307
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
308
     * @return \Apache_Solr_HttpTransport_Response
309
     */
310 54
    protected function performPingRequest($timeout = 2, $useCache = true)
311
    {
312 54
        $cacheKey = (string)($this);
313 54
        if ($useCache && isset(static::$pingCache[$cacheKey])) {
314 45
            return static::$pingCache[$cacheKey];
315
        }
316
317 54
        $pingResult = $this->getHttpTransport()->performGetRequest($this->_pingUrl, $timeout);
318
319 54
        if ($useCache) {
320 53
            static::$pingCache[$cacheKey] = $pingResult;
321 53
        }
322
323 54
        return $pingResult;
324
    }
325
326
    /**
327
     * Performs a content and meta data extraction request.
328
     *
329
     * @param ExtractingQuery $query An extraction query
330
     * @return array An array containing the extracted content [0] and meta data [1]
331
     */
332 1
    public function extractByQuery(ExtractingQuery $query)
333
    {
334
        $headers = [
335 1
            'Content-Type' => 'multipart/form-data; boundary=' . $query->getMultiPartPostDataBoundary()
336 1
        ];
337
338
        try {
339 1
            $response = $this->requestServlet(
340 1
                self::EXTRACT_SERVLET,
341 1
                $query->getQueryParameters(),
342 1
                'POST',
343 1
                $headers,
344 1
                $query->getRawPostFileData()
345 1
            );
346 1
        } catch (\Exception $e) {
347
            $this->logger->log(
348
                SolrLogManager::ERROR,
349
                'Extracting text and meta data through Solr Cell over HTTP POST',
350
                [
351
                    'query' => (array)$query,
352
                    'parameters' => $query->getQueryParameters(),
353
                    'file' => $query->getFile(),
354
                    'headers' => $headers,
355
                    'query url' => self::EXTRACT_SERVLET,
356
                    'exception' => $e->getMessage()
357
                ]
358
            );
359
        }
360
361
        return [
362 1
            $response->extracted,
363 1
            (array)$response->extracted_metadata
364 1
        ];
365
    }
366
367
    /**
368
     * Make a request to a servlet (a path) that's not a standard path.
369
     *
370
     * @param string $servlet Path to be added to the base Solr path.
371
     * @param array $parameters Optional, additional request parameters when constructing the URL.
372
     * @param string $method HTTP method to use, defaults to GET.
373
     * @param array $requestHeaders Key value pairs of header names and values. Should include 'Content-Type' for POST and PUT.
374
     * @param string $rawPost Must be an empty string unless method is POST or PUT.
375
     * @param float|bool $timeout Read timeout in seconds, defaults to FALSE.
376
     * @return \Apache_Solr_Response Response object
377
     * @throws \Apache_Solr_HttpTransportException if returned HTTP status is other than 200
378
     */
379 1
    public function requestServlet($servlet, $parameters = [], $method = 'GET', $requestHeaders = [], $rawPost = '', $timeout = false)
380
    {
381
        // Add default parameters
382 1
        $parameters['wt'] = self::SOLR_WRITER;
383 1
        $parameters['json.nl'] = $this->_namedListTreatment;
384 1
        $url = $this->_constructUrl($servlet, $parameters);
385
386 1
        $httpResponse = $this->getResponseFromTransport($url, $method, $requestHeaders, $rawPost, $timeout);
387 1
        $solrResponse = new \Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays);
388 1
        if ($solrResponse->getHttpStatus() != 200) {
389
            throw new \Apache_Solr_HttpTransportException($solrResponse);
390
        }
391
392 1
        return $solrResponse;
393
    }
394
395
    /**
396
     * Decides which transport method to used, depending on the request method and retrieves the response.
397
     *
398
     * @param string $url
399
     * @param string $method
400
     * @param array $requestHeaders
401
     * @param string $rawPost
402
     * @param float|bool $timeout
403
     * @return \Apache_Solr_HttpTransport_Response
404
     */
405 1
    protected function getResponseFromTransport($url, $method, $requestHeaders, $rawPost, $timeout)
406
    {
407 1
        $httpTransport = $this->getHttpTransport();
408
409 1
        if ($method == self::METHOD_GET) {
410
            return $httpTransport->performGetRequest($url, $timeout);
411
        }
412 1
        if ($method == self::METHOD_POST) {
413
            // FIXME should respect all headers, not only Content-Type
414 1
            return $httpTransport->performPostRequest($url, $rawPost, $requestHeaders['Content-Type'], $timeout);
415
        }
416
417
        throw new \InvalidArgumentException('$method should be GET or POST');
418
    }
419
420
    /**
421
     * Return a valid http URL given this server's scheme, host, port, and path
422
     * and a provided servlet name.
423
     *
424
     * @param string $servlet Servlet name
425
     * @param array $params Additional URL parameters to attach to the end of the URL
426
     * @return string Servlet URL
427
     */
428 93
    protected function _constructUrl($servlet, $params = [])
429
    {
430 93
        $url = parent::_constructUrl($servlet, $params);
431
432 93
        if (!GeneralUtility::isFirstPartOfStr($url, $this->_scheme)) {
433 2
            $parsedUrl = parse_url($url);
434
435
            // unfortunately can't use str_replace as it replace all
436
            // occurrences of $needle and can't be limited to replace only once
437 2
            $url = $this->_scheme . substr($url, strlen($parsedUrl['scheme']));
438 2
        }
439
440 93
        return $url;
441
    }
442
443
    /**
444
     * Returns the set scheme
445
     *
446
     * @return string
447
     */
448 3
    public function getScheme()
449
    {
450 3
        return $this->_scheme;
451
    }
452
453
    /**
454
     * Set the scheme used. If empty will fallback to constants
455
     *
456
     * @param string $scheme Either http or https
457
     * @throws \UnexpectedValueException
458
     */
459 94
    public function setScheme($scheme)
460
    {
461
        // Use the provided scheme or use the default
462 94
        if (empty($scheme)) {
463 2
            throw new \UnexpectedValueException('Scheme parameter is empty', 1380756390);
464
        }
465
466 93
        $isHttpOrHttps = in_array($scheme, [self::SCHEME_HTTP, self::SCHEME_HTTPS]);
467 93
        if (!$isHttpOrHttps) {
468 1
            throw new \UnexpectedValueException('Unsupported scheme parameter, scheme must be http or https', 1380756442);
469
        }
470
471
        // we have a valid scheme
472 93
        $this->_scheme = $scheme;
473
474 93
        if ($this->_urlsInited) {
475 1
            $this->_initUrls();
476 1
        }
477 93
    }
478
479
    /**
480
     * get field meta data for the index
481
     *
482
     * @param int $numberOfTerms Number of top terms to fetch for each field
483
     * @return array
484
     */
485
    public function getFieldsMetaData($numberOfTerms = 0)
486
    {
487
        return $this->getLukeMetaData($numberOfTerms)->fields;
488
    }
489
490
    /**
491
     * Retrieves meta data about the index from the luke request handler
492
     *
493
     * @param int $numberOfTerms Number of top terms to fetch for each field
494
     * @return \Apache_Solr_Response Index meta data
495
     */
496
    public function getLukeMetaData($numberOfTerms = 0)
497
    {
498
        if (!isset($this->lukeData[$numberOfTerms])) {
499
            $lukeUrl = $this->_constructUrl(
500
                self::LUKE_SERVLET,
501
                [
502
                    'numTerms' => $numberOfTerms,
503
                    'wt' => self::SOLR_WRITER,
504
                    'fl' => '*'
505
                ]
506
            );
507
508
            $this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl);
509
        }
510
511
        return $this->lukeData[$numberOfTerms];
512
    }
513
514
    /**
515
     * Central method for making a get operation against this Solr Server
516
     *
517
     * @param string $url
518
     * @param float|bool $timeout Read timeout in seconds
519
     * @return \Apache_Solr_Response
520
     */
521 41
    protected function _sendRawGet($url, $timeout = false)
522
    {
523 41
        $logSeverity = SolrLogManager::INFO;
524 41
        $exception = null;
525
526
        try {
527 41
            $response = parent::_sendRawGet($url, $timeout);
528 41
        } catch (\Apache_Solr_HttpTransportException $exception) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
529 4
            $logSeverity = SolrLogManager::ERROR;
530 4
            $response = $exception->getResponse();
531
        }
532
533 41
        if ($this->configuration->getLoggingQueryRawGet() || $response->getHttpStatus() != 200) {
534 4
            $this->writeLog($logSeverity, 'Querying Solr using GET', $url, $response, $exception);
535 4
        }
536
537 41
        return $response;
538
    }
539
540
    /**
541
     * Returns whether a search has been executed or not.
542
     *
543
     * @return bool TRUE if a search has been executed, FALSE otherwise
544
     */
545
    public function hasSearched()
546
    {
547
        return $this->hasSearched;
548
    }
549
550
    /**
551
     * Gets the most recent response (if any)
552
     *
553
     * @return \Apache_Solr_Response Most recent response, or NULL if a search has not been executed yet.
554
     */
555 1
    public function getResponse()
556
    {
557 1
        return $this->responseCache;
558
    }
559
560
    /**
561
     * Enable/Disable debug mode
562
     *
563
     * @param bool $debug TRUE to enable debug mode, FALSE to turn off, off by default
564
     */
565
    public function setDebug($debug)
566
    {
567
        $this->debug = (boolean)$debug;
568
    }
569
570
    /**
571
     * Gets information about the plugins installed in Solr
572
     *
573
     * @return array A nested array of plugin data.
574
     */
575 3
    public function getPluginsInformation()
576
    {
577 3
        if (empty($this->pluginsData)) {
578 3
            $pluginsInformation = $this->_sendRawGet($this->_pluginsUrl);
579
580
            // access a random property to trigger response parsing
581 3
            $pluginsInformation->responseHeader;
582 2
            $this->pluginsData = $pluginsInformation;
583 2
        }
584
585 2
        return $this->pluginsData;
586
    }
587
588
    /**
589
     * Gets the name of the schema.xml file installed and in use on the Solr
590
     * server.
591
     *
592
     * @deprecated use getSchema()->getName() instead will be removed in 7.0
593
     * @return string Name of the active schema.xml
594
     */
595
    public function getSchemaName()
596
    {
597
        GeneralUtility::logDeprecatedFunction();
598
        return $this->getSchema()->getName();
599
    }
600
601
    /**
602
     * Gets information about the Solr server
603
     *
604
     * @return array A nested array of system data.
605
     */
606 5
    public function getSystemInformation()
607
    {
608 5
        if (empty($this->systemData)) {
609 5
            $systemInformation = $this->system();
610
611
            // access a random property to trigger response parsing
612 5
            $systemInformation->responseHeader;
613 4
            $this->systemData = $systemInformation;
614 4
        }
615
616 4
        return $this->systemData;
617
    }
618
619
    /**
620
     * Gets the name of the solrconfig.xml file installed and in use on the Solr
621
     * server.
622
     *
623
     * @return string Name of the active solrconfig.xml
624
     */
625 3
    public function getSolrconfigName()
626
    {
627 3
        if (is_null($this->solrconfigName)) {
628 3
            $solrconfigXmlUrl = $this->_scheme . '://'
629 3
                . $this->_host . ':' . $this->_port
630 3
                . $this->_path . 'admin/file/?file=solrconfig.xml';
631 3
            $response = $this->_sendRawGet($solrconfigXmlUrl);
632
633 3
            $solrconfigXml = simplexml_load_string($response->getRawResponse());
634 3
            if ($solrconfigXml === false) {
635 1
                throw new \InvalidArgumentException('No valid xml response from schema file: ' . $solrconfigXmlUrl);
636
            }
637 2
            $this->solrconfigName = (string)$solrconfigXml->attributes()->name;
638 2
        }
639
640 2
        return $this->solrconfigName;
641
    }
642
643
    /**
644
     * Gets the Solr server's version number.
645
     *
646
     * @return string Solr version number
647
     */
648 4
    public function getSolrServerVersion()
649
    {
650 4
        $systemInformation = $this->getSystemInformation();
651
652
        // don't know why $systemInformation->lucene->solr-spec-version won't work
653 3
        $luceneInformation = (array)$systemInformation->lucene;
654 3
        return $luceneInformation['solr-spec-version'];
655
    }
656
657
    /**
658
     * Deletes all index documents of a certain type and does a commit
659
     * afterwards.
660
     *
661
     * @param string $type The type of documents to delete, usually a table name.
662
     * @param bool $commit Will commit immediately after deleting the documents if set, defaults to TRUE
663
     */
664
    public function deleteByType($type, $commit = true)
665
    {
666
        $this->deleteByQuery('type:' . trim($type));
667
668
        if ($commit) {
669
            $this->commit(false, false, false);
670
        }
671
    }
672
673
    /**
674
     * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
675
     * a complete and well formed "delete" xml document
676
     *
677
     * @param string $rawPost Expected to be utf-8 encoded xml document
678
     * @param float|int $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
679
     * @return \Apache_Solr_Response
680
     */
681 11
    public function delete($rawPost, $timeout = 3600)
682
    {
683 11
        $response = $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
684
685 11
        $this->logger->log(
686 11
            SolrLogManager::NOTICE,
687 11
            'Delete Query sent.',
688
            [
689 11
                'query' => $rawPost,
690 11
                'query url' => $this->_updateUrl,
691
                'response' => (array)$response
692 11
            ]
693 11
        );
694
695 11
        return $response;
696
    }
697
698
    /**
699
     * Central method for making a post operation against this Solr Server
700
     *
701
     * @param string $url
702
     * @param string $rawPost
703
     * @param float|bool $timeout Read timeout in seconds
704
     * @param string $contentType
705
     * @return \Apache_Solr_Response
706
     */
707 65
    protected function _sendRawPost($url, $rawPost, $timeout = false, $contentType = 'text/xml; charset=UTF-8')
708
    {
709 65
        $logSeverity = SolrLogManager::INFO;
710 65
        $exception = null;
711
712
        try {
713 65
            $response = parent::_sendRawPost($url, $rawPost, $timeout, $contentType);
714 65
        } catch (\Apache_Solr_HttpTransportException $exception) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
715
            $logSeverity = SolrLogManager::ERROR;
716
            $response = $exception->getResponse();
717
        }
718
719 65
        if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) {
720
            $this->writeLog($logSeverity, 'Querying Solr using POST', $url, $response, $exception, $rawPost);
721
        }
722
723 65
        return $response;
724
    }
725
726
    /**
727
     * Get currently configured synonyms
728
     *
729
     * @param string $baseWord If given a base word, retrieves the synonyms for that word only
730
     * @return array
731
     */
732 2
    public function getSynonyms($baseWord = '')
733
    {
734 2
        $this->initializeSynonymsUrl();
735 2
        $synonymsUrl = $this->_synonymsUrl;
736 2
        if (!empty($baseWord)) {
737 2
            $synonymsUrl .= '/' . $baseWord;
738 2
        }
739
740 2
        $response = $this->_sendRawGet($synonymsUrl);
741 2
        return $this->synonymParser->parseJson($baseWord, $response->getRawResponse());
742
    }
743
744
    /**
745
     * Add list of synonyms for base word to managed synonyms map
746
     *
747
     * @param string $baseWord
748
     * @param array $synonyms
749
     *
750
     * @return \Apache_Solr_Response
751
     *
752
     * @throws \Apache_Solr_InvalidArgumentException If $baseWord or $synonyms are empty
753
     */
754 2
    public function addSynonym($baseWord, array $synonyms)
755
    {
756 2
        $this->initializeSynonymsUrl();
757 2
        $json = $this->synonymParser->toJson($baseWord, $synonyms);
758 2
        return $this->_sendRawPost($this->_synonymsUrl, $json,
759 2
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
760
    }
761
762
    /**
763
     * Remove a synonym from the synonyms map
764
     *
765
     * @param string $baseWord
766
     * @return \Apache_Solr_Response
767
     * @throws \Apache_Solr_InvalidArgumentException
768
     */
769 2
    public function deleteSynonym($baseWord)
770
    {
771 2
        $this->initializeSynonymsUrl();
772 2
        if (empty($baseWord)) {
773
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word.');
774
        }
775
776 2
        return $this->_sendRawDelete($this->_synonymsUrl . '/' . urlencode($baseWord));
777
    }
778
779
    /**
780
     * Central method for making a HTTP DELETE operation against the Solr server
781
     *
782
     * @param string $url
783
     * @param bool|float $timeout Read timeout in seconds
784
     * @return \Apache_Solr_Response
785
     */
786 4
    protected function _sendRawDelete($url, $timeout = false)
787
    {
788 4
        $logSeverity = SolrLogManager::INFO;
789 4
        $exception = null;
790
791
        try {
792 4
            $httpTransport = $this->getHttpTransport();
793 4
            $httpResponse = $httpTransport->performDeleteRequest($url, $timeout);
794 4
            $solrResponse = new \Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays);
795
796 4
            if ($solrResponse->getHttpStatus() != 200) {
797 4
                throw new \Apache_Solr_HttpTransportException($solrResponse);
798
            }
799 4
        } catch (\Apache_Solr_HttpTransportException $exception) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
800 4
            $logSeverity = SolrLogManager::ERROR;
801 4
            $solrResponse = $exception->getResponse();
802
        }
803
804 4
        if ($this->configuration->getLoggingQueryRawDelete() || $solrResponse->getHttpStatus() != 200) {
805 4
            $this->writeLog($logSeverity, 'Querying Solr using DELETE', $url, $solrResponse, $exception);
806 4
        }
807
808 4
        return $solrResponse;
809
    }
810
811
    /**
812
     * Build the log data and writes the message to the log
813
     *
814
     * @param integer $logSeverity
815
     * @param string $message
816
     * @param string $url
817
     * @param \Apache_Solr_Response $solrResponse
818
     * @param \Exception $exception
819
     * @param string $contentSend
820
     */
821 6
    protected function writeLog($logSeverity, $message, $url, $solrResponse, $exception = null, $contentSend = '')
822
    {
823 6
        $logData = $this->buildLogDataFromResponse($solrResponse, $exception, $url, $contentSend);
824 6
        $this->logger->log($logSeverity, $message, $logData);
825 6
    }
826
827
    /**
828
     * Parses the solr information to build data for the logger.
829
     *
830
     * @param \Apache_Solr_Response $solrResponse
831
     * @param \Exception $e
832
     * @param string $url
833
     * @param string $contentSend
834
     * @return array
835
     */
836 6
    protected function buildLogDataFromResponse(\Apache_Solr_Response $solrResponse, \Exception $e = null, $url = '', $contentSend = '')
837
    {
838 6
        $logData = ['query url' => $url, 'response' => (array)$solrResponse];
839
840 6
        if ($contentSend !== '') {
841
            $logData['content'] = $contentSend;
842
        }
843
844 6
        if (!empty($e)) {
845 6
            $logData['exception'] = $e->__toString();
846 6
            return $logData;
847
        } else {
848
            // trigger data parsing
849
            $solrResponse->response;
850
            $logData['response data'] = print_r($solrResponse, true);
851
            return $logData;
852
        }
853
    }
854
855
    /**
856
     * Get currently configured stop words
857
     *
858
     * @return array
859
     */
860 3
    public function getStopWords()
861
    {
862 3
        $this->initializeStopWordsUrl();
863 3
        $response = $this->_sendRawGet($this->_stopWordsUrl);
864 3
        return $this->stopWordParser->parseJson($response->getRawResponse());
865
    }
866
867
    /**
868
     * Adds stop words to the managed stop word list
869
     *
870
     * @param array|string $stopWords string for a single word, array for multiple words
871
     * @return \Apache_Solr_Response
872
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
873
     */
874 2
    public function addStopWords($stopWords)
875
    {
876 2
        $this->initializeStopWordsUrl();
877 2
        $json = $this->stopWordParser->toJson($stopWords);
878 2
        return $this->_sendRawPost($this->_stopWordsUrl, $json,
879 2
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
880
    }
881
882
    /**
883
     * Deletes a words from the managed stop word list
884
     *
885
     * @param string $stopWord stop word to delete
886
     * @return \Apache_Solr_Response
887
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
888
     */
889 2
    public function deleteStopWord($stopWord)
890
    {
891 2
        $this->initializeStopWordsUrl();
892 2
        if (empty($stopWord)) {
893
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
894
        }
895
896 2
        return $this->_sendRawDelete($this->_stopWordsUrl . '/' . urlencode($stopWord));
897
    }
898
899
    /**
900
     * Returns the core name from the configured path.
901
     *
902
     * @return string
903
     */
904 3
    public function getCoreName()
905
    {
906 3
        $paths = explode('/', trim($this->_path, '/'));
907
908 3
        return (string)array_pop($paths);
909
    }
910
911
    /**
912
     * Returns the core name from the configured path without the core name.
913
     *
914
     * @return string
915
     */
916 93
    public function getCoreBasePath()
917
    {
918 93
        $pathWithoutLeadingAndTrailingSlashes = trim(trim($this->_path), "/");
919 93
        $pathWithoutLastSegment = substr($pathWithoutLeadingAndTrailingSlashes, 0, strrpos($pathWithoutLeadingAndTrailingSlashes, "/"));
920 93
        return '/' . $pathWithoutLastSegment . '/';
921
    }
922
923
    /**
924
     * Reloads the current core
925
     *
926
     * @return \Apache_Solr_Response
927
     */
928 1
    public function reloadCore()
929
    {
930 1
        return $this->reloadCoreByName($this->getCoreName());
931
    }
932
933
    /**
934
     * Reloads a core of the connection by a given corename.
935
     *
936
     * @param string $coreName
937
     * @return \Apache_Solr_Response
938
     */
939 1
    public function reloadCoreByName($coreName)
940
    {
941 1
        $coreAdminReloadUrl = $this->_coresUrl . '?action=reload&core=' . $coreName;
942 1
        return $this->_sendRawGet($coreAdminReloadUrl);
943
    }
944
945
    /**
946
     * initializes various URLs, including the Luke URL
947
     *
948
     * @return void
949
     */
950 93
    protected function _initUrls()
951
    {
952 93
        parent::_initUrls();
953
954 93
        $this->_lukeUrl = $this->_constructUrl(
955 93
            self::LUKE_SERVLET,
956
            [
957 93
                'numTerms' => '0',
958
                'wt' => self::SOLR_WRITER
959 93
            ]
960 93
        );
961
962 93
        $this->_pluginsUrl = $this->_constructUrl(
963 93
            self::PLUGINS_SERVLET,
964 93
            ['wt' => self::SOLR_WRITER]
965 93
        );
966
967 93
        $this->_coresUrl =
968 93
            $this->_scheme . '://' .
969 93
            $this->_host . ':' .
970 93
            $this->_port .
971 93
            $this->getCoreBasePath() .
972 93
            self::CORES_SERVLET;
973
974 93
        $this->_schemaUrl = $this->_constructUrl(self::SCHEMA_SERVLET);
975 93
    }
976
977
    /**
978
     * @return void
979
     */
980 2
    protected function initializeSynonymsUrl()
981
    {
982 2
        if (trim($this->_synonymsUrl) !== '') {
983 2
            return;
984
        }
985 2
        $this->_synonymsUrl = $this->_constructUrl(self::SYNONYMS_SERVLET) . $this->getSchema()->getLanguage();
986 2
    }
987
988
    /**
989
     * @return void
990
     */
991 3
    protected function initializeStopWordsUrl()
992
    {
993 3
        if (trim($this->_stopWordsUrl) !== '') {
994 2
            return;
995
        }
996
997 3
        $this->_stopWordsUrl = $this->_constructUrl(self::STOPWORDS_SERVLET) . $this->getSchema()->getLanguage();
998 3
    }
999
1000
    /**
1001
     * Get the configured schema for the current core.
1002
     *
1003
     * @return Schema
1004
     */
1005 8
    public function getSchema()
1006
    {
1007 8
        if ($this->schema !== null) {
1008
            return $this->schema;
1009
        }
1010 8
        $response = $this->_sendRawGet($this->_schemaUrl);
1011 8
        $this->schema = $this->schemaParser->parseJson($response->getRawResponse());
1012 8
        return $this->schema;
1013
    }
1014
}
1015