Passed
Pull Request — master (#857)
by Georges
01:55
created

Driver::getSolrField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 *
5
 * This file is part of Phpfastcache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt and LICENCE files.
10
 *
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Contributors  https://github.com/PHPSocialNetwork/phpfastcache/graphs/contributors
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phpfastcache\Drivers\Solr;
18
19
use Phpfastcache\Cluster\AggregatablePoolInterface;
20
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
21
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
22
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait;
23
use Phpfastcache\Entities\DriverStatistic;
24
use Phpfastcache\Event\EventReferenceParameter;
25
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
26
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException;
27
use Phpfastcache\Exceptions\PhpfastcacheDriverException;
28
use Phpfastcache\Exceptions\PhpfastcacheInvalidTypeException;
29
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
30
use Solarium\Client as SolariumClient;
0 ignored issues
show
Bug introduced by
The type Solarium\Client was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use Solarium\Core\Client\Adapter\Curl as SolariumCurlAdapter;
0 ignored issues
show
Bug introduced by
The type Solarium\Core\Client\Adapter\Curl was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
use Solarium\Exception\ExceptionInterface as SolariumExceptionInterface;
0 ignored issues
show
Bug introduced by
The type Solarium\Exception\ExceptionInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
use Solarium\QueryType\Select\Result\Document as SolariumDocument;
0 ignored issues
show
Bug introduced by
The type Solarium\QueryType\Select\Result\Document was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
35
/**
36
 * Class Driver
37
 * @property SolariumClient $instance
38
 * @method Config getConfig()
39
 */
40
class Driver implements ExtendedCacheItemPoolInterface, AggregatablePoolInterface
41
{
42
    use TaggableCacheItemPoolTrait;
43
44
    public const MINIMUM_SOLARIUM_VERSION = '6.1.0';
45
46
    public const SOLR_DEFAULT_ID_FIELD = 'id';
47
48
    public const SOLR_DISCRIMINATOR_FIELD = 'type';
49
50
    public const SOLR_DISCRIMINATOR_VALUE = '_pfc_';
51
52
    /**
53
     * Copy of configuration entry for performance optimization
54
     * @var string[]
55
     */
56
    protected array $mappingSchema = [];
57
58
    /**
59
     * @return bool
60
     * @throws PhpfastcacheDriverCheckException
61
     */
62
    public function driverCheck(): bool
63
    {
64
        if (!\class_exists(SolariumClient::class)) {
65
            throw new PhpfastcacheDriverCheckException(
66
                \sprintf(
67
                    'Phpfastcache needs Solarium %s or greater to be installed',
68
                    self::MINIMUM_SOLARIUM_VERSION
69
                )
70
            );
71
        }
72
73
        return true;
74
    }
75
76
    /**
77
     * @return bool
78
     * @throws PhpfastcacheDriverConnectException
79
     */
80
    protected function driverConnect(): bool
81
    {
82
        $this->mappingSchema = $this->getConfig()->getMappingSchema();
83
84
        $endpoint = [
85
            'endpoint' => [
86
                $this->getConfig()->getEndpointName() => [
87
                    'scheme' => $this->getConfig()->getScheme(),
88
                    'host' => $this->getConfig()->getHost(),
89
                    'port' => $this->getConfig()->getPort(),
90
                    'path' => $this->getConfig()->getPath(),
91
                    'core' => $this->getConfig()->getCoreName(),
92
                ]
93
            ]
94
        ];
95
96
        $this->eventManager->dispatch(Event::SOLR_BUILD_ENDPOINT, $this, new EventReferenceParameter($endpoint));
97
98
        $this->instance = new SolariumClient(new SolariumCurlAdapter(), $this->getConfig()->getEventDispatcher(), $endpoint);
99
100
        try {
101
            return $this->instance->ping($this->instance->createPing())->getStatus() === 0;
102
        } catch (SolariumExceptionInterface $e) {
103
            throw new PhpfastcacheDriverConnectException($e->getMessage(), 0, $e);
104
        }
105
    }
106
107
    /**
108
     * @param ExtendedCacheItemInterface $item
109
     * @return bool
110
     * @throws PhpfastcacheLogicException
111
     */
112
    protected function driverWrite(ExtendedCacheItemInterface $item): bool
113
    {
114
        $update = $this->instance->createUpdate();
115
116
        $doc = $update->createDocument();
117
        $doc->{$this->getSolrField(self::SOLR_DEFAULT_ID_FIELD)} = $item->getEncodedKey();
118
        $doc->{$this->getSolrField(self::SOLR_DISCRIMINATOR_FIELD)} = self::SOLR_DISCRIMINATOR_VALUE;
119
        $doc->{$this->getSolrField(self::DRIVER_KEY_WRAPPER_INDEX)} = $item->getKey();
120
        $doc->{$this->getSolrField(self::DRIVER_DATA_WRAPPER_INDEX)} = $this->encode($item->getRawValue());
121
        $doc->{$this->getSolrField(self::DRIVER_TAGS_WRAPPER_INDEX)} = $item->getTags();
122
        $doc->{$this->getSolrField(self::DRIVER_EDATE_WRAPPER_INDEX)} = $item->getExpirationDate()->format(\DateTimeInterface::ATOM);
123
124
        if ($this->getConfig()->isItemDetailedDate()) {
125
            $doc->{$this->getSolrField(self::DRIVER_MDATE_WRAPPER_INDEX)} = $item->getModificationDate()->format(\DateTimeInterface::ATOM);
126
            $doc->{$this->getSolrField(self::DRIVER_CDATE_WRAPPER_INDEX)} = $item->getCreationDate()->format(\DateTimeInterface::ATOM);
127
        }
128
129
        $update->addDocument($doc);
130
        $update->addCommit();
131
132
        return $this->instance->update($update)->getStatus() === 0;
133
    }
134
135
    /**
136
     * @param ExtendedCacheItemInterface $item
137
     * @return null|array
138
     * @throws \Exception
139
     */
140
    protected function driverRead(ExtendedCacheItemInterface $item): ?array
141
    {
142
        $query = $this->instance->createSelect()
143
            ->setQuery($this->getSolrField(self::SOLR_DEFAULT_ID_FIELD) . ':' . $item->getEncodedKey())
144
            ->setRows(1);
145
146
        $results = $this->instance->execute($query);
147
148
        if ($results instanceof \IteratorAggregate) {
149
            $document = $results->getIterator()[0] ?? null;
150
151
            if ($document instanceof SolariumDocument) {
152
                return $this->decodeDocument($document);
153
            }
154
        }
155
156
        return null;
157
    }
158
159
    /**
160
     * @param SolariumDocument $document
161
     * @return array
162
     * @throws \Exception
163
     */
164
    protected function decodeDocument(SolariumDocument $document): array
165
    {
166
        $fields = $document->getFields();
167
        $key = $fields[$this->getSolrField(self::DRIVER_KEY_WRAPPER_INDEX)];
168
169
        if (\is_array($key)) {
170
            throw new PhpfastcacheInvalidTypeException(
171
                'Your Solr core seems to be misconfigured, please check the Phpfastcache wiki to setup the expected schema: 
172
                https://github.com/PHPSocialNetwork/phpfastcache/wiki/%5BV9.1%CB%96%5D-Configuring-a-Solr-driver'
173
            );
174
        }
175
176
        $value = [
177
            self::DRIVER_KEY_WRAPPER_INDEX => $key,
178
            self::DRIVER_TAGS_WRAPPER_INDEX => $fields[$this->getSolrField(self::DRIVER_TAGS_WRAPPER_INDEX)] ?? [],
179
            self::DRIVER_DATA_WRAPPER_INDEX => $this->decode(
180
                $fields[$this->getSolrField(self::DRIVER_DATA_WRAPPER_INDEX)],
181
            ),
182
        ];
183
184
        $eDate = $fields[$this->getSolrField(self::DRIVER_EDATE_WRAPPER_INDEX)];
185
186
        $value[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX] = new \DateTime($eDate);
187
188
        if ($this->getConfig()->isItemDetailedDate()) {
189
            $cDate = $fields[$this->getSolrField(self::DRIVER_CDATE_WRAPPER_INDEX)];
190
            if (!empty($cDate)) {
191
                $value[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX] = new \DateTime($cDate);
192
            }
193
194
            $mDate = $fields[$this->getSolrField(self::DRIVER_MDATE_WRAPPER_INDEX)];
195
            if (!empty($mDate)) {
196
                $value[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX] = new \DateTime($mDate);
197
            }
198
        }
199
200
        return $value;
201
    }
202
203
204
    /**
205
     * @param ExtendedCacheItemInterface $item
206
     * @return bool
207
     */
208
    protected function driverDelete(ExtendedCacheItemInterface $item): bool
209
    {
210
        $update = $this->instance->createUpdate();
211
212
        $update->addDeleteQuery($this->getSolrField(self::SOLR_DEFAULT_ID_FIELD) . ':' . $item->getEncodedKey());
213
        $update->addDeleteQuery($this->getSolrField(self::SOLR_DISCRIMINATOR_FIELD) . ':' . self::SOLR_DISCRIMINATOR_VALUE);
214
        $update->addCommit();
215
216
        return $this->instance->update($update)->getStatus() === 0;
217
    }
218
219
    /**
220
     * @return bool
221
     * @throws PhpfastcacheDriverException
222
     */
223
    protected function driverClear(): bool
224
    {
225
        // get an update query instance
226
        $update = $this->instance->createUpdate();
227
        $update->addDeleteQuery($this->getSolrField(self::SOLR_DISCRIMINATOR_FIELD) . ':' . self::SOLR_DISCRIMINATOR_VALUE);
228
        $update->addCommit();
229
230
        return $this->instance->update($update)->getStatus() === 0;
231
    }
232
233
    /**
234
     * @param string $pfcField
235
     * @return string
236
     */
237
    protected function getSolrField(string $pfcField): string
238
    {
239
        return $this->mappingSchema[$pfcField];
240
    }
241
242
    public function getStats(): DriverStatistic
243
    {
244
        /**
245
         * Solr "phpfastcache" core info
246
         */
247
        $coreAdminQuery = $this->instance->createCoreAdmin();
248
        $statusAction = $coreAdminQuery->createStatus();
249
        $coreAdminQuery->setAction($statusAction);
250
        $response = $this->instance->coreAdmin($coreAdminQuery);
251
        $coreServerInfo = $response->getData()['status'][$this->getConfig()->getCoreName()];
252
253
        /**
254
         * Unfortunately Solarium does not offer
255
         * an API to query the admin info system :(
256
         */
257
        $adminSystemInfoUrl = $this->getConfig()->getScheme()
258
            . '://'
259
            . $this->getConfig()->getHost()
260
            . ':'
261
            . $this->getConfig()->getPort()
262
            . rtrim($this->getConfig()->getPath(), '/')
263
            . '/solr/admin/info/system';
264
265
        if (($content = @\file_get_contents($adminSystemInfoUrl)) !== false) {
266
            try {
267
                $serverSystemInfo = \json_decode($content, true, 512, \JSON_THROW_ON_ERROR);
268
            } catch (\JsonException) {
269
                $serverSystemInfo = [];
270
            }
271
        }
272
273
        return (new DriverStatistic())
274
            ->setData(implode(', ', array_keys($this->itemInstances)))
275
            ->setInfo(sprintf(
276
                'Solarium %s and Solr %s for %s %s. %d document(s) stored in the "%s" core',
277
                $this->instance::VERSION,
278
                $serverSystemInfo['lucene']['solr-spec-version'] ?? '[unknown SOLR version]',
279
                $serverSystemInfo['system']['name'] ?? '[unknown OS]',
280
                $serverSystemInfo['system']['version'] ?? '[unknown OS version]',
281
                $coreServerInfo['index']['numDocs'] ?? 0,
282
                $this->getConfig()->getCoreName()
283
            ))
284
            ->setRawData($coreServerInfo)
285
            ->setSize($coreServerInfo['index']['sizeInBytes'] ?? 0);
286
    }
287
}
288