Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
WinCacheBagOStuff
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 9
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 doGet
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 doCas
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 doSet
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 doAdd
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 doDelete
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 makeKeyInternal
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 requireConvertGenericKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doIncrWithInit
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2/**
3 * Object caching using WinCache.
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 * @ingroup Cache
22 */
23
24namespace Wikimedia\ObjectCache;
25
26/**
27 * Wrapper for WinCache object caching functions; identical interface
28 * to the APC wrapper
29 *
30 * @ingroup Cache
31 */
32class WinCacheBagOStuff extends MediumSpecificBagOStuff {
33    public function __construct( array $params = [] ) {
34        $params['segmentationSize'] ??= INF;
35        parent::__construct( $params );
36
37        if ( PHP_SAPI === 'cli' ) {
38            $this->attrMap[self::ATTR_DURABILITY] = ini_get( 'wincache.enablecli' )
39                ? self::QOS_DURABILITY_SCRIPT
40                : self::QOS_DURABILITY_NONE;
41        } else {
42            $this->attrMap[self::ATTR_DURABILITY] = self::QOS_DURABILITY_SERVICE;
43        }
44    }
45
46    protected function doGet( $key, $flags = 0, &$casToken = null ) {
47        $getToken = ( $casToken === self::PASS_BY_REF );
48        $casToken = null;
49
50        $data = wincache_ucache_get( $key );
51        if ( !is_string( $data ) && !is_int( $data ) ) {
52            return false;
53        }
54
55        $value = $this->unserialize( $data );
56        if ( $getToken && $value !== false ) {
57            $casToken = $data;
58        }
59
60        return $value;
61    }
62
63    protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
64        // optimize with FIFO lock
65        if ( !wincache_lock( $key ) ) {
66            return false;
67        }
68
69        $curCasToken = self::PASS_BY_REF;
70        $this->doGet( $key, self::READ_LATEST, $curCasToken );
71        if ( $casToken === $curCasToken ) {
72            $success = $this->set( $key, $value, $exptime, $flags );
73        } else {
74            $this->logger->info(
75                __METHOD__ . ' failed due to race condition for {key}.',
76                [ 'key' => $key ]
77            );
78
79            // mismatched or failed
80            $success = false;
81        }
82
83        wincache_unlock( $key );
84
85        return $success;
86    }
87
88    protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
89        $ttl = $this->getExpirationAsTTL( $exptime );
90        $result = wincache_ucache_set( $key, $this->getSerialized( $value, $key ), $ttl );
91
92        // false positive, wincache_ucache_set returns an empty array
93        // in some circumstances.
94        // @phan-suppress-next-line PhanTypeComparisonToArray
95        return ( $result === [] || $result === true );
96    }
97
98    protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
99        if ( wincache_ucache_exists( $key ) ) {
100            // avoid warnings
101            return false;
102        }
103
104        $ttl = $this->getExpirationAsTTL( $exptime );
105        $result = wincache_ucache_add( $key, $this->getSerialized( $value, $key ), $ttl );
106
107        // false positive, wincache_ucache_add returns an empty array
108        // in some circumstances.
109        // @phan-suppress-next-line PhanTypeComparisonToArray
110        return ( $result === [] || $result === true );
111    }
112
113    protected function doDelete( $key, $flags = 0 ) {
114        wincache_ucache_delete( $key );
115
116        return true;
117    }
118
119    protected function makeKeyInternal( $keyspace, $components ) {
120        // WinCache keys have a maximum length of 150 characters. From that,
121        // subtract the number of characters we need for the keyspace and for
122        // the separator character needed for each argument. To handle some
123        // custom prefixes used by thing like WANObjectCache, limit to 125.
124        // NOTE: Same as in memcached, except the max key length there is 255.
125        $charsLeft = 125 - strlen( $keyspace ) - count( $components );
126
127        $components = array_map(
128            static function ( $component ) use ( &$charsLeft ) {
129                // 33 = 32 characters for the MD5 + 1 for the '#' prefix.
130                if ( $charsLeft > 33 && strlen( $component ) > $charsLeft ) {
131                    $component = '#' . md5( $component );
132                }
133
134                $charsLeft -= strlen( $component );
135                return $component;
136            },
137            $components
138        );
139
140        if ( $charsLeft < 0 ) {
141            return $keyspace . ':BagOStuff-long-key:##' . md5( implode( ':', $components ) );
142        }
143
144        return $keyspace . ':' . implode( ':', $components );
145    }
146
147    protected function requireConvertGenericKey(): bool {
148        return true;
149    }
150
151    public function doIncrWithInit( $key, $exptime, $step, $init, $flags ) {
152        // optimize with FIFO lock
153        if ( !wincache_lock( $key ) ) {
154            return false;
155        }
156
157        $curValue = $this->doGet( $key );
158        if ( $curValue === false ) {
159            $newValue = $this->doSet( $key, $init, $exptime ) ? $init : false;
160        } elseif ( $this->isInteger( $curValue ) ) {
161            $sum = max( $curValue + $step, 0 );
162            $oldTTL = wincache_ucache_info( false, $key )["ucache_entries"][1]["ttl_seconds"];
163            $newValue = $this->doSet( $key, $sum, $oldTTL ) ? $sum : false;
164        } else {
165            $newValue = false;
166        }
167
168        wincache_unlock( $key );
169
170        return $newValue;
171    }
172}
173
174/** @deprecated class alias since 1.43 */
175class_alias( WinCacheBagOStuff::class, 'WinCacheBagOStuff' );