Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: sfFilesystem.class.php 31247 2010-10-26 12:26:15Z fabien $
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
   * Executes a shell command.
278
   *
279
   * @param string $cmd            The command to execute on the shell
280
   * @param array  $stdoutCallback A callback for stdout output
281
   * @param array  $stderrCallback A callback for stderr output
282
   *
283
   * @return array An array composed of the content output and the error output
284
   */
285
  public function execute($cmd, $stdoutCallback = null, $stderrCallback = null)
286
  {
287
    $this->logSection('exec ', $cmd);
288
 
289
    $descriptorspec = array(
290
      1 => array('pipe', 'w'), // stdout
291
      2 => array('pipe', 'w'), // stderr
292
    );
293
 
294
    $process = proc_open($cmd, $descriptorspec, $pipes);
295
    if (!is_resource($process))
296
    {
297
      throw new RuntimeException('Unable to execute the command.');
298
    }
299
 
300
    stream_set_blocking($pipes[1], false);
301
    stream_set_blocking($pipes[2], false);
302
 
303
    $output = '';
304
    $err = '';
305
    while (!feof($pipes[1]) || !feof($pipes[2]))
306
    {
307
      foreach ($pipes as $key => $pipe)
308
      {
309
        if (!$line = fread($pipe, 128))
310
        {
311
          continue;
312
        }
313
 
314
        if (1 == $key)
315
        {
316
          // stdout
317
          $output .= $line;
318
          if ($stdoutCallback)
319
          {
320
            call_user_func($stdoutCallback, $line);
321
          }
322
        }
323
        else
324
        {
325
          // stderr
326
          $err .= $line;
327
          if ($stderrCallback)
328
          {
329
            call_user_func($stderrCallback, $line);
330
          }
331
        }
332
      }
333
 
334
      usleep(100000);
335
    }
336
 
337
    fclose($pipes[1]);
338
    fclose($pipes[2]);
339
 
340
    if (($return = proc_close($process)) > 0)
341
    {
342
      throw new RuntimeException('Problem executing command.', $return);
343
    }
344
 
345
    return array($output, $err);
346
  }
347
 
348
  /**
349
   * Replaces tokens in an array of files.
350
   *
351
   * @param array  $files       An array of filenames
352
   * @param string $beginToken  The begin token delimiter
353
   * @param string $endToken    The end token delimiter
354
   * @param array  $tokens      An array of token/value pairs
355
   */
356
  public function replaceTokens($files, $beginToken, $endToken, $tokens)
357
  {
358
    if (!is_array($files))
359
    {
360
      $files = array($files);
361
    }
362
 
363
    foreach ($files as $file)
364
    {
365
      $content = file_get_contents($file);
366
      foreach ($tokens as $key => $value)
367
      {
368
        $content = str_replace($beginToken.$key.$endToken, $value, $content, $count);
369
      }
370
 
371
      $this->logSection('tokens', $file);
372
 
373
      file_put_contents($file, $content);
374
    }
375
  }
376
 
377
  /**
378
   * Logs a message in a section.
379
   *
380
   * @param string $section  The section name
381
   * @param string $message  The message
382
   * @param int    $size     The maximum size of a line
383
   */
384
  protected function logSection($section, $message, $size = null)
385
  {
386
    if (!$this->dispatcher)
387
    {
388
      return;
389
    }
390
 
391
    $message = $this->formatter ? $this->formatter->formatSection($section, $message, $size) : $section.' '.$message."\n";
392
 
393
    $this->dispatcher->notify(new sfEvent($this, 'command.log', array($message)));
394
  }
395
 
396
  /**
397
   * Calculates the relative path from one to another directory.
398
   *
399
   * If the paths share no common path the absolute target dir is returned.
400
   *
401
   * @param string $from The directory from which to calculate the relative path
402
   * @param string $to   The target directory
403
   *
404
   * @return string
405
   */
406
  protected function calculateRelativeDir($from, $to)
407
  {
408
    $from = $this->canonicalizePath($from);
409
    $to = $this->canonicalizePath($to);
410
 
411
    $commonLength = 0;
412
    $minPathLength = min(strlen($from), strlen($to));
413
 
414
    // count how many chars the strings have in common
415
    for ($i = 0; $i < $minPathLength; $i++)
416
    {
417
      if ($from[$i] != $to[$i])
418
      {
419
        break;
420
      }
421
 
422
      if (DIRECTORY_SEPARATOR == $from[$i])
423
      {
424
        $commonLength = $i + 1;
425
      }
426
    }
427
 
428
    if ($commonLength)
429
    {
430
      if (extension_loaded('mbstring'))
431
      {
432
        $levelUp = mb_substr_count(mb_strcut($from, $commonLength), DIRECTORY_SEPARATOR);
433
      }
434
      else
435
      {
436
        $levelUp = substr_count($from, DIRECTORY_SEPARATOR, $commonLength);
437
      }
438
 
439
      // up that many level
440
      $relativeDir = str_repeat('..'.DIRECTORY_SEPARATOR, $levelUp);
441
 
442
      // down the remaining $to path
443
      $relativeDir .= substr($to, $commonLength);
444
 
445
      return $relativeDir;
446
    }
447
 
448
    return $to;
449
  }
450
 
451
  /**
452
   * @param string A filesystem path
453
   *
454
   * @return string
455
   */
456
  protected function canonicalizePath($path)
457
  {
458
    if (empty($path))
459
    {
460
      return '';
461
    }
462
 
463
    $out = array();
464
    foreach (explode(DIRECTORY_SEPARATOR, $path) as $i => $fold)
465
    {
466
      if ('' == $fold || '.' == $fold)
467
      {
468
        continue;
469
      }
470
 
471
      if ('..' == $fold && $i > 0 && '..' != end($out))
472
      {
473
        array_pop($out);
474
      }
475
      else
476
      {
477
        $out[] = $fold;
478
      }
479
    }
480
 
481
    $result  = DIRECTORY_SEPARATOR == $path[0] ? DIRECTORY_SEPARATOR : '';
482
    $result .= implode(DIRECTORY_SEPARATOR, $out);
483
    $result .= DIRECTORY_SEPARATOR == $path[strlen($path) - 1] ? DIRECTORY_SEPARATOR : '';
484
 
485
    return $result;
486
  }
487
}