Passed
Push — master ( 04715c...48f77b )
by Timo
47s
created

SolrService::reloadCore()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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