Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 #[AllowDynamicProperties] 31 class Google_Cache_File extends Google_Cache_Abstract 32 { 33 const MAX_LOCK_RETRIES = 10; 34 private $path; 35 private $fh; 36 37 /** 38 * @var Google_Client the current client 39 */ 40 private $client; 41 42 public function __construct(Google_Client $client) 43 { 44 $this->client = $client; 45 $this->path = $this->client->getClassConfig($this, 'directory'); 46 } 47 48 public function get($key, $expiration = false) 49 { 50 $storageFile = $this->getCacheFile($key); 51 $data = false; 52 53 if (!file_exists($storageFile)) { 54 $this->client->getLogger()->debug( 55 'File cache miss', 56 array('key' => $key, 'file' => $storageFile) 57 ); 58 return false; 59 } 60 61 if ($expiration) { 62 $mtime = filemtime($storageFile); 63 if ((time() - $mtime) >= $expiration) { 64 $this->client->getLogger()->debug( 65 'File cache miss (expired)', 66 array('key' => $key, 'file' => $storageFile) 67 ); 68 $this->delete($key); 69 return false; 70 } 71 } 72 73 if ($this->acquireReadLock($storageFile)) { 74 if (filesize($storageFile) > 0) { 75 $data = fread($this->fh, filesize($storageFile)); 76 $data = unserialize($data); 77 } else { 78 $this->client->getLogger()->debug( 79 'Cache file was empty', 80 array('file' => $storageFile) 81 ); 82 } 83 $this->unlock($storageFile); 84 } 85 86 $this->client->getLogger()->debug( 87 'File cache hit', 88 array('key' => $key, 'file' => $storageFile, 'var' => $data) 89 ); 90 91 return $data; 92 } 93 94 public function set($key, $value) 95 { 96 $storageFile = $this->getWriteableCacheFile($key); 97 if ($this->acquireWriteLock($storageFile)) { 98 // We serialize the whole request object, since we don't only want the 99 // responseContent but also the postBody used, headers, size, etc. 100 $data = serialize($value); 101 $result = fwrite($this->fh, $data); 102 $this->unlock($storageFile); 103 104 $this->client->getLogger()->debug( 105 'File cache set', 106 array('key' => $key, 'file' => $storageFile, 'var' => $value) 107 ); 108 } else { 109 $this->client->getLogger()->notice( 110 'File cache set failed', 111 array('key' => $key, 'file' => $storageFile) 112 ); 113 } 114 } 115 116 public function delete($key) 117 { 118 $file = $this->getCacheFile($key); 119 if (file_exists($file) && !unlink($file)) { 120 $this->client->getLogger()->error( 121 'File cache delete failed', 122 array('key' => $key, 'file' => $file) 123 ); 124 throw new Google_Cache_Exception("Cache file could not be deleted"); 125 } 126 127 $this->client->getLogger()->debug( 128 'File cache delete', 129 array('key' => $key, 'file' => $file) 130 ); 131 } 132 133 private function getWriteableCacheFile($file) 134 { 135 return $this->getCacheFile($file, true); 136 } 137 138 private function getCacheFile($file, $forWrite = false) 139 { 140 return $this->getCacheDir($file, $forWrite) . '/' . md5($file); 141 } 142 143 private function getCacheDir($file, $forWrite) 144 { 145 // use the first 2 characters of the hash as a directory prefix 146 // this should prevent slowdowns due to huge directory listings 147 // and thus give some basic amount of scalability 148 $storageDir = $this->path . '/' . substr(md5($file), 0, 2); 149 if ($forWrite && ! is_dir($storageDir)) { 150 if (! mkdir($storageDir, 0700, true)) { 151 $this->client->getLogger()->error( 152 'File cache creation failed', 153 array('dir' => $storageDir) 154 ); 155 throw new Google_Cache_Exception("Could not create storage directory: $storageDir"); 156 } 157 } 158 return $storageDir; 159 } 160 161 private function acquireReadLock($storageFile) 162 { 163 return $this->acquireLock(LOCK_SH, $storageFile); 164 } 165 166 private function acquireWriteLock($storageFile) 167 { 168 $rc = $this->acquireLock(LOCK_EX, $storageFile); 169 if (!$rc) { 170 $this->client->getLogger()->notice( 171 'File cache write lock failed', 172 array('file' => $storageFile) 173 ); 174 $this->delete($storageFile); 175 } 176 return $rc; 177 } 178 179 private function acquireLock($type, $storageFile) 180 { 181 $mode = $type == LOCK_EX ? "w" : "r"; 182 $this->fh = fopen($storageFile, $mode); 183 if (!$this->fh) { 184 $this->client->getLogger()->error( 185 'Failed to open file during lock acquisition', 186 array('file' => $storageFile) 187 ); 188 return false; 189 } 190 if ($type == LOCK_EX) { 191 chmod($storageFile, 0600); 192 } 193 $count = 0; 194 while (!flock($this->fh, $type | LOCK_NB)) { 195 // Sleep for 10ms. 196 usleep(10000); 197 if (++$count < self::MAX_LOCK_RETRIES) { 198 return false; 199 } 200 } 201 return true; 202 } 203 204 public function unlock($storageFile) 205 { 206 if ($this->fh) { 207 flock($this->fh, LOCK_UN); 208 } 209 } 210 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body