Development

/branches/1.2/lib/task/sfFilesystem.class.php

You must first sign up to be able to contribute.

root/branches/1.2/lib/task/sfFilesystem.class.php

Revision 27816, 9.9 kB (checked in by FabianLange, 4 years ago)

[1.2, 1.3, 1.4] fixed invalid sprintf syntax in exception (closes #8256)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * sfFilesystem provides basic utility to manipulate the file system.
13  *
14  * @package    symfony
15  * @subpackage util
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @version    SVN: $Id$
18  */
19 class sfFilesystem
20 {
21   protected
22     $dispatcher = null,
23     $formatter  = null;
24
25   /**
26    * Constructor.
27    *
28    * @param sfEventDispatcher $dispatcher  An sfEventDispatcher instance
29    * @param sfFormatter       $formatter   An sfFormatter instance
30    */
31   public function __construct(sfEventDispatcher $dispatcher = null, sfFormatter $formatter = null)
32   {
33     $this->dispatcher = $dispatcher;
34     $this->formatter = $formatter;
35   }
36
37   /**
38    * Copies a file.
39    *
40    * This method only copies the file if the origin file is newer than the target file.
41    *
42    * By default, if the target already exists, it is not overriden.
43    *
44    * To override existing files, pass the "override" option.
45    *
46    * @param string $originFile  The original filename
47    * @param string $targetFile  The target filename
48    * @param array  $options     An array of options
49    */
50   public function copy($originFile, $targetFile, $options = array())
51   {
52     if (!array_key_exists('override', $options))
53     {
54       $options['override'] = false;
55     }
56
57     // we create target_dir if needed
58     if (!is_dir(dirname($targetFile)))
59     {
60       $this->mkdirs(dirname($targetFile));
61     }
62
63     $mostRecent = false;
64     if (file_exists($targetFile))
65     {
66       $statTarget = stat($targetFile);
67       $stat_origin = stat($originFile);
68       $mostRecent = ($stat_origin['mtime'] > $statTarget['mtime']) ? true : false;
69     }
70
71     if ($options['override'] || !file_exists($targetFile) || $mostRecent)
72     {
73       $this->logSection('file+', $targetFile);
74       copy($originFile, $targetFile);
75     }
76   }
77
78   /**
79    * Creates a directory recursively.
80    *
81    * @param  string $path  The directory path
82    * @param  int    $mode  The directory mode
83    *
84    * @return bool true if the directory has been created, false otherwise
85    */
86   public function mkdirs($path, $mode = 0777)
87   {
88     if (is_dir($path))
89     {
90       return true;
91     }
92
93     $this->logSection('dir+', $path);
94
95     return @mkdir($path, $mode, true);
96   }
97
98   /**
99    * Creates empty files.
100    *
101    * @param mixed $files  The filename, or an array of filenames
102    */
103   public function touch($files)
104   {
105     if (!is_array($files))
106     {
107       $files = array($files);
108     }
109
110     foreach ($files as $file)
111     {
112       $this->logSection('file+', $file);
113
114       touch($file);
115     }
116   }
117
118   /**
119    * Removes files or directories.
120    *
121    * @param mixed $files  A filename or an array of files to remove
122    */
123   public function remove($files)
124   {
125     if (!is_array($files))
126     {
127       $files = array($files);
128     }
129
130     $files = array_reverse($files);
131     foreach ($files as $file)
132     {
133       if (is_dir($file) && !is_link($file))
134       {
135         $this->logSection('dir-', $file);
136
137         rmdir($file);
138       }
139       else
140       {
141         $this->logSection(is_link($file) ? 'link-' : 'file-', $file);
142
143         unlink($file);
144       }
145     }
146   }
147
148   /**
149    * Change mode for an array of files or directories.
150    *
151    * @param array   $files  An array of files or directories
152    * @param integer $mode   The new mode
153    * @param integer $umask  The mode mask (octal)
154    */
155   public function chmod($files, $mode, $umask = 0000)
156   {
157     $currentUmask = umask();
158     umask($umask);
159
160     if (!is_array($files))
161     {
162       $files = array($files);
163     }
164
165     foreach ($files as $file)
166     {
167       $this->logSection(sprintf('chmod %o', $mode), $file);
168       chmod($file, $mode);
169     }
170
171     umask($currentUmask);
172   }
173
174   /**
175    * Renames a file.
176    *
177    * @param string $origin  The origin filename
178    * @param string $target  The new filename
179    */
180   public function rename($origin, $target)
181   {
182     // we check that target does not exist
183     if (is_readable($target))
184     {
185       throw new sfException(sprintf('Cannot rename because the target "%s" already exist.', $target));
186     }
187
188     $this->logSection('rename', $origin.' > '.$target);
189     rename($origin, $target);
190   }
191
192   /**
193    * Creates a symbolic link or copy a directory.
194    *
195    * @param string $originDir      The origin directory path
196    * @param string $targetDir      The symbolic link name
197    * @param bool   $copyOnWindows  Whether to copy files if on windows
198    */
199   public function symlink($originDir, $targetDir, $copyOnWindows = false)
200   {
201     if (!function_exists('symlink') && $copyOnWindows)
202     {
203       $finder = sfFinder::type('any');
204       $this->mirror($originDir, $targetDir, $finder);
205       return;
206     }
207
208     $ok = false;
209     if (is_link($targetDir))
210     {
211       if (readlink($targetDir) != $originDir)
212       {
213         unlink($targetDir);
214       }
215       else
216       {
217         $ok = true;
218       }
219     }
220
221     if (!$ok)
222     {
223       $this->logSection('link+', $targetDir);
224       symlink($originDir, $targetDir);
225     }
226   }
227
228   /**
229    * Creates a symbolic link using a relative path if possible.
230    *
231    * @param string $originDir      The origin directory path
232    * @param string $targetDir      The symbolic link name
233    * @param bool   $copyOnWindows  Whether to copy files if on windows
234    */
235   public function relativeSymlink($originDir, $targetDir, $copyOnWindows = false)
236   {
237     if (function_exists('symlink') || !$copyOnWindows)
238     {
239       $originDir = $this->calculateRelativeDir($targetDir, $originDir);
240     }
241     $this->symlink($originDir, $targetDir, $copyOnWindows);
242   }
243
244   /**
245    * Mirrors a directory to another.
246    *
247    * @param string   $originDir  The origin directory
248    * @param string   $targetDir  The target directory
249    * @param sfFinder $finder     An sfFinder instance
250    * @param array    $options    An array of options (see copy())
251    */
252   public function mirror($originDir, $targetDir, $finder, $options = array())
253   {
254     foreach ($finder->relative()->in($originDir) as $file)
255     {
256       if (is_dir($originDir.DIRECTORY_SEPARATOR.$file))
257       {
258         $this->mkdirs($targetDir.DIRECTORY_SEPARATOR.$file);
259       }
260       else if (is_file($originDir.DIRECTORY_SEPARATOR.$file))
261       {
262         $this->copy($originDir.DIRECTORY_SEPARATOR.$file, $targetDir.DIRECTORY_SEPARATOR.$file, $options);
263       }
264       else if (is_link($originDir.DIRECTORY_SEPARATOR.$file))
265       {
266         $this->symlink($originDir.DIRECTORY_SEPARATOR.$file, $targetDir.DIRECTORY_SEPARATOR.$file);
267       }
268       else
269       {
270         throw new sfException(sprintf('Unable to guess "%s" file type.', $file));
271       }
272     }
273   }
274
275   /**
276    * Executes a shell command.
277    *
278    * @param string $cmd  The command to execute on the shell
279    */
280   public function sh($cmd)
281   {
282     $this->logSection('exec ', $cmd);
283
284     ob_start();
285     passthru($cmd.' 2>&1', $return);
286     $content = ob_get_contents();
287     ob_end_clean();
288
289     if ($return > 0)
290     {
291       throw new sfException(sprintf('Problem executing command %s', "\n".$content));
292     }
293
294     return $content;
295   }
296
297   /**
298    * Replaces tokens in an array of files.
299    *
300    * @param array  $files       An array of filenames
301    * @param string $beginToken  The begin token delimiter
302    * @param string $endToken    The end token delimiter
303    * @param array  $tokens      An array of token/value pairs
304    */
305   public function replaceTokens($files, $beginToken, $endToken, $tokens)
306   {
307     if (!is_array($files))
308     {
309       $files = array($files);
310     }
311
312     foreach ($files as $file)
313     {
314       $content = file_get_contents($file);
315       foreach ($tokens as $key => $value)
316       {
317         $content = str_replace($beginToken.$key.$endToken, $value, $content, $count);
318       }
319
320       $this->logSection('tokens', $file);
321
322       file_put_contents($file, $content);
323     }
324   }
325
326   /**
327    * Logs a message in a section.
328    *
329    * @param string $section  The section name
330    * @param string $message  The message
331    * @param int    $size     The maximum size of a line
332    */
333   protected function logSection($section, $message, $size = null)
334   {
335     if (!$this->dispatcher)
336     {
337       return;
338     }
339
340     $message = $this->formatter ? $this->formatter->formatSection($section, $message, $size) : $section.' '.$message."\n";
341
342     $this->dispatcher->notify(new sfEvent($this, 'command.log', array($message)));
343   }
344
345   /**
346    * Calculates the relative path from one to another directory.
347    * If they share no common path the absolute target dir is returned
348    *
349    * @param string $from directory from that the relative path shall be calculated
350    * @param string $to target directory
351    */
352   protected function calculateRelativeDir($from, $to)
353   {
354     $from = $this->canonicalizePath($from);
355     $to = $this->canonicalizePath($to);
356
357     $commonLength = 0;
358     $minPathLength = min(strlen($from), strlen($to));
359     // count how many chars the strings have in common
360     for ($i = 0; $i < $minPathLength; $i++)
361     {
362       if ($from[$i] != $to[$i]) break;
363       if ($from[$i] == DIRECTORY_SEPARATOR) $commonLength = $i + 1;
364     }
365
366     if ($commonLength)
367     {
368       $levelUp = substr_count($from, DIRECTORY_SEPARATOR, $commonLength);
369       // up that many level
370       $relativeDir  = str_repeat("..".DIRECTORY_SEPARATOR, $levelUp);
371       // down the remaining $to path
372       $relativeDir .= substr($to, $commonLength);
373       return $relativeDir;
374     }
375
376     return $to;
377   }
378
379   protected function canonicalizePath($path)
380   {
381     if (empty($path)) return '';
382     $out=array();
383     foreach( explode(DIRECTORY_SEPARATOR, $path) as $i => $fold)
384     {
385       if ($fold=='' || $fold=='.') continue;
386       if ($fold=='..' && $i>0 && end($out)!='..')
387       {
388         array_pop($out);
389       }
390       else
391       {
392         $out[]= $fold;
393       }
394     }
395     $result = $path{0} == DIRECTORY_SEPARATOR ? DIRECTORY_SEPARATOR : '';
396     $result .= join(DIRECTORY_SEPARATOR, $out);
397     $result .= $path{strlen($path)-1} == DIRECTORY_SEPARATOR ? DIRECTORY_SEPARATOR : '';
398     return $result;
399   }
400 }
401
Note: See TracBrowser for help on using the browser.