Differences Between: [Versions 311 and 402] [Versions 311 and 403]
1 <?php 2 /* 3 * Copyright 2008 Google Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 if (!class_exists('Google_Client')) { 19 require_once dirname(__FILE__) . '/../autoload.php'; 20 } 21 22 /* 23 * This class implements a basic on disk storage. While that does 24 * work quite well it's not the most elegant and scalable solution. 25 * It will also get you into a heap of trouble when you try to run 26 * this in a clustered environment. 27 * 28 * @author Chris Chabot <chabotc@google.com> 29 */ 30 class Google_Cache_File extends Google_Cache_Abstract 31 { 32 const MAX_LOCK_RETRIES = 10; 33 private $path; 34 private $fh; 35 36 /** 37 * @var Google_Client the current client 38 */ 39 private $client; 40 41 public function __construct(Google_Client $client) 42 { 43 $this->client = $client; 44 $this->path = $this->client->getClassConfig($this, 'directory'); 45 } 46 47 public function get($key, $expiration = false) 48 { 49 $storageFile = $this->getCacheFile($key); 50 $data = false; 51 52 if (!file_exists($storageFile)) { 53 $this->client->getLogger()->debug( 54 'File cache miss', 55 array('key' => $key, 'file' => $storageFile) 56 ); 57 return false; 58 } 59 60 if ($expiration) { 61 $mtime = filemtime($storageFile); 62 if ((time() - $mtime) >= $expiration) { 63 $this->client->getLogger()->debug( 64 'File cache miss (expired)', 65 array('key' => $key, 'file' => $storageFile) 66 ); 67 $this->delete($key); 68 return false; 69 } 70 } 71 72 if ($this->acquireReadLock($storageFile)) { 73 if (filesize($storageFile) > 0) { 74 $data = fread($this->fh, filesize($storageFile)); 75 $data = unserialize($data); 76 } else { 77 $this->client->getLogger()->debug( 78 'Cache file was empty', 79 array('file' => $storageFile) 80 ); 81 } 82 $this->unlock($storageFile); 83 } 84 85 $this->client->getLogger()->debug( 86 'File cache hit', 87 array('key' => $key, 'file' => $storageFile, 'var' => $data) 88 ); 89 90 return $data; 91 } 92 93 public function set($key, $value) 94 { 95 $storageFile = $this->getWriteableCacheFile($key); 96 if ($this->acquireWriteLock($storageFile)) { 97 // We serialize the whole request object, since we don't only want the 98 // responseContent but also the postBody used, headers, size, etc. 99 $data = serialize($value); 100 $result = fwrite($this->fh, $data); 101 $this->unlock($storageFile); 102 103 $this->client->getLogger()->debug( 104 'File cache set', 105 array('key' => $key, 'file' => $storageFile, 'var' => $value) 106 ); 107 } else { 108 $this->client->getLogger()->notice( 109 'File cache set failed', 110 array('key' => $key, 'file' => $storageFile) 111 ); 112 } 113 } 114 115 public function delete($key) 116 { 117 $file = $this->getCacheFile($key); 118 if (file_exists($file) && !unlink($file)) { 119 $this->client->getLogger()->error( 120 'File cache delete failed', 121 array('key' => $key, 'file' => $file) 122 ); 123 throw new Google_Cache_Exception("Cache file could not be deleted"); 124 } 125 126 $this->client->getLogger()->debug( 127 'File cache delete', 128 array('key' => $key, 'file' => $file) 129 ); 130 } 131 132 private function getWriteableCacheFile($file) 133 { 134 return $this->getCacheFile($file, true); 135 } 136 137 private function getCacheFile($file, $forWrite = false) 138 { 139 return $this->getCacheDir($file, $forWrite) . '/' . md5($file); 140 } 141 142 private function getCacheDir($file, $forWrite) 143 { 144 // use the first 2 characters of the hash as a directory prefix 145 // this should prevent slowdowns due to huge directory listings 146 // and thus give some basic amount of scalability 147 $storageDir = $this->path . '/' . substr(md5($file), 0, 2); 148 if ($forWrite && ! is_dir($storageDir)) { 149 if (! mkdir($storageDir, 0700, true)) { 150 $this->client->getLogger()->error( 151 'File cache creation failed', 152 array('dir' => $storageDir) 153 ); 154 throw new Google_Cache_Exception("Could not create storage directory: $storageDir"); 155 } 156 } 157 return $storageDir; 158 } 159 160 private function acquireReadLock($storageFile) 161 { 162 return $this->acquireLock(LOCK_SH, $storageFile); 163 } 164 165 private function acquireWriteLock($storageFile) 166 { 167 $rc = $this->acquireLock(LOCK_EX, $storageFile); 168 if (!$rc) { 169 $this->client->getLogger()->notice( 170 'File cache write lock failed', 171 array('file' => $storageFile) 172 ); 173 $this->delete($storageFile); 174 } 175 return $rc; 176 } 177 178 private function acquireLock($type, $storageFile) 179 { 180 $mode = $type == LOCK_EX ? "w" : "r"; 181 $this->fh = fopen($storageFile, $mode); 182 if (!$this->fh) { 183 $this->client->getLogger()->error( 184 'Failed to open file during lock acquisition', 185 array('file' => $storageFile) 186 ); 187 return false; 188 } 189 if ($type == LOCK_EX) { 190 chmod($storageFile, 0600); 191 } 192 $count = 0; 193 while (!flock($this->fh, $type | LOCK_NB)) { 194 // Sleep for 10ms. 195 usleep(10000); 196 if (++$count < self::MAX_LOCK_RETRIES) { 197 return false; 198 } 199 } 200 return true; 201 } 202 203 public function unlock($storageFile) 204 { 205 if ($this->fh) { 206 flock($this->fh, LOCK_UN); 207 } 208 } 209 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body