CRM_Core_BAO_Cache - Load/store binary data in cache (via base64)
authorTim Otten <totten@civicrm.org>
Fri, 22 Jun 2018 03:05:04 +0000 (20:05 -0700)
committerTim Otten <totten@civicrm.org>
Fri, 22 Jun 2018 03:22:19 +0000 (20:22 -0700)
CRM/Core/BAO/Cache.php
tests/phpunit/CRM/Core/BAO/CacheTest.php

index 0e619143e33a830c4623ac49657158bff82a32c7..be605afc590633169d29f493baf29aed36527a90 100644 (file)
@@ -71,7 +71,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
         $table = self::getTableName();
         $where = self::whereCache($group, $path, $componentID);
         $rawData = CRM_Core_DAO::singleValueQuery("SELECT data FROM $table WHERE $where");
-        $data = $rawData ? unserialize($rawData) : NULL;
+        $data = $rawData ? self::decode($rawData) : NULL;
 
         self::$_cache[$argString] = $data;
         $cache->set($argString, self::$_cache[$argString]);
@@ -107,7 +107,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
 
         $result = array();
         while ($dao->fetch()) {
-          $result[$dao->path] = unserialize($dao->data);
+          $result[$dao->path] = self::decode($dao->data);
         }
         $dao->free();
 
@@ -148,7 +148,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
     $where = self::whereCache($group, $path, $componentID);
     $dataExists = CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM $table WHERE {$where}");
     $now = date('Y-m-d H:i:s'); // FIXME - Use SQL NOW() or CRM_Utils_Time?
-    $dataSerialized = serialize($data);
+    $dataSerialized = self::encode($data);
 
     // This table has a wonky index, so we cannot use REPLACE or
     // "INSERT ... ON DUPE". Instead, use SELECT+(INSERT|UPDATE).
@@ -180,7 +180,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
 
     $argString = "CRM_CT_{$group}_{$path}_{$componentID}";
     $cache = CRM_Utils_Cache::singleton();
-    $data = unserialize($dataSerialized);
+    $data = self::decode($dataSerialized);
     self::$_cache[$argString] = $data;
     $cache->set($argString, $data);
 
@@ -365,6 +365,33 @@ AND         created_date < date_sub( NOW( ), INTERVAL $timeIntervalDays DAY )
     }
   }
 
+  /**
+   * (Quasi-private) Encode an object/array/string/int as a string.
+   *
+   * @param $mixed
+   * @return string
+   */
+  public static function encode($mixed) {
+    return base64_encode(serialize($mixed));
+  }
+
+  /**
+   * (Quasi-private) Decode an object/array/string/int from a string.
+   *
+   * @param $string
+   * @return mixed
+   */
+  public static function decode($string) {
+    // Upgrade support -- old records (serialize) always have this punctuation,
+    // and new records (base64) never do.
+    if (strpos($string, ':') !== FALSE || strpos($string, ';') !== FALSE) {
+      return unserialize($string);
+    }
+    else {
+      return unserialize(base64_decode($string));
+    }
+  }
+
   /**
    * Compose a SQL WHERE clause for the cache.
    *
index d29193e02ac6352249dfcb324dee260288dd6637..ac181236cb75888a18a02f7da6cf67cf49eca759 100644 (file)
  */
 class CRM_Core_BAO_CacheTest extends CiviUnitTestCase {
 
+  public function testMultiVersionDecode() {
+    $encoders = ['serialize', ['CRM_Core_BAO_Cache', 'encode']];
+    $values = [NULL, 0, 1, TRUE, FALSE, [], ['abcd'], 'ab;cd', new stdClass()];
+    foreach ($encoders as $encoder) {
+      foreach ($values as $value) {
+        $encoded = $encoder($value);
+        $decoded = CRM_Core_BAO_Cache::decode($encoded);
+        $this->assertEquals($value, $decoded, "Failure encoding/decoding value " . var_export($value, 1) . ' with ' . var_export($encoder, 1));
+      }
+    }
+  }
+
   public function exampleValues() {
     $binary = '';
     for ($i = 0; $i < 256; $i++) {