Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • ActiveRecord
  • Cache
  • Configs
  • Core
  • DateTime
  • Debug
  • DeleteOperation
  • Errors
  • Event
  • EventHook
  • Events
  • FileCache
  • FormattedString
  • Helpers
  • I18n
  • Image
  • Inflections
  • Inflector
  • Models
  • Module
  • Modules
  • Object
  • Operation
  • PingOperation
  • Prototype
  • Route
  • Routes
  • SaveOperation
  • Session
  • TimeZone
  • TimeZoneLocation
  • Uploaded
  • Vars
  • VarsIterator

Interfaces

  • StorageInterface
  • ToArray
  • ToArrayRecursive

Traits

  • PrototypeTrait
  • ToArrayRecursiveTrait

Exceptions

  • AlreadyAuthenticated
  • AuthenticationRequired
  • Exception
  • ModuleConstructorMissing
  • ModuleIsDisabled
  • ModuleNotDefined
  • OffsetError
  • OffsetNotDefined
  • OffsetNotReadable
  • OffsetNotWritable
  • PermissionRequired
  • PropertyError
  • PropertyIsReserved
  • PropertyNotDefined
  • PropertyNotReadable
  • PropertyNotWritable
  • RouteNotDefined
  • SecurityException

Constants

  • TOKEN_ALPHA
  • TOKEN_ALPHA_UPCASE
  • TOKEN_NUMERIC
  • TOKEN_SYMBOL
  • TOKEN_SYMBOL_WIDE

Functions

  • array_flatten
  • array_insert
  • array_merge_recursive
  • camelize
  • capitalize
  • downcase
  • dump
  • escape
  • escape_all
  • exact_array_merge_recursive
  • excerpt
  • format
  • generate_token
  • generate_token_wide
  • generate_v4_uuid
  • get_autoconfig
  • humanize
  • hyphenate
  • log
  • log_error
  • log_info
  • log_success
  • log_time
  • normalize
  • normalize_namespace_part
  • normalize_url_path
  • pbkdf2
  • pluralize
  • remove_accents
  • shorten
  • singularize
  • sort_by_weight
  • stable_sort
  • strip_root
  • titleize
  • unaccent_compare
  • unaccent_compare_ci
  • underscore
  • upcase
  1 <?php
  2 
  3 /*
  4  * This file is part of the ICanBoogie package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace ICanBoogie;
 13 
 14 class FileCache
 15 {
 16     const T_COMPRESS = 'compress';
 17     const T_REPOSITORY = 'repository';
 18     const T_REPOSITORY_DELETE_RATIO = 'repository_delete_ratio';
 19     const T_REPOSITORY_SIZE = 'repository_size';
 20     const T_SERIALIZE = 'serialize';
 21     const T_MODIFIED_TIME = 'modified_time';
 22 
 23     public $compress = false;
 24     public $repository;
 25     public $repository_delete_ratio = .25;
 26     public $repository_size = 512;
 27     public $serialize = false;
 28     public $modified_time;
 29 
 30     protected $root;
 31 
 32     public function __construct(array $tags)
 33     {
 34         if (empty($tags[self::T_REPOSITORY]))
 35         {
 36             throw new Exception('The %tag tag is required', [ '%tag' => 'T_REPOSITORY' ]);
 37         }
 38 
 39         foreach ($tags as $tag => $value)
 40         {
 41             $this->$tag = $value;
 42         }
 43 
 44         if (strpos($this->repository, DOCUMENT_ROOT) === 0)
 45         {
 46             $this->repository = substr($this->repository, strlen(DOCUMENT_ROOT) - 1);
 47         }
 48 
 49         $this->root = realpath(\ICanBoogie\DOCUMENT_ROOT . $this->repository);
 50     }
 51 
 52     /**
 53      * Check if a file exists in the repository.
 54      *
 55      * If the file does not exists, it's created using the provided constructor.
 56      *
 57      * The working directory is changed to the repository during the process.
 58      *
 59      * @param string $file The name of the file in the repository.
 60      *
 61      * @param callable $constructor The constructor for the file.
 62      * The constructor is called with the cache object, the name of the file and the userdata.
 63      *
 64      * @param mixed $userdata Userdata that will be passed to the constructor.
 65      *
 66      * @return mixed The URL of the file. FALSE is the file failed to be created.
 67      */
 68 
 69     public function get($file, $constructor, $userdata=null)
 70     {
 71         if (!is_dir($this->root))
 72         {
 73             throw new Exception('The repository %repository does not exists.', [ '%repository' => $this->repository ], 404);
 74         }
 75 
 76         $location = getcwd();
 77 
 78         chdir($this->root);
 79 
 80         if (!is_file($file) || ($this->modified_time && $this->modified_time > filemtime($file)))
 81         {
 82             $file = call_user_func($constructor, $this, $file, $userdata);
 83         }
 84 
 85         chdir($location);
 86 
 87         return $file ? $this->repository . '/' . $file : $file;
 88     }
 89 
 90     public function exists($key)
 91     {
 92         $location = getcwd();
 93 
 94         chdir($this->root);
 95 
 96         $rc = file_exists($key);
 97 
 98         chdir($location);
 99 
100         return $rc;
101     }
102 
103     /**
104      * Load cached contents.
105      *
106      * If the content is not cached, the constructor is called to create the content.
107      * The content generated by the constructor is save to the cache.
108      *
109      * @param string $key Key for the value in the cache.
110      * @param callable $constructor Constructor callback. The constructor is called to
111      * generated the contents of the file. The constructor is called with the FileCache
112      * object, the @file and the @userdata as arguments.
113      * @param mixed $userdata User data that is passed as is to the constructor.
114      *
115      * @return mixed The contents of the file
116      */
117     public function load($key, $constructor, $userdata=null)
118     {
119         #
120         # if the repository does not exists we simply return the contents
121         # created by the constructor.
122         #
123 
124         if (!is_dir($this->root))
125         {
126             throw new Exception('The repository %repository does not exists.', [ '%repository' => $this->repository ], 404);
127 
128             return call_user_func($contructor, $userdata, $this, $key);
129         }
130 
131         #
132         #
133         #
134 
135         $location = getcwd();
136 
137         chdir($this->root);
138 
139         $contents = null;
140 
141         if (is_readable($key))
142         {
143             $contents = file_get_contents($key);
144 
145             if ($this->compress)
146             {
147                 $contents = gzinflate($contents);
148             }
149 
150             if ($this->serialize)
151             {
152                 $contents = unserialize($contents);
153             }
154         }
155 
156         if ($contents === null)
157         {
158             $contents = call_user_func($constructor, $userdata, $this, $key);
159 
160             $this->save($key, $contents);
161         }
162 
163         chdir($location);
164 
165         return $contents;
166     }
167 
168     /**
169      * Save contents to a cached file.
170      *
171      * @param $file string Name of the file.
172      * @param $contents mixed The contents to write.
173      *
174      * @return int Return value from @file_put_contents()
175      */
176     protected function save($file, $contents)
177     {
178         if (!is_writable($this->root))
179         {
180             throw new Exception('The repository %repository is not writable.', [ '%repository' => $this->repository ]);
181         }
182 
183         $location = getcwd();
184 
185         chdir($this->root);
186 
187         if ($this->serialize)
188         {
189             $contents = serialize($contents);
190         }
191 
192         if ($this->compress)
193         {
194             $contents = gzdeflate($contents);
195         }
196 
197         $rc = file_put_contents($file, $contents, LOCK_EX);
198 
199         chdir($location);
200 
201         return $rc;
202     }
203 
204     public function store($key, $data)
205     {
206         return $this->save($key, $data);
207     }
208 
209     public function retrieve($key)
210     {
211         $location = getcwd();
212 
213         chdir($this->root);
214 
215         $rc = file_get_contents($key);
216 
217         chdir($location);
218 
219         return $rc;
220     }
221 
222     public function delete($file)
223     {
224         return $this->unlink([ $file => true ]);
225     }
226 
227     /**
228      * Read to repository and return an array of files.
229      *
230      * Each entry in the array is made up using the _ctime_ and _size_ of the file. The
231      * key of the entry is the file name.
232      *
233      * @return unknown_type
234      */
235 
236     protected function read()
237     {
238         $root = $this->root;
239 
240         if (!is_dir($root))
241         {
242             //Debug::trigger('%repository is not a directory', [ '%repository' => $this->repository ]);
243 
244             return false;
245         }
246 
247         try
248         {
249             $dir = new \DirectoryIterator($root);
250         }
251         catch (\UnexpectedValueException $e)
252         {
253             throw new Exception('Unable to open directory %root', [ '%root' => $root ]);
254         }
255 
256         #
257         # create file list, with the filename as key and ctime and size as value.
258         # we set the ctime first to be able to sort the file by ctime when necessary.
259         #
260 
261         $files = [];
262 
263         foreach ($dir as $file)
264         {
265             if (!$file->isDot())
266             {
267                 $files[$file->getFilename()] = [ $file->getCTime(), $file->getSize() ];
268             }
269         }
270 
271         return $files;
272     }
273 
274     protected function unlink($files)
275     {
276         if (!$files)
277         {
278             return;
279         }
280 
281         #
282         # change the working directory to the repository
283         #
284 
285         $location = getcwd();
286 
287         chdir($this->root);
288 
289         #
290         # obtain exclusive lock to delete files
291         #
292 
293         $lh = fopen('.lock', 'w+');
294 
295         if (!$lh)
296         {
297             Debug::trigger('Unable to lock %repository', [ '%repository' => $this->repository ]);
298 
299             chdir($location);
300 
301             return;
302         }
303 
304         #
305         # We will try $n time to obtain the exclusive lock
306         #
307 
308         $n = 10;
309 
310         while (!flock($lh, LOCK_EX | LOCK_NB))
311         {
312             #
313             # If the lock is not obtained we sleep for 0 to 100 milliseconds.
314             # We sleep to avoid CPU load, and we sleep for a random time
315             # to avoid collision.
316             #
317 
318             usleep(round(rand(0, 100) * 1000));
319 
320             if (!--$n)
321             {
322                 #
323                 # We were unable to obtain the lock in time.
324                 # We exit silently.
325                 #
326 
327                 chdir($location);
328 
329                 return;
330             }
331         }
332 
333         #
334         # The lock was obtained, we can now delete the files
335         #
336 
337         foreach ($files as $file => $dummy)
338         {
339             #
340             # Because of concurrent access, the file might have already been deleted.
341             # We have to check if the file still exists before calling unlink()
342             #
343 
344             if (!file_exists($file))
345             {
346                 continue;
347             }
348 
349             unlink($file);
350         }
351 
352         chdir($location);
353 
354         #
355         # and release the lock.
356         #
357 
358         fclose($lh);
359     }
360 
361     /**
362      *
363      * Clear all the files in the repository.
364      *
365      */
366 
367     public function clear()
368     {
369         $files = $this->read();
370 
371         return $this->unlink($files);
372     }
373 
374     /**
375      *
376      * Clean the repository according to the size and time rules.
377      *
378      */
379 
380     public function clean()
381     {
382         $files = $this->read();
383 
384         if (!$files)
385         {
386             return;
387         }
388 
389         $totalsize = 0;
390 
391         foreach ($files as $stat)
392         {
393             $totalsize += $stat[1];
394         }
395 
396         $repository_size = $this->repository_size * 1024;
397 
398         if ($totalsize < $repository_size)
399         {
400             #
401             # There is enough space in the repository. We don't need to delete any file.
402             #
403 
404             return;
405         }
406 
407         #
408         # The repository is completely full, we need to make some space.
409         # We create an array with the files to delete. Files are added until
410         # the delete ratio is reached.
411         #
412 
413         asort($files);
414 
415         $deletesize = $repository_size * $this->repository_delete_ratio;
416 
417         $i = 0;
418 
419         foreach ($files as $file => $stat)
420         {
421             $i++;
422 
423             $deletesize -= $stat[1];
424 
425             if ($deletesize < 0)
426             {
427                 break;
428             }
429         }
430 
431         $files = array_slice($files, 0, $i);
432 
433         return $this->unlink($files);
434     }
435 }
Autodoc API documentation generated by ApiGen 2.8.0