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