Completed
Pull Request — master (#797)
by Timo
14:24
created

SolrService::_constructUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
crap 2
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\Service\StopWordParser;
29
use ApacheSolrForTypo3\Solr\System\Solr\Service\SynonymParser;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32
/**
33
 * Solr Service Access
34
 *
35
 * @author Ingo Renner <[email protected]>
36
 */
37
class SolrService extends \Apache_Solr_Service
38
{
39
    const LUKE_SERVLET = 'admin/luke';
40
    const SYSTEM_SERVLET = 'admin/system';
41
    const PLUGINS_SERVLET = 'admin/plugins';
42
    const CORES_SERVLET = 'admin/cores';
43
    const SCHEMA_SERVLET = 'schema';
44
    const SYNONYMS_SERVLET = 'schema/analysis/synonyms/';
45
    const STOPWORDS_SERVLET = 'schema/analysis/stopwords/';
46
47
    const SCHEME_HTTP = 'http';
48
    const SCHEME_HTTPS = 'https';
49
50
    /**
51
     * Server connection scheme. http or https.
52
     *
53
     * @var string
54
     */
55
    protected $_scheme = self::SCHEME_HTTP;
56
57
    /**
58
     * Constructed servlet URL for Luke
59
     *
60
     * @var string
61
     */
62
    protected $_lukeUrl;
63
64
    /**
65
     * Constructed servlet URL for plugin information
66
     *
67
     * @var string
68
     */
69
    protected $_pluginsUrl;
70
71
    /**
72
     * @var string
73
     */
74
    protected $_coresUrl;
75
76
    /**
77
     * @var string
78
     */
79
    protected $_extractUrl;
80
81
    /**
82
     * @var string
83
     */
84
    protected $_synonymsUrl;
85
86
    /**
87
     * @var string
88
     */
89
    protected $_stopWordsUrl;
90
91
    /**
92
     * @var string
93
     */
94
    protected $_schemaUrl;
95
96
    /**
97
     * @var bool
98
     */
99
    protected $debug = false;
100
101
    /**
102
     * @var \Apache_Solr_Response
103
     */
104
    protected $responseCache = null;
105
106
    /**
107
     * @var bool
108
     */
109
    protected $hasSearched = false;
110
111
    /**
112
     * @var array
113
     */
114
    protected $lukeData = [];
115
116
    protected $systemData = null;
117
    protected $pluginsData = null;
118
119
    protected $schemaName = null;
120
    protected $solrconfigName = null;
121
122
    /**
123
     * @var TypoScriptConfiguration
124
     */
125
    protected $configuration;
126
127
    /**
128
     * @var array
129
     */
130
    protected static $pingCache = [];
131
132
    /**
133
     * @var SynonymParser
134
     */
135
    protected $synonymParser = null;
136
137
    /**
138
     * @var StopWordParser
139
     */
140
    protected $stopWordParser = null;
141
142
    /**
143
     * @var string
144
     */
145
    protected $managedLanguage = '';
146
147
    /**
148
     * Constructor
149
     *
150
     * @param string $host Solr host
151
     * @param string $port Solr port
152
     * @param string $path Solr path
153
     * @param string $scheme Scheme, defaults to http, can be https
154
     * @param TypoScriptConfiguration $typoScriptConfiguration
155
     * @param SynonymParser $synonymParser
156
     * @param StopWordParser $stopWordParser
157
     */
158 61
    public function __construct(
159
        $host = '',
160
        $port = '8983',
161
        $path = '/solr/',
162
        $scheme = 'http',
163
        TypoScriptConfiguration $typoScriptConfiguration = null,
164
        SynonymParser $synonymParser = null,
165
        StopWordParser $stopWordParser = null
166
    ) {
167 61
        $this->setScheme($scheme);
168 60
        $this->configuration = is_null($typoScriptConfiguration) ? Util::getSolrConfiguration() : $typoScriptConfiguration;
169 60
        $this->synonymParser = is_null($synonymParser) ? GeneralUtility::makeInstance(SynonymParser::class) : $synonymParser;
170 60
        $this->stopWordParser = is_null($stopWordParser) ? GeneralUtility::makeInstance(StopWordParser::class) : $stopWordParser;
171
172 60
        parent::__construct($host, $port, $path);
173 60
    }
174
175
    /**
176
     * Creates a string representation of the Solr connection. Specifically
177
     * will return the Solr URL.
178
     *
179
     * @return string The Solr URL.
180
     */
181 40
    public function __toString()
182
    {
183 40
        return $this->_scheme . '://' . $this->_host . ':' . $this->_port . $this->_path;
184
    }
185
186
    /**
187
     * Returns the current time in milliseconds.
188
     *
189
     * @return double
190
     */
191 1
    protected function getMilliseconds()
192
    {
193 1
        return GeneralUtility::milliseconds();
194
    }
195
196
    /**
197
     * Performs a search.
198
     *
199
     * @param string $query query string / search term
200
     * @param int $offset result offset for pagination
201
     * @param int $limit number of results to retrieve
202
     * @param array $params additional HTTP GET parameters
203
     * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST)
204
     * @return \Apache_Solr_Response Solr response
205
     * @throws \RuntimeException if Solr returns a HTTP status code other than 200
206
     */
207 25
    public function search($query, $offset = 0, $limit = 10, $params = array(), $method = self::METHOD_GET)
208
    {
209 25
        $response = parent::search($query, $offset, $limit, $params, $method);
210 25
        $this->hasSearched = true;
211
212 25
        $this->responseCache = $response;
213
214 25
        if ($response->getHttpStatus() != 200) {
215
            throw new \RuntimeException(
216
                'Invalid query. Solr returned an error: '
217
                . $response->getHttpStatus() . ' '
218
                . $response->getHttpStatusMessage(),
219
                1293109870
220
            );
221
        }
222
223 25
        return $response;
224
    }
225
226
    /**
227
     * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
228
     * server is available.
229
     *
230
     * Simply overrides the SolrPhpClient implementation, changing ping from a
231
     * HEAD to a GET request, see http://forge.typo3.org/issues/44167
232
     *
233
     * Also does not report the time, see https://forge.typo3.org/issues/64551
234
     *
235
     * @param float|int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
236
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
237
     * @return bool TRUE if Solr can be reached, FALSE if not
238
     */
239 37
    public function ping($timeout = 2, $useCache = true)
240
    {
241 37
        $httpResponse = $this->performPingRequest($timeout, $useCache);
242 37
        return ($httpResponse->getStatusCode() === 200);
243
    }
244
245
    /**
246
     * Call the /admin/ping servlet, can be used to get the runtime of a ping request.
247
     *
248
     * @param float|int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
249
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
250
     * @return double runtime in milliseconds
251
     * @throws \ApacheSolrForTypo3\Solr\PingFailedException
252
     */
253 1
    public function getPingRoundTripRuntime($timeout = 2, $useCache = true)
254
    {
255 1
        $start = $this->getMilliseconds();
256 1
        $httpResponse = $this->performPingRequest($timeout, $useCache);
257 1
        $end = $this->getMilliseconds();
258
259 1
        if ($httpResponse->getStatusCode() !== 200) {
260
            $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getStatusCode();
261
            /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */
262
            $exception = GeneralUtility::makeInstance('ApacheSolrForTypo3\Solr\PingFailedException', $message);
263
            $exception->setHttpResponse($httpResponse);
264
            throw $exception;
265
        }
266
267 1
        return $end - $start;
268
    }
269
270
    /**
271
     * Performs a ping request and returns the result.
272
     *
273
     * @param int $timeout
274
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
275
     * @return \Apache_Solr_HttpTransport_Response
276
     */
277 38
    protected function performPingRequest($timeout = 2, $useCache = true)
278
    {
279 38
        $cacheKey = (string) ($this);
280 38
        if ($useCache && isset(static::$pingCache[$cacheKey])) {
281 32
            return static::$pingCache[$cacheKey];
282
        }
283
284 38
        $pingResult = $this->getHttpTransport()->performGetRequest($this->_pingUrl, $timeout);
285
286 38
        if ($useCache) {
287 37
            static::$pingCache[$cacheKey] = $pingResult;
288
        }
289
290 38
        return $pingResult;
291
    }
292
293
    /**
294
     * Performs a content and meta data extraction request.
295
     *
296
     * @param ExtractingQuery $query An extraction query
297
     * @return array An array containing the extracted content [0] and meta data [1]
298
     */
299 1
    public function extractByQuery(ExtractingQuery $query)
300
    {
301
        $headers = array(
302 1
            'Content-Type' => 'multipart/form-data; boundary=' . $query->getMultiPartPostDataBoundary()
303
        );
304
305
        try {
306 1
            $response = $this->requestServlet(
307 1
                self::EXTRACT_SERVLET,
308 1
                $query->getQueryParameters(),
309 1
                'POST',
310
                $headers,
311 1
                $query->getRawPostFileData()
312
            );
313
        } catch (\Exception $e) {
314
            GeneralUtility::devLog('Extracting text and meta data through Solr Cell over HTTP POST',
315
                'solr', 3, array(
316
                    'query' => (array)$query,
317
                    'parameters' => $query->getQueryParameters(),
318
                    'file' => $query->getFile(),
319
                    'headers' => $headers,
320
                    'query url' => self::EXTRACT_SERVLET,
321
                    'exception' => $e->getMessage()
322
                ));
323
        }
324
325
        return array(
326 1
            $response->extracted,
327 1
            (array)$response->extracted_metadata
328
        );
329
    }
330
331
    /**
332
     * Make a request to a servlet (a path) that's not a standard path.
333
     *
334
     * @param string $servlet Path to be added to the base Solr path.
335
     * @param array $parameters Optional, additional request parameters when constructing the URL.
336
     * @param string $method HTTP method to use, defaults to GET.
337
     * @param array $requestHeaders Key value pairs of header names and values. Should include 'Content-Type' for POST and PUT.
338
     * @param string $rawPost Must be an empty string unless method is POST or PUT.
339
     * @param float|bool $timeout Read timeout in seconds, defaults to FALSE.
340
     * @return \Apache_Solr_Response Response object
341
     * @throws \Apache_Solr_HttpTransportException if returned HTTP status is other than 200
342
     */
343 1
    public function requestServlet(
344
        $servlet,
345
        $parameters = array(),
346
        $method = 'GET',
347
        $requestHeaders = array(),
348
        $rawPost = '',
349
        $timeout = false
350
    ) {
351 1
        $httpTransport = $this->getHttpTransport();
352
353 1
        if ($method == 'GET' || $method == 'HEAD') {
354
            // Make sure we are not sending a request body.
355
            $rawPost = '';
356
        }
357
358
        // Add default parameters
359 1
        $parameters['wt'] = self::SOLR_WRITER;
360 1
        $parameters['json.nl'] = $this->_namedListTreatment;
361 1
        $url = $this->_constructUrl($servlet, $parameters);
362
363 1
        if ($method == self::METHOD_GET) {
364
            $httpResponse = $httpTransport->performGetRequest($url, $timeout);
365 1
        } elseif ($method == self::METHOD_POST) {
366
            // FIXME should respect all headers, not only Content-Type
367 1
            $httpResponse = $httpTransport->performPostRequest($url, $rawPost,
368 1
                $requestHeaders['Content-Type'], $timeout);
369
        }
370
371 1
        if (empty($httpResponse)) {
372
            throw new \InvalidArgumentException('$method should be GET or POST');
373
        }
374
375 1
        $solrResponse = new \Apache_Solr_Response($httpResponse,
376 1
            $this->_createDocuments, $this->_collapseSingleValueArrays);
377
378 1
        if ($solrResponse->getHttpStatus() != 200) {
379
            throw new \Apache_Solr_HttpTransportException($solrResponse);
380
        }
381
382 1
        return $solrResponse;
383
    }
384
385
    /**
386
     * Return a valid http URL given this server's scheme, host, port, and path
387
     * and a provided servlet name.
388
     *
389
     * @param string $servlet Servlet name
390
     * @param array $params Additional URL parameters to attach to the end of the URL
391
     * @return string Servlet URL
392
     */
393 60
    protected function _constructUrl($servlet, $params = array())
394
    {
395 60
        $url = parent::_constructUrl($servlet, $params);
396
397 60
        if (!GeneralUtility::isFirstPartOfStr($url, $this->_scheme)) {
398 1
            $parsedUrl = parse_url($url);
399
400
            // unfortunately can't use str_replace as it replace all
401
            // occurrences of $needle and can't be limited to replace only once
402 1
            $url = $this->_scheme . substr($url, strlen($parsedUrl['scheme']));
403
        }
404
405 60
        return $url;
406
    }
407
408
    /**
409
     * Returns the set scheme
410
     *
411
     * @return string
412
     */
413
    public function getScheme()
414
    {
415
        return $this->_scheme;
416
    }
417
418
    /**
419
     * Set the scheme used. If empty will fallback to constants
420
     *
421
     * @param string $scheme Either http or https
422
     * @throws \UnexpectedValueException
423
     */
424 61
    public function setScheme($scheme)
425
    {
426
        // Use the provided scheme or use the default
427 61
        if (empty($scheme)) {
428 1
            throw new \UnexpectedValueException('Scheme parameter is empty',
429 1
                1380756390);
430
        } else {
431 60
            if (in_array($scheme,
432 60
                array(self::SCHEME_HTTP, self::SCHEME_HTTPS))) {
433 60
                $this->_scheme = $scheme;
434
            } else {
435
                throw new \UnexpectedValueException('Unsupported scheme parameter, scheme must be http or https',
436
                    1380756442);
437
            }
438
        }
439
440 60
        if ($this->_urlsInited) {
441
            $this->_initUrls();
442
        }
443 60
    }
444
445
    /**
446
     * get field meta data for the index
447
     *
448
     * @param int $numberOfTerms Number of top terms to fetch for each field
449
     * @return array
450
     */
451
    public function getFieldsMetaData($numberOfTerms = 0)
452
    {
453
        return $this->getLukeMetaData($numberOfTerms)->fields;
454
    }
455
456
    /**
457
     * Retrieves meta data about the index from the luke request handler
458
     *
459
     * @param int $numberOfTerms Number of top terms to fetch for each field
460
     * @return \Apache_Solr_Response Index meta data
461
     */
462
    public function getLukeMetaData($numberOfTerms = 0)
463
    {
464
        if (!isset($this->lukeData[$numberOfTerms])) {
465
            $lukeUrl = $this->_constructUrl(
466
                self::LUKE_SERVLET,
467
                array(
468
                    'numTerms' => $numberOfTerms,
469
                    'wt' => self::SOLR_WRITER,
470
                    'fl' => '*'
471
                )
472
            );
473
474
            $this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl);
475
        }
476
477
        return $this->lukeData[$numberOfTerms];
478
    }
479
480
    /**
481
     * Central method for making a get operation against this Solr Server
482
     *
483
     * @param string $url
484
     * @param float|bool $timeout Read timeout in seconds
485
     * @return \Apache_Solr_Response
486
     */
487 31
    protected function _sendRawGet($url, $timeout = false)
488
    {
489 31
        $logSeverity = 0; // info
490
491
        try {
492 31
            $response = parent::_sendRawGet($url, $timeout);
493 1
        } catch (\Apache_Solr_HttpTransportException $e) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
494 1
            $logSeverity = 3; // fatal error
495 1
            $response = $e->getResponse();
496
        }
497
498 31
        if ($this->configuration->getLoggingQueryRawGet() || $response->getHttpStatus() != 200) {
499
            $logData = array(
500 1
                'query url' => $url,
501 1
                'response' => (array)$response
502
            );
503
504 1
            if (!empty($e)) {
505 1
                $logData['exception'] = $e->__toString();
506
            } else {
507
                // trigger data parsing
508
                $response->response;
509
                $logData['response data'] = print_r($response, true);
510
            }
511
512 1
            GeneralUtility::devLog('Querying Solr using GET', 'solr',
513
                $logSeverity, $logData);
514
        }
515
516 31
        return $response;
517
    }
518
519
    /**
520
     * Returns whether a search has been executed or not.
521
     *
522
     * @return bool TRUE if a search has been executed, FALSE otherwise
523
     */
524
    public function hasSearched()
525
    {
526
        return $this->hasSearched;
527
    }
528
529
    /**
530
     * Gets the most recent response (if any)
531
     *
532
     * @return \Apache_Solr_Response Most recent response, or NULL if a search has not been executed yet.
533
     */
534
    public function getResponse()
535
    {
536
        return $this->responseCache;
537
    }
538
539
    /**
540
     * Enable/Disable debug mode
541
     *
542
     * @param bool $debug TRUE to enable debug mode, FALSE to turn off, off by default
543
     */
544
    public function setDebug($debug)
545
    {
546
        $this->debug = (boolean)$debug;
547
    }
548
549
    /**
550
     * Gets information about the plugins installed in Solr
551
     *
552
     * @return array A nested array of plugin data.
553
     */
554
    public function getPluginsInformation()
555
    {
556
        if (empty($this->pluginsData)) {
557
            $pluginsInformation = $this->_sendRawGet($this->_pluginsUrl);
558
559
            // access a random property to trigger response parsing
560
            $pluginsInformation->responseHeader;
561
            $this->pluginsData = $pluginsInformation;
562
        }
563
564
        return $this->pluginsData;
565
    }
566
567
    /**
568
     * Gets the name of the schema.xml file installed and in use on the Solr
569
     * server.
570
     *
571
     * @return string Name of the active schema.xml
572
     */
573 1
    public function getSchemaName()
574
    {
575 1
        if (is_null($this->schemaName)) {
576 1
            $systemInformation = $this->getSystemInformation();
577 1
            $this->schemaName = $systemInformation->core->schema;
578
        }
579
580 1
        return $this->schemaName;
581
    }
582
583
    /**
584
     * Gets information about the Solr server
585
     *
586
     * @return array A nested array of system data.
587
     */
588 4
    public function getSystemInformation()
589
    {
590 4
        if (empty($this->systemData)) {
591 4
            $systemInformation = $this->system();
592
593
            // access a random property to trigger response parsing
594 4
            $systemInformation->responseHeader;
595 4
            $this->systemData = $systemInformation;
596
        }
597
598 4
        return $this->systemData;
599
    }
600
601
    /**
602
     * Gets the name of the solrconfig.xml file installed and in use on the Solr
603
     * server.
604
     *
605
     * @return string Name of the active solrconfig.xml
606
     */
607 1
    public function getSolrconfigName()
608
    {
609 1
        if (is_null($this->solrconfigName)) {
610 1
            $solrconfigXmlUrl = $this->_scheme . '://'
611 1
                . $this->_host . ':' . $this->_port
612 1
                . $this->_path . 'admin/file/?file=solrconfig.xml';
613
614 1
            $solrconfigXml = simplexml_load_file($solrconfigXmlUrl);
615 1
            if ($solrconfigXml === false) {
616
                throw new \InvalidArgumentException('No valid xml response from schema file: ' . $solrconfigXmlUrl);
617
            }
618 1
            $this->solrconfigName = (string)$solrconfigXml->attributes()->name;
619
        }
620
621 1
        return $this->solrconfigName;
622
    }
623
624
    /**
625
     * Gets the Solr server's version number.
626
     *
627
     * @return string Solr version number
628
     */
629 2
    public function getSolrServerVersion()
630
    {
631 2
        $systemInformation = $this->getSystemInformation();
632
633
        // don't know why $systemInformation->lucene->solr-spec-version won't work
634 2
        $luceneInformation = (array)$systemInformation->lucene;
635 2
        return $luceneInformation['solr-spec-version'];
636
    }
637
638
    /**
639
     * Deletes all index documents of a certain type and does a commit
640
     * afterwards.
641
     *
642
     * @param string $type The type of documents to delete, usually a table name.
643
     * @param bool $commit Will commit immediately after deleting the documents if set, defaults to TRUE
644
     */
645
    public function deleteByType($type, $commit = true)
646
    {
647
        $this->deleteByQuery('type:' . trim($type));
648
649
        if ($commit) {
650
            $this->commit(false, false, false);
651
        }
652
    }
653
654
    /**
655
     * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
656
     * a complete and well formed "delete" xml document
657
     *
658
     * @param string $rawPost Expected to be utf-8 encoded xml document
659
     * @param float|int $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
660
     * @return \Apache_Solr_Response
661
     */
662 5
    public function delete($rawPost, $timeout = 3600)
663
    {
664 5
        $response = $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
665
666 5
        GeneralUtility::devLog(
667 5
            'Delete Query sent.',
668 5
            'solr',
669 5
            1,
670
            array(
671 5
                'query' => $rawPost,
672 5
                'query url' => $this->_updateUrl,
673 5
                'response' => (array)$response
674
            )
675
        );
676
677 5
        return $response;
678
    }
679
680
    /**
681
     * Central method for making a post operation against this Solr Server
682
     *
683
     * @param string $url
684
     * @param string $rawPost
685
     * @param float|bool $timeout Read timeout in seconds
686
     * @param string $contentType
687
     * @return \Apache_Solr_Response
688
     */
689 48
    protected function _sendRawPost(
690
        $url,
691
        $rawPost,
692
        $timeout = false,
693
        $contentType = 'text/xml; charset=UTF-8'
694
    ) {
695 48
        $logSeverity = 0; // info
696
697
        try {
698 48
            $response = parent::_sendRawPost($url, $rawPost, $timeout,
699
                $contentType);
700
        } catch (\Apache_Solr_HttpTransportException $e) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
701
            $logSeverity = 3; // fatal error
702
            $response = $e->getResponse();
703
        }
704
705 48
        if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) {
706
            $logData = array(
707
                'query url' => $url,
708
                'content' => $rawPost,
709
                'response' => (array)$response
710
            );
711
712
            if (!empty($e)) {
713
                $logData['exception'] = $e->__toString();
714
            }
715
716
            GeneralUtility::devLog('Querying Solr using POST', 'solr',
717
                $logSeverity, $logData);
718
        }
719
720 48
        return $response;
721
    }
722
723
    /**
724
     * Get currently configured synonyms
725
     *
726
     * @param string $baseWord If given a base word, retrieves the synonyms for that word only
727
     * @return array
728
     */
729 1
    public function getSynonyms($baseWord = '')
730
    {
731 1
        $this->initializeSynonymsUrl();
732 1
        $synonymsUrl = $this->_synonymsUrl;
733 1
        if (!empty($baseWord)) {
734 1
            $synonymsUrl .= '/' . $baseWord;
735
        }
736
737 1
        $response = $this->_sendRawGet($synonymsUrl);
738 1
        return $this->synonymParser->parseJson($baseWord, $response->getRawResponse());
739
    }
740
741
    /**
742
     * Add list of synonyms for base word to managed synonyms map
743
     *
744
     * @param string $baseWord
745
     * @param array $synonyms
746
     *
747
     * @return \Apache_Solr_Response
748
     *
749
     * @throws \Apache_Solr_InvalidArgumentException If $baseWord or $synonyms are empty
750
     */
751 1
    public function addSynonym($baseWord, array $synonyms)
752
    {
753 1
        $this->initializeSynonymsUrl();
754 1
        $json = $this->synonymParser->toJson($baseWord, $synonyms);
755 1
        return $this->_sendRawPost($this->_synonymsUrl, $json,
756 1
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
757
    }
758
759
    /**
760
     * Remove a synonym from the synonyms map
761
     *
762
     * @param string $baseWord
763
     * @return \Apache_Solr_Response
764
     * @throws \Apache_Solr_InvalidArgumentException
765
     */
766 1
    public function deleteSynonym($baseWord)
767
    {
768 1
        $this->initializeSynonymsUrl();
769 1
        if (empty($baseWord)) {
770
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word.');
771
        }
772
773 1
        return $this->_sendRawDelete($this->_synonymsUrl . '/' . $baseWord);
774
    }
775
776
    /**
777
     * Central method for making a HTTP DELETE operation against the Solr server
778
     *
779
     * @param string $url
780
     * @param bool|float $timeout Read timeout in seconds
781
     * @return \Apache_Solr_Response
782
     */
783 2
    protected function _sendRawDelete($url, $timeout = false)
784
    {
785 2
        $logSeverity = 0; // info
786
787
        try {
788 2
            $httpTransport = $this->getHttpTransport();
789
790 2
            $httpResponse = $httpTransport->performDeleteRequest($url,
791
                $timeout);
792 2
            $solrResponse = new \Apache_Solr_Response($httpResponse,
793 2
                $this->_createDocuments, $this->_collapseSingleValueArrays);
794
795 2
            if ($solrResponse->getHttpStatus() != 200) {
796 2
                throw new \Apache_Solr_HttpTransportException($solrResponse);
797
            }
798 2
        } catch (\Apache_Solr_HttpTransportException $e) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
799 2
            $logSeverity = 3; // fatal error
800 2
            $solrResponse = $e->getResponse();
801
        }
802
803 2
        if ($this->configuration->getLoggingQueryRawDelete() || $solrResponse->getHttpStatus() != 200) {
804
            $logData = array(
805 2
                'query url' => $url,
806 2
                'response' => (array)$solrResponse
807
            );
808
809 2
            if (!empty($e)) {
810 2
                $logData['exception'] = $e->__toString();
811
            } else {
812
                // trigger data parsing
813
                $solrResponse->response;
814
                $logData['response data'] = print_r($solrResponse, true);
815
            }
816
817 2
            GeneralUtility::devLog('Querying Solr using DELETE', 'solr',
818
                $logSeverity, $logData);
819
        }
820
821 2
        return $solrResponse;
822
    }
823
824
    /**
825
     * Get currently configured stop words
826
     *
827
     * @return array
828
     */
829 1
    public function getStopWords()
830
    {
831 1
        $this->initializeStopWordsUrl();
832 1
        $response = $this->_sendRawGet($this->_stopWordsUrl);
833 1
        return $this->stopWordParser->parseJson($response->getRawResponse());
834
    }
835
836
    /**
837
     * Adds stop words to the managed stop word list
838
     *
839
     * @param array|string $stopWords string for a single word, array for multiple words
840
     * @return \Apache_Solr_Response
841
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
842
     */
843 1
    public function addStopWords($stopWords)
844
    {
845 1
        $this->initializeStopWordsUrl();
846 1
        $json = $this->stopWordParser->toJson($stopWords);
847 1
        return $this->_sendRawPost($this->_stopWordsUrl, $json,
848 1
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
849
    }
850
851
    /**
852
     * Deletes a words from the managed stop word list
853
     *
854
     * @param string $stopWord stop word to delete
855
     * @return \Apache_Solr_Response
856
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
857
     */
858 1
    public function deleteStopWord($stopWord)
859
    {
860 1
        $this->initializeStopWordsUrl();
861 1
        if (empty($stopWord)) {
862
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
863
        }
864
865 1
        return $this->_sendRawDelete($this->_stopWordsUrl . '/' . $stopWord);
866
    }
867
868
    /**
869
     * Reloads the current core
870
     *
871
     * @return \Apache_Solr_Response
872
     */
873
    public function reloadCore()
874
    {
875
        $coreName = array_pop(explode('/', trim($this->_path, '/')));
0 ignored issues
show
Bug introduced by
explode('/', trim($this->_path, '/')) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
876
        $coreAdminReloadUrl = $this->_coresUrl . '?action=reload&core=' . $coreName;
877
878
        return $this->_sendRawGet($coreAdminReloadUrl);
879
    }
880
881
    /**
882
     * initializes various URLs, including the Luke URL
883
     *
884
     * @return void
885
     */
886 60
    protected function _initUrls()
887
    {
888 60
        parent::_initUrls();
889
890 60
        $this->_lukeUrl = $this->_constructUrl(
891 60
            self::LUKE_SERVLET,
892
            array(
893 60
                'numTerms' => '0',
894 60
                'wt' => self::SOLR_WRITER
895
            )
896
        );
897
898 60
        $this->_pluginsUrl = $this->_constructUrl(
899 60
            self::PLUGINS_SERVLET,
900 60
            array('wt' => self::SOLR_WRITER)
901
        );
902
903 60
        $pathElements = explode('/', trim($this->_path, '/'));
904 60
        $this->_coresUrl =
905 60
            $this->_scheme . '://' .
906 60
            $this->_host . ':' .
907 60
            $this->_port . '/' .
908 60
            $pathElements[0] . '/' .
909 60
            self::CORES_SERVLET;
910
911 60
        $this->_schemaUrl = $this->_constructUrl(self::SCHEMA_SERVLET);
912 60
    }
913
914
    /**
915
     * @return void
916
     */
917 1
    protected function initializeSynonymsUrl()
918
    {
919 1
        if (trim($this->_synonymsUrl) !== '') {
920 1
            return;
921
        }
922 1
        $this->_synonymsUrl = $this->_constructUrl(self::SYNONYMS_SERVLET) . $this->getManagedLanguage();
923 1
    }
924
925
    /**
926
     * @return void
927
     */
928 1
    protected function initializeStopWordsUrl()
929
    {
930 1
        if (trim($this->_stopWordsUrl) !== '') {
931 1
            return;
932
        }
933
934 1
        $this->_stopWordsUrl = $this->_constructUrl(self::STOPWORDS_SERVLET) . $this->getManagedLanguage();
935 1
    }
936
937
    /**
938
     * Get the language map name for the text field.
939
     * This is necessary to select the correct synonym map.
940
     *
941
     * @return string
942
     */
943 2
    protected function getManagedLanguage()
944
    {
945 2
        if ($this->managedLanguage !== '') {
946
            return $this->managedLanguage;
947
        }
948
949 2
        $schema = $this->getSchema();
950 2
        $language = 'english';
951
952 2
        if (is_object($schema) && isset($schema->fieldTypes)) {
953 2
            foreach ($schema->fieldTypes as $fieldType) {
954 2
                if ($fieldType->name === 'text') {
955 2
                    foreach ($fieldType->queryAnalyzer->filters as $filter) {
956 2
                        if ($filter->class === 'solr.ManagedSynonymFilterFactory') {
957 2
                            $language = $filter->managed;
958
                        }
959
                    }
960
                }
961
            }
962
        }
963
964 2
        $this->managedLanguage = $language;
965 2
        return $language;
966
    }
967
968
    /**
969
     * Get the configured schema for the current core
970
     *
971
     * @return \stdClass
972
     */
973 2
    protected function getSchema()
974
    {
975 2
        $response = $this->_sendRawGet($this->_schemaUrl);
976 2
        return json_decode($response->getRawResponse())->schema;
977
    }
978
}
979