FRED™  3.0
FRED™: Framework for Rapid and Easy Development
File.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Rsi\Fred;
4 
5 /**
6  * File storage and archiving.
7  */
8 class File extends Component{
9 
10  const EVENT_ARCHIVE = 'file:archive';
11  const EVENT_SAVE = 'file:save';
12  const EVENT_DELETE = 'file:delete';
13 
14  public $mode = null; //!< File mode (not set if empty).
15 
16  protected $_indexBase = 1; //!< Index starting value.
17  protected $_indexSize = 0; //!< Minimum index length (prefixed with zeros when smaller).
18  protected $_types = []; //!< Key = type name, value = array with 'format' and optional 'archive' format. An asterisk is
19  // replaced with the ID. A quotation mark is replaced with the index. Placeholder "(x,y)" indicates a part of the ID,
20  // starting on offset "x" (negative denotes from end) and with length "y" (optionally left padded when the ID is actually
21  // smaller than the length). Placeholder "[z]" will be replaced with the corresponding date tag for "z".
22 
23  protected function format($format,$id,$index = null,$time = null){
24  return \Rsi\Str::replaceDate(strtr(preg_replace_callback('/\\((-?\\d+),(\\d+)\\)/',function($match) use ($id){
25  return \Rsi\Str::pad(substr($id,$match[1],$match[2]),$match[2]);
26  },$format),['*' => $id,'?' => $index === null ? null : \Rsi\Str::pad($index,$this->_indexSize)]),$time);
27  }
28  /**
29  * Filename for a file.
30  * @param string $type File type.
31  * @param string $id File ID.
32  * @return string Filename.
33  */
34  public function name($type,$id){
35  return $this->format($this->_types[$type]['format'],$id);
36  }
37  /**
38  * Check if a file exists.
39  * @param string $type File type.
40  * @param string $id File ID.
41  * @return bool True if the file exists.
42  */
43  public function exists($type,$id){
44  return is_file($this->name($type,$id));
45  }
46  /**
47  * Archive a file (if it exists).
48  * @param string $type File type.
49  * @param string $id File ID.
50  * @param string $filename Filename for the current file.
51  * @return string Archive filename (null if archive not set; false if no current file or error).
52  */
53  protected function archive($type,$id,&$filename = null){
54  $filename = $this->name($type,$id);
55  if($format = $this->_types[$type]['archive'] ?? null){
56  if(!is_file($filename)) return false;
57  $index = strpos($format,'?') === null ? null : $this->_indexBase;
58  do $archive = $this->format($format,$id,$index === null ? null : $index++);
59  while(($index !== null) && is_file($archive));
60  \Rsi\File::mkdir(dirname($archive));
61  if($result = \Rsi\File::rename($filename,$archive))
62  $this->component('event')->trigger(self::EVENT_ARCHIVE,$this,$type,$id,$filename,$archive);
63  return $result ? $archive : false;
64  }
65  return null;
66  }
67  /**
68  * Save date into a file (archive existing file first).
69  * @param string $type File type.
70  * @param string $id File ID.
71  * @param string $data Data to save.
72  * @return int Number of bytes written or false on error.
73  */
74  public function save($type,$id,$data){
75  $this->archive($type,$id,$filename);
76  \Rsi\File::mkdir(dirname($filename));
77  if(($result = \Rsi\File::write($filename,$data,$this->mode)) !== false)
78  $this->component('event')->trigger(self::EVENT_SAVE,$this,$type,$id,$filename);
79  return $result;
80  }
81  /**
82  * Delete a file (archive first).
83  * @param string $type File type.
84  * @param string $id File ID.
85  * @return bool True if file does not exist afterwards.
86  */
87  public function delete($type,$id){
88  $this->archive($type,$id,$filename);
89  if($result = \Rsi\File::unlink($filename)) $this->component('event')->trigger(self::EVENT_DELETE,$this,$type,$id);
90  return $result;
91  }
92  /**
93  * Find archive files in a directory.
94  * @param string $dir Directory to scan.
95  * @param array $dirs Directory filters for sub-directories.
96  * @param int $time Only select files older than this timestamp (empty = no filter).
97  * @return array Key = full name, value = info
98  */
99  protected function find($dir,$dirs,$time = null){
100  $archives = [];
101  if($filter = array_shift($dirs)){
102  $filter = [\Rsi\File::FIND_FILTER_NAME . '//' => $filter];
103  if(!$dirs) $archives += \Rsi\File::find($dir,
104  [\Rsi\File::FIND_FILTER_TYPE => \Rsi\File::FIND_TYPE_FILE] +
105  $filter +
106  ($time ? [\Rsi\File::FIND_FILTER_TIME . '<' => $time] : [])
107  );
108  elseif($subs = \Rsi\File::find($dir,[\Rsi\File::FIND_FILTER_TYPE => \Rsi\File::FIND_TYPE_DIR] + $filter))
109  foreach($subs as $sub => $info) $archives += $this->find($sub,$dirs,$time);
110  }
111  return $archives;
112  }
113  /**
114  * Find archive files.
115  * @param string $type File type.
116  * @param string $id File ID.
117  * @param int $time Only select files older than this timestamp (empty = no filter).
118  * @return array Archive files found (null if archive not set).
119  */
120  public function archives($type,$id,$time = null){
121  if($format = $this->_types[$type]['archive'] ?? null){
122  $root = $dirs = [];
123  foreach(preg_split('/[\\/\\\\]/',str_replace('*',$id ?: '*',$format)) as $dir){
124  if(!$dirs && !preg_match('/[\\(\\[\\?\\*]/',$dir)) $root[] = $dir;
125  else{
126  $dir = preg_quote($dir,'/');
127  $dir = preg_replace('/\\\\\\(.*?,(\\d+)\\\\\\)/','.{$1}',$dir);
128  $dir = preg_replace('/\\\\\\[.*?\\\\\\]/','\\w+?',$dir);
129  $dir = strtr($dir,['\\?' => '\\d+','\\*' => '.*?']);
130  $dirs[] = '/^' . $dir . '$/';
131  }
132  }
133  return array_keys($this->find(implode('/',$root),$dirs,$time));
134  }
135  return null;
136  }
137  /**
138  * Purge the archive.
139  * @param string $type File type.
140  * @param int $time Purge files older than this timestamp.
141  * @return int Number if files purged (null if archive not set).
142  */
143  public function purge($type,$time){
144  if(($archives = $this->archives($type,null,$time)) === null) return null;
145  $count = 0;
146  foreach($archives as $archive) if(\Rsi\File::unlink($archive)) $count++;
147  return $count;
148  }
149  /**
150  * Purge archive for all types with an optional "purge" parameter.
151  * @return int Number if files purged.
152  */
153  public function purgeAll(){
154  $count = 0;
155  foreach($this->_types as $type => $config) if($time = $config['purge'] ?? null) $count += $this->purge($type,$time);
156  return $count;
157  }
158 
159 }
Rsi
Rsi\Fred\File\purgeAll
purgeAll()
Purge archive for all types with an optional "purge" parameter.
Definition: File.php:153
Rsi\Fred\File\save
save($type, $id, $data)
Save date into a file (archive existing file first).
Definition: File.php:74
Rsi\Fred\File\EVENT_DELETE
const EVENT_DELETE
Definition: File.php:12
Rsi\Fred\File\purge
purge($type, $time)
Purge the archive.
Definition: File.php:143
Rsi\Fred\File\archive
archive($type, $id, &$filename=null)
Archive a file (if it exists).
Definition: File.php:53
Rsi\Fred\File\name
name($type, $id)
Filename for a file.
Definition: File.php:34
Rsi\Fred\Component
Basic component class.
Definition: Component.php:8
Rsi\Fred\File\find
find($dir, $dirs, $time=null)
Find archive files in a directory.
Definition: File.php:99
Rsi\Fred\File\$_indexBase
$_indexBase
Index starting value.
Definition: File.php:16
Rsi\Fred\Controller\Widget\File
Definition: File.php:7
Rsi\Fred\Component\component
component($name)
Get a component (local or default).
Definition: Component.php:81
Rsi\Fred\File\archives
archives($type, $id, $time=null)
Find archive files.
Definition: File.php:120
Rsi\Fred\File\$_indexSize
$_indexSize
Minimum index length (prefixed with zeros when smaller).
Definition: File.php:17
Rsi\Fred\File\$mode
$mode
File mode (not set if empty).
Definition: File.php:14
Rsi\Fred\File\EVENT_SAVE
const EVENT_SAVE
Definition: File.php:11
Rsi\Fred\File\$_types
$_types
Key = type name, value = array with 'format' and optional 'archive' format. An asterisk is.
Definition: File.php:18
Rsi\Fred\File\EVENT_ARCHIVE
const EVENT_ARCHIVE
Definition: File.php:10
Rsi\Fred\File\format
format($format, $id, $index=null, $time=null)
Definition: File.php:23
Rsi\Fred\File\exists
exists($type, $id)
Check if a file exists.
Definition: File.php:43
Rsi\Fred
Definition: Alive.php:3