Merge "Improve docs for Title::getInternalURL/getCanonicalURL"
[lhc/web/wiklou.git] / includes / libs / objectcache / APCUBagOStuff.php
index fb43d4d..0483ee7 100644 (file)
 /**
  * This is a wrapper for APCU's shared memory functions
  *
+ * Use PHP serialization to avoid bugs and easily create CAS tokens.
+ * APCu has a memory corruption bug when the serializer is set to 'default'.
+ * See T120267, and upstream bug reports:
+ *  - https://github.com/krakjoe/apcu/issues/38
+ *  - https://github.com/krakjoe/apcu/issues/35
+ *  - https://github.com/krakjoe/apcu/issues/111
+ *
  * @ingroup Cache
  */
-class APCUBagOStuff extends APCBagOStuff {
+class APCUBagOStuff extends BagOStuff {
        /**
-        * Available parameters are:
-        *   - nativeSerialize:     If true, pass objects to apcu_store(), and trust it
-        *                          to serialize them correctly. If false, serialize
-        *                          all values in PHP.
-        *
-        * @param array $params
+        * @var string String to append to each APC key. This may be changed
+        *  whenever the handling of values is changed, to prevent existing code
+        *  from encountering older values which it cannot handle.
         */
-       public function __construct( array $params = [] ) {
-               parent::__construct( $params );
-       }
+       const KEY_SUFFIX = ':3';
 
-       protected function doGet( $key, $flags = 0 ) {
-               return $this->getUnserialize(
-                       apcu_fetch( $key . self::KEY_SUFFIX )
-               );
+       protected function doGet( $key, $flags = 0, &$casToken = null ) {
+               $casToken = null;
+
+               $blob = apcu_fetch( $key . self::KEY_SUFFIX );
+               $value = $this->unserialize( $blob );
+               if ( $value !== false ) {
+                       $casToken = $blob; // don't bother hashing this
+               }
+
+               return $value;
        }
 
        public function set( $key, $value, $exptime = 0, $flags = 0 ) {
                apcu_store(
                        $key . self::KEY_SUFFIX,
-                       $this->setSerialize( $value ),
+                       $this->serialize( $value ),
                        $exptime
                );
 
                return true;
        }
 
+       public function add( $key, $value, $exptime = 0, $flags = 0 ) {
+               return apcu_add(
+                       $key . self::KEY_SUFFIX,
+                       $this->serialize( $value ),
+                       $exptime
+               );
+       }
+
        public function delete( $key, $flags = 0 ) {
                apcu_delete( $key . self::KEY_SUFFIX );
 
@@ -86,4 +102,12 @@ class APCUBagOStuff extends APCBagOStuff {
                        return false;
                }
        }
+
+       protected function serialize( $value ) {
+               return $this->isInteger( $value ) ? (int)$value : serialize( $value );
+       }
+
+       protected function unserialize( $value ) {
+               return $this->isInteger( $value ) ? (int)$value : unserialize( $value );
+       }
 }