Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}