Merge "StringUtils: Add a utility for checking if a string is a valid regex"
[lhc/web/wiklou.git] / includes / historyblob / ConcatenatedGzipHistoryBlob.php
1 <?php
2 /**
3 * Efficient concatenated text storage.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22
23 /**
24 * Concatenated gzip (CGZ) storage
25 * Improves compression ratio by concatenating like objects before gzipping
26 */
27 class ConcatenatedGzipHistoryBlob implements HistoryBlob {
28 public $mVersion = 0;
29 public $mCompressed = false;
30 /**
31 * @var array|string
32 * @fixme Why are some methods treating it as an array, and others as a string, unconditionally?
33 */
34 public $mItems = [];
35 public $mDefaultHash = '';
36 public $mSize = 0;
37 public $mMaxSize = 10000000;
38 public $mMaxCount = 100;
39
40 public function __construct() {
41 if ( !function_exists( 'gzdeflate' ) ) {
42 throw new MWException( "Need zlib support to read or write this "
43 . "kind of history object (ConcatenatedGzipHistoryBlob)\n" );
44 }
45 }
46
47 /**
48 * @param string $text
49 * @return string
50 */
51 public function addItem( $text ) {
52 $this->uncompress();
53 $hash = md5( $text );
54 if ( !isset( $this->mItems[$hash] ) ) {
55 $this->mItems[$hash] = $text;
56 $this->mSize += strlen( $text );
57 }
58 return $hash;
59 }
60
61 /**
62 * @param string $hash
63 * @return array|bool
64 */
65 public function getItem( $hash ) {
66 $this->uncompress();
67 if ( array_key_exists( $hash, $this->mItems ) ) {
68 return $this->mItems[$hash];
69 } else {
70 return false;
71 }
72 }
73
74 /**
75 * @param string $text
76 * @return void
77 */
78 public function setText( $text ) {
79 $this->uncompress();
80 $this->mDefaultHash = $this->addItem( $text );
81 }
82
83 /**
84 * @return array|bool
85 */
86 public function getText() {
87 $this->uncompress();
88 return $this->getItem( $this->mDefaultHash );
89 }
90
91 /**
92 * Remove an item
93 *
94 * @param string $hash
95 */
96 public function removeItem( $hash ) {
97 $this->mSize -= strlen( $this->mItems[$hash] );
98 unset( $this->mItems[$hash] );
99 }
100
101 /**
102 * Compress the bulk data in the object
103 */
104 public function compress() {
105 if ( !$this->mCompressed ) {
106 $this->mItems = gzdeflate( serialize( $this->mItems ) );
107 $this->mCompressed = true;
108 }
109 }
110
111 /**
112 * Uncompress bulk data
113 */
114 public function uncompress() {
115 if ( $this->mCompressed ) {
116 $this->mItems = unserialize( gzinflate( $this->mItems ) );
117 $this->mCompressed = false;
118 }
119 }
120
121 /**
122 * @return array
123 */
124 function __sleep() {
125 $this->compress();
126 return [ 'mVersion', 'mCompressed', 'mItems', 'mDefaultHash' ];
127 }
128
129 function __wakeup() {
130 $this->uncompress();
131 }
132
133 /**
134 * Helper function for compression jobs
135 * Returns true until the object is "full" and ready to be committed
136 *
137 * @return bool
138 */
139 public function isHappy() {
140 return $this->mSize < $this->mMaxSize
141 && count( $this->mItems ) < $this->mMaxCount;
142 }
143 }
144
145 // phpcs:ignore Generic.CodeAnalysis.UnconditionalIfStatement.Found
146 if ( false ) {
147 // Blobs generated by MediaWiki < 1.5 on PHP 4 were serialized with the
148 // class name coerced to lowercase. We can improve efficiency by adding
149 // autoload entries for the lowercase variants of these classes (T166759).
150 // The code below is never executed, but it is picked up by the AutoloadGenerator
151 // parser, which scans for class_alias() calls.
152 class_alias( ConcatenatedGzipHistoryBlob::class, 'concatenatedgziphistoryblob' );
153 }