Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
LCStoreCDB
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 7
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
56
 startWrite
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 finishWrite
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 set
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getFileName
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 lateFallback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Language;
8
9use Cdb\Exception as CdbException;
10use Cdb\Reader;
11use Cdb\Writer;
12use InvalidArgumentException;
13use LogicException;
14use RuntimeException;
15
16/**
17 * LCStore implementation which stores data as a collection of CDB files.
18 *
19 * Profiling indicates that on Linux, this implementation outperforms MySQL if
20 * the directory is on a local filesystem and there is ample kernel cache
21 * space. The performance advantage is greater when the DBA extension is
22 * available than it is with the PHP port.
23 *
24 * See Cdb.php and https://cr.yp.to/cdb.html
25 *
26 * @ingroup Language
27 */
28class LCStoreCDB implements LCStore {
29
30    /** @var Reader[]|false[] */
31    private $readers;
32
33    /** @var Writer|null */
34    private $writer;
35
36    /** @var string|null Current language code */
37    private $currentLang;
38
39    /** @var string Cache directory */
40    private $directory;
41
42    public function __construct( array $conf = [] ) {
43        $this->directory = $conf['directory'];
44    }
45
46    /** @inheritDoc */
47    public function get( $code, $key ) {
48        if ( !isset( $this->readers[$code] ) ) {
49            $fileName = $this->getFileName( $code );
50
51            $this->readers[$code] = false;
52            if ( is_file( $fileName ) ) {
53                try {
54                    $this->readers[$code] = Reader::open( $fileName );
55                } catch ( CdbException ) {
56                    wfDebug( __METHOD__ . ": unable to open cdb file for reading" );
57                }
58            }
59        }
60
61        if ( !$this->readers[$code] ) {
62            return null;
63        } else {
64            $value = false;
65            try {
66                $value = $this->readers[$code]->get( $key );
67            } catch ( CdbException $e ) {
68                wfDebug( __METHOD__ . ": \Cdb\Exception caught, error message was "
69                    . $e->getMessage() );
70            }
71            if ( $value === false ) {
72                return null;
73            }
74
75            return unserialize( $value );
76        }
77    }
78
79    /** @inheritDoc */
80    public function startWrite( $code ) {
81        if ( !is_dir( $this->directory ) && !wfMkdirParents( $this->directory, null, __METHOD__ ) ) {
82            throw new RuntimeException( "Unable to create the localisation store " .
83                "directory \"{$this->directory}\"" );
84        }
85
86        // Close reader to stop permission errors on write
87        if ( !empty( $this->readers[$code] ) ) {
88            $this->readers[$code]->close();
89        }
90
91        $this->writer = Writer::open( $this->getFileName( $code ) );
92        $this->currentLang = $code;
93    }
94
95    public function finishWrite() {
96        $this->writer->close();
97        $this->writer = null;
98        unset( $this->readers[$this->currentLang] );
99        $this->currentLang = null;
100    }
101
102    /** @inheritDoc */
103    public function set( $key, $value ) {
104        if ( $this->writer === null ) {
105            throw new LogicException( __CLASS__ . ': must call startWrite() before calling set()' );
106        }
107        $this->writer->set( $key, serialize( $value ) );
108    }
109
110    /**
111     * @param string|null $code
112     * @return string
113     */
114    protected function getFileName( $code ) {
115        if ( strval( $code ) === '' || str_contains( $code, '/' ) ) {
116            throw new InvalidArgumentException( __METHOD__ . ": Invalid language \"$code\"" );
117        }
118
119        return "{$this->directory}/l10n_cache-$code.cdb";
120    }
121
122    /** @inheritDoc */
123    public function lateFallback(): bool {
124        return false;
125    }
126}
127
128/** @deprecated class alias since 1.46 */
129class_alias( LCStoreCDB::class, 'LCStoreCDB' );