Development

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

You must first sign up to be able to contribute.

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

Revision 31247, 12.1 kB (checked in by fabien, 3 years ago)

fixed mbstring problem in sfFilesystem (closes #9139 - based on a patch from nresni)

  • 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 ('\\' == DIRECTORY_SEPARATOR && $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 ('\\' != DIRECTORY_SEPARATOR || !$copyOnWindows)
238     {
239       $originDir = $this->calculateRelativeDir($targetDir, $originDir);
240     }
241
242     $this->symlink($originDir, $targetDir, $copyOnWindows);
243   }
244
245   /**
246    * Mirrors a directory to another.
247    *
248    * @param string   $originDir  The origin directory
249    * @param string   $targetDir  The target directory
250    * @param sfFinder $finder     An sfFinder instance
251    * @param array    $options    An array of options (see copy())
252    */
253   public function mirror($originDir, $targetDir, $finder, $options = array())
254   {
255     foreach ($finder->relative()->in($originDir) as $file)
256     {
257       if (is_dir($originDir.DIRECTORY_SEPARATOR.$file))
258       {
259         $this->mkdirs($targetDir.DIRECTORY_SEPARATOR.$file);
260       }
261       else if (is_file($originDir.DIRECTORY_SEPARATOR.$file))
262       {
263         $this->copy($originDir.DIRECTORY_SEPARATOR.$file, $targetDir.DIRECTORY_SEPARATOR.$file, $options);
264       }
265       else if (is_link($originDir.DIRECTORY_SEPARATOR.$file))
266       {
267         $this->symlink($originDir.DIRECTORY_SEPARATOR.$file, $targetDir.DIRECTORY_SEPARATOR.$file);
268       }
269       else
270       {
271         throw new sfException(sprintf('Unable to guess "%s" file type.', $file));
272       }
273     }
274   }
275
276   /**
277    * DEPRECATED: Executes a shell command.
278    *
279    * This method is deprecated. Use the more powerful execute() method instead.
280    *
281    * @param string $cmd  The command to execute on the shell
282    */
283   public function sh($cmd)
284   {
285     $this->logSection('exec ', $cmd);
286
287     ob_start();
288     passthru($cmd.' 2>&1', $return);
289     $content = ob_get_contents();
290     ob_end_clean();
291
292     if ($return > 0)
293     {
294       throw new sfException(sprintf('Problem executing command %s', "\n".$content));
295     }
296
297     return $content;
298   }
299
300   /**
301    * Executes a shell command.
302    *
303    * @param string $cmd            The command to execute on the shell
304    * @param array  $stdoutCallback A callback for stdout output
305    * @param array  $stderrCallback A callback for stderr output
306    *
307    * @return array An array composed of the content output and the error output
308    */
309   public function execute($cmd, $stdoutCallback = null, $stderrCallback = null)
310   {
311     $this->logSection('exec ', $cmd);
312
313     $descriptorspec = array(
314       1 => array('pipe', 'w'), // stdout
315       2 => array('pipe', 'w'), // stderr
316     );
317
318     $process = proc_open($cmd, $descriptorspec, $pipes);
319     if (!is_resource($process))
320     {
321       throw new RuntimeException('Unable to execute the command.');
322     }
323
324     stream_set_blocking($pipes[1], false);
325     stream_set_blocking($pipes[2], false);
326
327     $output = '';
328     $err = '';
329     while (!feof($pipes[1]))
330     {
331       foreach ($pipes as $key => $pipe)
332       {
333         if (!$line = fread($pipe, 128))
334         {
335           continue;
336         }
337
338         if (1 == $key)
339         {
340           // stdout
341           $output .= $line;
342           if ($stdoutCallback)
343           {
344             call_user_func($stdoutCallback, $line);
345           }
346         }
347         else
348         {
349           // stderr
350           $err .= $line;
351           if ($stderrCallback)
352           {
353             call_user_func($stderrCallback, $line);
354           }
355         }
356       }
357
358       usleep(100000);
359     }
360
361     fclose($pipes[1]);
362     fclose($pipes[2]);
363
364     if (($return = proc_close($process)) > 0)
365     {
366       throw new RuntimeException('Problem executing command.', $return);
367     }
368
369     return array($output, $err);
370   }
371
372   /**
373    * Replaces tokens in an array of files.
374    *
375    * @param array  $files       An array of filenames
376    * @param string $beginToken  The begin token delimiter
377    * @param string $endToken    The end token delimiter
378    * @param array  $tokens      An array of token/value pairs
379    */
380   public function replaceTokens($files, $beginToken, $endToken, $tokens)
381   {
382     if (!is_array($files))
383     {
384       $files = array($files);
385     }
386
387     foreach ($files as $file)
388     {
389       $content = file_get_contents($file);
390       foreach ($tokens as $key => $value)
391       {
392         $content = str_replace($beginToken.$key.$endToken, $value, $content, $count);
393       }
394
395       $this->logSection('tokens', $file);
396
397       file_put_contents($file, $content);
398     }
399   }
400
401   /**
402    * Logs a message in a section.
403    *
404    * @param string $section  The section name
405    * @param string $message  The message
406    * @param int    $size     The maximum size of a line
407    */
408   protected function logSection($section, $message, $size = null)
409   {
410     if (!$this->dispatcher)
411     {
412       return;
413     }
414
415     $message = $this->formatter ? $this->formatter->formatSection($section, $message, $size) : $section.' '.$message."\n";
416
417     $this->dispatcher->notify(new sfEvent($this, 'command.log', array($message)));
418   }
419
420   /**
421    * Calculates the relative path from one to another directory.
422    *
423    * If the paths share no common path the absolute target dir is returned.
424    *
425    * @param string $from The directory from which to calculate the relative path
426    * @param string $to   The target directory
427    *
428    * @return string
429    */
430   protected function calculateRelativeDir($from, $to)
431   {
432     $from = $this->canonicalizePath($from);
433     $to = $this->canonicalizePath($to);
434
435     $commonLength = 0;
436     $minPathLength = min(strlen($from), strlen($to));
437
438     // count how many chars the strings have in common
439     for ($i = 0; $i < $minPathLength; $i++)
440     {
441       if ($from[$i] != $to[$i])
442       {
443         break;
444       }
445
446       if (DIRECTORY_SEPARATOR == $from[$i])
447       {
448         $commonLength = $i + 1;
449       }
450     }
451
452     if ($commonLength)
453     {
454       if (extension_loaded('mbstring'))
455       {
456         $levelUp = mb_substr_count(mb_strcut($from, $commonLength), DIRECTORY_SEPARATOR);
457       }
458       else
459       {
460         $levelUp = substr_count($from, DIRECTORY_SEPARATOR, $commonLength);
461       }
462
463       // up that many level
464       $relativeDir = str_repeat('..'.DIRECTORY_SEPARATOR, $levelUp);
465
466       // down the remaining $to path
467       $relativeDir .= substr($to, $commonLength);
468
469       return $relativeDir;
470     }
471
472     return $to;
473   }
474
475   /**
476    * @param string A filesystem path
477    *
478    * @return string
479    */
480   protected function canonicalizePath($path)
481   {
482     if (empty($path))
483     {
484       return '';
485     }
486
487     $out = array();
488     foreach (explode(DIRECTORY_SEPARATOR, $path) as $i => $fold)
489     {
490       if ('' == $fold || '.' == $fold)
491       {
492         continue;
493       }
494
495       if ('..' == $fold && $i > 0 && '..' != end($out))
496       {
497         array_pop($out);
498       }
499       else
500       {
501         $out[] = $fold;
502       }
503     }
504
505     $result  = DIRECTORY_SEPARATOR == $path[0] ? DIRECTORY_SEPARATOR : '';
506     $result .= implode(DIRECTORY_SEPARATOR, $out);
507     $result .= DIRECTORY_SEPARATOR == $path[strlen($path) - 1] ? DIRECTORY_SEPARATOR : '';
508
509     return $result;
510   }
511 }
512
Note: See TracBrowser for help on using the browser.