Passed
Pull Request — master (#856)
by
unknown
10:26
created

Driver::driverClear()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 8
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
declare(strict_types=1);
15
16
namespace Phpfastcache\Drivers\Solr;
17
18
use Phpfastcache\Cluster\AggregatablePoolInterface;
19
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
20
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
21
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait;
22
use Phpfastcache\Entities\DriverStatistic;
23
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
24
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException;
25
use Phpfastcache\Exceptions\PhpfastcacheDriverException;
26
use Phpfastcache\Exceptions\PhpfastcacheInvalidTypeException;
27
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
28
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...
29
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...
30
use Solarium\Core\Client\Endpoint;
0 ignored issues
show
Bug introduced by
The type Solarium\Core\Client\Endpoint 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\Request;
0 ignored issues
show
Bug introduced by
The type Solarium\Core\Client\Request 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 Config $config
38
 * @property SolariumClient $instance
39
 */
40
class Driver implements ExtendedCacheItemPoolInterface, AggregatablePoolInterface
41
{
42
    public const MINIMUM_SOLARIUM_VERSION = '6.1.0';
43
44
    public const SOLR_DEFAULT_ID_FIELD = 'id';
45
46
    public const SOLR_DISCRIMINATOR_FIELD = 'type';
47
48
    public const SOLR_DISCRIMINATOR_VALUE = '_pfc_';
49
50
    use TaggableCacheItemPoolTrait;
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
        $this->instance = new SolariumClient(new SolariumCurlAdapter(), $this->getConfig()->getEventDispatcher(), [
84
            'endpoint' => [
85
                $this->getConfig()->getEndpointName() => [
86
                    'scheme' => $this->getConfig()->getScheme(),
87
                    'host' => $this->getConfig()->getHost(),
88
                    'port' => $this->getConfig()->getPort(),
89
                    'path' => $this->getConfig()->getPath(),
90
                    'core' => $this->getConfig()->getCoreName(),
91
                ]
92
            ]
93
        ]);
94
95
        try {
96
            return $this->instance->ping($this->instance->createPing())->getStatus() === 0;
97
        } catch (SolariumExceptionInterface $e) {
98
            throw new PhpfastcacheDriverConnectException($e->getMessage(), 0, $e);
99
        }
100
    }
101
102
    /**
103
     * @param ExtendedCacheItemInterface $item
104
     * @return bool
105
     * @throws PhpfastcacheLogicException
106
     */
107
    protected function driverWrite(ExtendedCacheItemInterface $item): bool
108
    {
109
        $update = $this->instance->createUpdate();
110
111
        $doc = $update->createDocument();
112
        $doc->{$this->getSolrField(self::SOLR_DEFAULT_ID_FIELD)} = $item->getEncodedKey();
113
        $doc->{$this->getSolrField(self::SOLR_DISCRIMINATOR_FIELD)} = self::SOLR_DISCRIMINATOR_VALUE;
114
        $doc->{$this->getSolrField(self::DRIVER_KEY_WRAPPER_INDEX)} = $item->getKey();
115
        $doc->{$this->getSolrField(self::DRIVER_DATA_WRAPPER_INDEX)} = $this->encode($item->getRawValue());
116
        $doc->{$this->getSolrField(self::DRIVER_TAGS_WRAPPER_INDEX)} = $item->getTags();
117
        $doc->{$this->getSolrField(self::DRIVER_EDATE_WRAPPER_INDEX)} = $item->getExpirationDate()->format(\DateTimeInterface::ATOM);
118
119
        if ($this->getConfig()->isItemDetailedDate()) {
120
            $doc->{$this->getSolrField(self::DRIVER_MDATE_WRAPPER_INDEX)} = $item->getModificationDate()->format(\DateTimeInterface::ATOM);
121
            $doc->{$this->getSolrField(self::DRIVER_CDATE_WRAPPER_INDEX)} = $item->getCreationDate()->format(\DateTimeInterface::ATOM);
122
        }
123
124
        $update->addDocument($doc);
125
        $update->addCommit();
126
127
        return $this->instance->update($update)->getStatus() === 0;
128
    }
129
130
    /**
131
     * @param ExtendedCacheItemInterface $item
132
     * @return null|array
133
     * @throws \Exception
134
     */
135
    protected function driverRead(ExtendedCacheItemInterface $item): ?array
136
    {
137
        $query = $this->instance->createSelect()
138
            ->setQuery($this->getSolrField(self::SOLR_DEFAULT_ID_FIELD) . ':' . $item->getEncodedKey())
139
            ->setRows(1);
140
141
        $results = $this->instance->execute($query);
142
143
        if ($results instanceof \IteratorAggregate) {
144
            $document = $results->getIterator()[0] ?? null;
145
146
            if ($document instanceof SolariumDocument) {
147
                return $this->decodeDocument($document);
148
            }
149
        }
150
151
        return null;
152
    }
153
154
    /**
155
     * @param SolariumDocument $document
156
     * @return array
157
     * @throws \Exception
158
     */
159
    protected function decodeDocument(SolariumDocument $document): array
160
    {
161
        $fields = $document->getFields();
162
        $key = $fields[$this->getSolrField(self::DRIVER_KEY_WRAPPER_INDEX)];
163
164
        if (\is_array($key)) {
165
            throw new PhpfastcacheInvalidTypeException(
166
                'Your Solr core seems to be misconfigured, please check the Phpfastcache wiki to setup the expected schema: 
167
                https://github.com/PHPSocialNetwork/phpfastcache/wiki/%5BV9.1%CB%96%5D-Configuring-a-Solr-driver'
168
            );
169
        }
170
171
        $value = [
172
            self::DRIVER_KEY_WRAPPER_INDEX => $key,
173
            self::DRIVER_TAGS_WRAPPER_INDEX => $fields[$this->getSolrField(self::DRIVER_TAGS_WRAPPER_INDEX)] ?? [],
174
            self::DRIVER_DATA_WRAPPER_INDEX => $this->decode(
175
                $fields[$this->getSolrField(self::DRIVER_DATA_WRAPPER_INDEX)],
176
            ),
177
        ];
178
179
        $eDate = $fields[$this->getSolrField(self::DRIVER_EDATE_WRAPPER_INDEX)];
180
181
        $value[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX] = new \DateTime($eDate);
182
183
        if ($this->getConfig()->isItemDetailedDate()) {
184
            $cDate = $fields[$this->getSolrField(self::DRIVER_CDATE_WRAPPER_INDEX)];
185
            if (!empty($cDate)) {
186
                $value[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX] = new \DateTime($cDate);
187
            }
188
189
            $mDate = $fields[$this->getSolrField(self::DRIVER_MDATE_WRAPPER_INDEX)];
190
            if (!empty($mDate)) {
191
                $value[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX] = new \DateTime($mDate);
192
            }
193
        }
194
195
        return $value;
196
    }
197
198
199
    /**
200
     * @param ExtendedCacheItemInterface $item
201
     * @return bool
202
     */
203
    protected function driverDelete(ExtendedCacheItemInterface $item): bool
204
    {
205
        $update = $this->instance->createUpdate();
206
207
        $update->addDeleteQuery($this->getSolrField(self::SOLR_DEFAULT_ID_FIELD) . ':' . $item->getEncodedKey());
208
        $update->addDeleteQuery($this->getSolrField(self::SOLR_DISCRIMINATOR_FIELD) . ':' . self::SOLR_DISCRIMINATOR_VALUE);
209
        $update->addCommit();
210
211
        return $this->instance->update($update)->getStatus() === 0;
212
    }
213
214
    /**
215
     * @return bool
216
     * @throws PhpfastcacheDriverException
217
     */
218
    protected function driverClear(): bool
219
    {
220
        // get an update query instance
221
        $update = $this->instance->createUpdate();
222
        $update->addDeleteQuery($this->getSolrField(self::SOLR_DISCRIMINATOR_FIELD) . ':' . self::SOLR_DISCRIMINATOR_VALUE);
223
        $update->addCommit();
224
225
        return $this->instance->update($update)->getStatus() === 0;
226
    }
227
228
    /**
229
     * @param string $pfcField
230
     * @return string
231
     */
232
    protected function getSolrField(string $pfcField): string
233
    {
234
        return $this->mappingSchema[$pfcField];
235
    }
236
237
    public function getStats(): DriverStatistic
238
    {
239
        /**
240
         * Solr "phpfastcache" core info
241
         */
242
        $coreAdminQuery = $this->instance->createCoreAdmin();
243
        $statusAction = $coreAdminQuery->createStatus();
244
        $coreAdminQuery->setAction($statusAction);
245
        $response = $this->instance->coreAdmin($coreAdminQuery);
246
        $coreServerInfo = $response->getData()['status'][$this->config->getCoreName()];
247
248
        /**
249
         * Unfortunately Solarium does not offer
250
         * an API to query the admin info system :(
251
         */
252
        $adminSystemInfoUrl = $this->config->getScheme()
253
            . '://'
254
            . $this->config->getHost()
255
            . ':'
256
            . $this->config->getPort()
257
            . rtrim($this->config->getPath(), '/')
258
            . '/solr/admin/info/system';
259
260
        if (($content = @\file_get_contents($adminSystemInfoUrl)) !== false) {
261
            try {
262
                $serverSystemInfo = \json_decode($content, true, 512, \JSON_THROW_ON_ERROR);
263
            } catch (\JsonException) {
264
                $serverSystemInfo = [];
265
            }
266
        }
267
268
        return (new DriverStatistic())
269
            ->setData(implode(', ', array_keys($this->itemInstances)))
270
            ->setInfo(sprintf(
271
                'Solarium %s and Solr %s for %s %s. %d document(s) stored in the "%s" core',
272
                $this->instance::VERSION,
273
                $serverSystemInfo['lucene']['solr-spec-version'] ?? '[unknown SOLR version]',
274
                $serverSystemInfo['system']['name'] ?? '[unknown OS]',
275
                $serverSystemInfo['system']['version'] ?? '[unknown OS version]',
276
                $coreServerInfo['index']['numDocs'] ?? 0,
277
                $this->config->getCoreName()
278
            ))
279
            ->setRawData($coreServerInfo)
280
            ->setSize($coreServerInfo['index']['sizeInBytes'] ?? 0);
281
    }
282
283
    public function getConfig(): Config
284
    {
285
        return $this->config;
286
    }
287
}
288