Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
PersistentDatabaseCache.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\Cache;
5
6use ArrayIterator;
7use Iterator;
8use MediaWiki\Json\JsonCodec;
9use Wikimedia\Rdbms\ILoadBalancer;
10
18 private const TABLE_NAME = 'translate_cache';
19
21 private $loadBalancer;
23 private $jsonCodec;
24
25 public function __construct( ILoadBalancer $loadBalancer, JsonCodec $jsonCodec ) {
26 $this->loadBalancer = $loadBalancer;
27 $this->jsonCodec = $jsonCodec;
28 }
29
31 public function get( string ...$keynames ): array {
32 $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
33 $rows = $dbr->newSelectQueryBuilder()
34 ->select( [ 'tc_key', 'tc_value', 'tc_exptime', 'tc_tag' ] )
35 ->from( self::TABLE_NAME )
36 ->where( [ 'tc_key' => $keynames ] )
37 ->caller( __METHOD__ )
38 ->fetchResultSet();
39
40 return $this->buildEntries( $rows );
41 }
42
43 public function getWithLock( string $keyname ): ?PersistentCacheEntry {
44 $dbr = $this->loadBalancer->getConnection( DB_PRIMARY );
45 $rows = $dbr->newSelectQueryBuilder()
46 ->select( [ 'tc_key', 'tc_value', 'tc_exptime', 'tc_tag' ] )
47 ->forUpdate()
48 ->from( self::TABLE_NAME )
49 ->where( [ 'tc_key' => $keyname ] )
50 ->caller( __METHOD__ )
51 ->fetchResultSet();
52
53 $entries = $this->buildEntries( $rows );
54 return count( $entries ) ? $entries[0] : null;
55 }
56
58 public function getByTag( string $tag ): array {
59 $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
60 $rows = $dbr->newSelectQueryBuilder()
61 ->select( [ 'tc_key', 'tc_value', 'tc_exptime', 'tc_tag' ] )
62 ->from( self::TABLE_NAME )
63 ->where( [ 'tc_tag' => $tag ] )
64 ->caller( __METHOD__ )
65 ->fetchResultSet();
66
67 return $this->buildEntries( $rows );
68 }
69
70 public function has( string $keyname ): bool {
71 $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
72 $hasRow = $dbr->newSelectQueryBuilder()
73 ->select( 'tc_key' )
74 ->from( self::TABLE_NAME )
75 ->where( [ 'tc_key' => $keyname ] )
76 ->caller( __METHOD__ )
77 ->fetchRow();
78
79 return (bool)$hasRow;
80 }
81
82 public function hasEntryWithTag( string $tag ): bool {
83 $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
84 $hasRow = $dbr->newSelectQueryBuilder()
85 ->select( 'tc_key' )
86 ->from( self::TABLE_NAME )
87 ->where( [ 'tc_tag' => $tag ] )
88 ->caller( __METHOD__ )
89 ->fetchRow();
90
91 return (bool)$hasRow;
92 }
93
94 public function hasExpiredEntry( string $keyname ): bool {
95 $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
96 $row = $dbr->newSelectQueryBuilder()
97 ->select( 'tc_expired' )
98 ->from( self::TABLE_NAME )
99 ->where( [ 'tc_key' => $keyname ] )
100 ->caller( __METHOD__ )
101 ->fetchRow();
102
103 if ( $row === false ) {
104 return false;
105 }
106
107 $rows = new ArrayIterator( [ $row ] );
108 $entry = $this->buildEntries( $rows )[0];
109 return $entry->hasExpired();
110 }
111
112 public function set( PersistentCacheEntry ...$entries ): void {
113 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
114
115 foreach ( $entries as $entry ) {
116 $value = $this->jsonCodec->serialize( $entry->value() );
117 $rowsToInsert = [
118 'tc_key' => $entry->key(),
119 'tc_value' => $value,
120 'tc_exptime' => $entry->exptime(),
121 'tc_tag' => $entry->tag()
122 ];
123
124 $rowsToUpdate = [
125 'tc_value' => $value,
126 'tc_exptime' => $entry->exptime(),
127 'tc_tag' => $entry->tag()
128 ];
129
130 $dbw->upsert(
131 self::TABLE_NAME,
132 $rowsToInsert,
133 'tc_key',
134 $rowsToUpdate,
135 __METHOD__
136 );
137 }
138 }
139
140 public function setExpiry( string $keyname, int $expiryTime ): void {
141 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
142 $dbw->update(
143 self::TABLE_NAME,
144 [ 'tc_exptime' => $expiryTime ],
145 [ 'tc_key' => $keyname ],
146 __METHOD__
147 );
148 }
149
150 public function delete( string ...$keynames ): void {
151 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
152 $dbw->delete(
153 self::TABLE_NAME,
154 [ 'tc_key' => $keynames ],
155 __METHOD__
156 );
157 }
158
159 public function deleteEntriesWithTag( string $tag ): void {
160 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
161 $dbw->delete(
162 self::TABLE_NAME,
163 [ 'tc_tag' => $tag ],
164 __METHOD__
165 );
166 }
167
168 public function clear(): void {
169 $dbw = $this->loadBalancer->getConnection( DB_PRIMARY );
170 $dbw->delete(
171 self::TABLE_NAME,
172 '*',
173 __METHOD__
174 );
175 }
176
178 private function buildEntries( Iterator $rows ): array {
179 $entries = [];
180 foreach ( $rows as $row ) {
181 $entries[] = new PersistentCacheEntry(
182 $row->tc_key,
183 $this->jsonCodec->unserialize( $row->tc_value ),
184 $row->tc_exptime ? (int)$row->tc_exptime : null,
185 $row->tc_tag
186 );
187 }
188
189 return $entries;
190 }
191}
Represents a single result from the persistent cache.
A persistent cache implementation using the database.
Defines what method should be provided by a class implementing a persistent cache.