Project

General

Profile

Paste
Download (27.6 KB) Statistics
| Branch: | Revision:

root / drupal7 / includes / stream_wrappers.inc @ 6d8023f2

1
<?php
2

    
3
/**
4
 * @file
5
 * Drupal stream wrapper interface.
6
 *
7
 * Provides a Drupal interface and classes to implement PHP stream wrappers for
8
 * public, private, and temporary files.
9
 *
10
 * A stream wrapper is an abstraction of a file system that allows Drupal to
11
 * use the same set of methods to access both local files and remote resources.
12
 *
13
 * Note that PHP 5.2 fopen() only supports URIs of the form "scheme://target"
14
 * despite the fact that according to RFC 3986 a URI's scheme component
15
 * delimiter is in general just ":", not "://".  Because of this PHP limitation
16
 * and for consistency Drupal will only accept URIs of form "scheme://target".
17
 *
18
 * @see http://www.faqs.org/rfcs/rfc3986.html
19
 * @see http://bugs.php.net/bug.php?id=47070
20
 */
21

    
22
/**
23
 * Stream wrapper bit flags that are the basis for composite types.
24
 *
25
 * Note that 0x0002 is skipped, because it was the value of a constant that has
26
 * since been removed.
27
 */
28

    
29
/**
30
 * Stream wrapper bit flag -- a filter that matches all wrappers.
31
 */
32
define('STREAM_WRAPPERS_ALL', 0x0000);
33

    
34
/**
35
 * Stream wrapper bit flag -- refers to a local file system location.
36
 */
37
define('STREAM_WRAPPERS_LOCAL', 0x0001);
38

    
39
/**
40
 * Stream wrapper bit flag -- wrapper is readable (almost always true).
41
 */
42
define('STREAM_WRAPPERS_READ', 0x0004);
43

    
44
/**
45
 * Stream wrapper bit flag -- wrapper is writeable.
46
 */
47
define('STREAM_WRAPPERS_WRITE', 0x0008);
48

    
49
/**
50
 * Stream wrapper bit flag -- exposed in the UI and potentially web accessible.
51
 */
52
define('STREAM_WRAPPERS_VISIBLE', 0x0010);
53

    
54
/**
55
 * Composite stream wrapper bit flags that are usually used as the types.
56
 */
57

    
58
/**
59
 * Stream wrapper type flag -- not visible in the UI or accessible via web,
60
 * but readable and writable. E.g. the temporary directory for uploads.
61
 */
62
define('STREAM_WRAPPERS_HIDDEN', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE);
63

    
64
/**
65
 * Stream wrapper type flag -- hidden, readable and writeable using local files.
66
 */
67
define('STREAM_WRAPPERS_LOCAL_HIDDEN', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_HIDDEN);
68

    
69
/**
70
 * Stream wrapper type flag -- visible, readable and writeable.
71
 */
72
define('STREAM_WRAPPERS_WRITE_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | STREAM_WRAPPERS_VISIBLE);
73

    
74
/**
75
 * Stream wrapper type flag -- visible and read-only.
76
 */
77
define('STREAM_WRAPPERS_READ_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_VISIBLE);
78

    
79
/**
80
 * Stream wrapper type flag -- the default when 'type' is omitted from
81
 * hook_stream_wrappers(). This does not include STREAM_WRAPPERS_LOCAL,
82
 * because PHP grants a greater trust level to local files (for example, they
83
 * can be used in an "include" statement, regardless of the "allow_url_include"
84
 * setting), so stream wrappers need to explicitly opt-in to this.
85
 */
86
define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_WRITE_VISIBLE);
87

    
88
/**
89
 * Stream wrapper type flag -- visible, readable and writeable using local files.
90
 */
91
define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_NORMAL);
92

    
93
/**
94
 * Generic PHP stream wrapper interface.
95
 *
96
 * @see http://www.php.net/manual/class.streamwrapper.php
97
 */
98
interface StreamWrapperInterface {
99
  public function stream_open($uri, $mode, $options, &$opened_url);
100
  public function stream_close();
101
  public function stream_lock($operation);
102
  public function stream_read($count);
103
  public function stream_write($data);
104
  public function stream_eof();
105
  public function stream_seek($offset, $whence);
106
  public function stream_flush();
107
  public function stream_tell();
108
  public function stream_stat();
109
  public function unlink($uri);
110
  public function rename($from_uri, $to_uri);
111
  public function mkdir($uri, $mode, $options);
112
  public function rmdir($uri, $options);
113
  public function url_stat($uri, $flags);
114
  public function dir_opendir($uri, $options);
115
  public function dir_readdir();
116
  public function dir_rewinddir();
117
  public function dir_closedir();
118
}
119

    
120
/**
121
 * Drupal stream wrapper extension.
122
 *
123
 * Extend the StreamWrapperInterface with methods expected by Drupal stream
124
 * wrapper classes.
125
 */
126
interface DrupalStreamWrapperInterface extends StreamWrapperInterface {
127
  /**
128
   * Set the absolute stream resource URI.
129
   *
130
   * This allows you to set the URI. Generally is only called by the factory
131
   * method.
132
   *
133
   * @param $uri
134
   *   A string containing the URI that should be used for this instance.
135
   */
136
  public function setUri($uri);
137

    
138
  /**
139
   * Returns the stream resource URI.
140
   *
141
   * @return
142
   *   Returns the current URI of the instance.
143
   */
144
  public function getUri();
145

    
146
  /**
147
   * Returns a web accessible URL for the resource.
148
   *
149
   * This function should return a URL that can be embedded in a web page
150
   * and accessed from a browser. For example, the external URL of
151
   * "youtube://xIpLd0WQKCY" might be
152
   * "http://www.youtube.com/watch?v=xIpLd0WQKCY".
153
   *
154
   * @return
155
   *   Returns a string containing a web accessible URL for the resource.
156
   */
157
  public function getExternalUrl();
158

    
159
  /**
160
   * Returns the MIME type of the resource.
161
   *
162
   * @param $uri
163
   *   The URI, path, or filename.
164
   * @param $mapping
165
   *   An optional map of extensions to their mimetypes, in the form:
166
   *    - 'mimetypes': a list of mimetypes, keyed by an identifier,
167
   *    - 'extensions': the mapping itself, an associative array in which
168
   *      the key is the extension and the value is the mimetype identifier.
169
   *
170
   * @return
171
   *   Returns a string containing the MIME type of the resource.
172
   */
173
  public static function getMimeType($uri, $mapping = NULL);
174

    
175
  /**
176
   * Changes permissions of the resource.
177
   *
178
   * PHP lacks this functionality and it is not part of the official stream
179
   * wrapper interface. This is a custom implementation for Drupal.
180
   *
181
   * @param $mode
182
   *   Integer value for the permissions. Consult PHP chmod() documentation
183
   *   for more information.
184
   *
185
   * @return
186
   *   Returns TRUE on success or FALSE on failure.
187
   */
188
  public function chmod($mode);
189

    
190
  /**
191
   * Returns canonical, absolute path of the resource.
192
   *
193
   * Implementation placeholder. PHP's realpath() does not support stream
194
   * wrappers. We provide this as a default so that individual wrappers may
195
   * implement their own solutions.
196
   *
197
   * @return
198
   *   Returns a string with absolute pathname on success (implemented
199
   *   by core wrappers), or FALSE on failure or if the registered
200
   *   wrapper does not provide an implementation.
201
   */
202
  public function realpath();
203

    
204
  /**
205
   * Gets the name of the directory from a given path.
206
   *
207
   * This method is usually accessed through drupal_dirname(), which wraps
208
   * around the normal PHP dirname() function, which does not support stream
209
   * wrappers.
210
   *
211
   * @param $uri
212
   *   An optional URI.
213
   *
214
   * @return
215
   *   A string containing the directory name, or FALSE if not applicable.
216
   *
217
   * @see drupal_dirname()
218
   */
219
  public function dirname($uri = NULL);
220
}
221

    
222
/**
223
 * Drupal stream wrapper base class for local files.
224
 *
225
 * This class provides a complete stream wrapper implementation. URIs such as
226
 * "public://example.txt" are expanded to a normal filesystem path such as
227
 * "sites/default/files/example.txt" and then PHP filesystem functions are
228
 * invoked.
229
 *
230
 * DrupalLocalStreamWrapper implementations need to implement at least the
231
 * getDirectoryPath() and getExternalUrl() methods.
232
 */
233
abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface {
234
  /**
235
   * Stream context resource.
236
   *
237
   * @var Resource
238
   */
239
  public $context;
240

    
241
  /**
242
   * A generic resource handle.
243
   *
244
   * @var Resource
245
   */
246
  public $handle = NULL;
247

    
248
  /**
249
   * Instance URI (stream).
250
   *
251
   * A stream is referenced as "scheme://target".
252
   *
253
   * @var String
254
   */
255
  protected $uri;
256

    
257
  /**
258
   * Gets the path that the wrapper is responsible for.
259
   * @TODO: Review this method name in D8 per http://drupal.org/node/701358
260
   *
261
   * @return
262
   *   String specifying the path.
263
   */
264
  abstract function getDirectoryPath();
265

    
266
  /**
267
   * Base implementation of setUri().
268
   */
269
  function setUri($uri) {
270
    $this->uri = $uri;
271
  }
272

    
273
  /**
274
   * Base implementation of getUri().
275
   */
276
  function getUri() {
277
    return $this->uri;
278
  }
279

    
280
  /**
281
   * Returns the local writable target of the resource within the stream.
282
   *
283
   * This function should be used in place of calls to realpath() or similar
284
   * functions when attempting to determine the location of a file. While
285
   * functions like realpath() may return the location of a read-only file, this
286
   * method may return a URI or path suitable for writing that is completely
287
   * separate from the URI used for reading.
288
   *
289
   * @param $uri
290
   *   Optional URI.
291
   *
292
   * @return
293
   *   Returns a string representing a location suitable for writing of a file,
294
   *   or FALSE if unable to write to the file such as with read-only streams.
295
   */
296
  protected function getTarget($uri = NULL) {
297
    if (!isset($uri)) {
298
      $uri = $this->uri;
299
    }
300

    
301
    list($scheme, $target) = explode('://', $uri, 2);
302

    
303
    // Remove erroneous leading or trailing, forward-slashes and backslashes.
304
    return trim($target, '\/');
305
  }
306

    
307
  /**
308
   * Base implementation of getMimeType().
309
   */
310
  static function getMimeType($uri, $mapping = NULL) {
311
    if (!isset($mapping)) {
312
      // The default file map, defined in file.mimetypes.inc is quite big.
313
      // We only load it when necessary.
314
      include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
315
      $mapping = file_mimetype_mapping();
316
    }
317

    
318
    $extension = '';
319
    $file_parts = explode('.', drupal_basename($uri));
320

    
321
    // Remove the first part: a full filename should not match an extension.
322
    array_shift($file_parts);
323

    
324
    // Iterate over the file parts, trying to find a match.
325
    // For my.awesome.image.jpeg, we try:
326
    //   - jpeg
327
    //   - image.jpeg, and
328
    //   - awesome.image.jpeg
329
    while ($additional_part = array_pop($file_parts)) {
330
      $extension = strtolower($additional_part . ($extension ? '.' . $extension : ''));
331
      if (isset($mapping['extensions'][$extension])) {
332
        return $mapping['mimetypes'][$mapping['extensions'][$extension]];
333
      }
334
    }
335

    
336
    return 'application/octet-stream';
337
  }
338

    
339
  /**
340
   * Base implementation of chmod().
341
   */
342
  function chmod($mode) {
343
    $output = @chmod($this->getLocalPath(), $mode);
344
    // We are modifying the underlying file here, so we have to clear the stat
345
    // cache so that PHP understands that URI has changed too.
346
    clearstatcache();
347
    return $output;
348
  }
349

    
350
  /**
351
   * Base implementation of realpath().
352
   */
353
  function realpath() {
354
    return $this->getLocalPath();
355
  }
356

    
357
  /**
358
   * Returns the canonical absolute path of the URI, if possible.
359
   *
360
   * @param string $uri
361
   *   (optional) The stream wrapper URI to be converted to a canonical
362
   *   absolute path. This may point to a directory or another type of file.
363
   *
364
   * @return string|false
365
   *   If $uri is not set, returns the canonical absolute path of the URI
366
   *   previously set by the DrupalStreamWrapperInterface::setUri() function.
367
   *   If $uri is set and valid for this class, returns its canonical absolute
368
   *   path, as determined by the realpath() function. If $uri is set but not
369
   *   valid, returns FALSE.
370
   */
371
  protected function getLocalPath($uri = NULL) {
372
    if (!isset($uri)) {
373
      $uri = $this->uri;
374
    }
375
    $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
376
    $realpath = realpath($path);
377
    if (!$realpath) {
378
      // This file does not yet exist.
379
      $realpath = realpath(dirname($path)) . '/' . drupal_basename($path);
380
    }
381
    $directory = realpath($this->getDirectoryPath());
382
    if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
383
      return FALSE;
384
    }
385
    return $realpath;
386
  }
387

    
388
  /**
389
   * Support for fopen(), file_get_contents(), file_put_contents() etc.
390
   *
391
   * @param $uri
392
   *   A string containing the URI to the file to open.
393
   * @param $mode
394
   *   The file mode ("r", "wb" etc.).
395
   * @param $options
396
   *   A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
397
   * @param $opened_path
398
   *   A string containing the path actually opened.
399
   *
400
   * @return
401
   *   Returns TRUE if file was opened successfully.
402
   *
403
   * @see http://php.net/manual/streamwrapper.stream-open.php
404
   */
405
  public function stream_open($uri, $mode, $options, &$opened_path) {
406
    $this->uri = $uri;
407
    $path = $this->getLocalPath();
408
    $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
409

    
410
    if ((bool) $this->handle && $options & STREAM_USE_PATH) {
411
      $opened_path = $path;
412
    }
413

    
414
    return (bool) $this->handle;
415
  }
416

    
417
  /**
418
   * Support for flock().
419
   *
420
   * @param $operation
421
   *   One of the following:
422
   *   - LOCK_SH to acquire a shared lock (reader).
423
   *   - LOCK_EX to acquire an exclusive lock (writer).
424
   *   - LOCK_UN to release a lock (shared or exclusive).
425
   *   - LOCK_NB if you don't want flock() to block while locking (not
426
   *     supported on Windows).
427
   *
428
   * @return
429
   *   Always returns TRUE at the present time.
430
   *
431
   * @see http://php.net/manual/streamwrapper.stream-lock.php
432
   */
433
  public function stream_lock($operation) {
434
    if (in_array($operation, array(LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB))) {
435
      return flock($this->handle, $operation);
436
    }
437

    
438
    return TRUE;
439
  }
440

    
441
  /**
442
   * Support for fread(), file_get_contents() etc.
443
   *
444
   * @param $count
445
   *   Maximum number of bytes to be read.
446
   *
447
   * @return
448
   *   The string that was read, or FALSE in case of an error.
449
   *
450
   * @see http://php.net/manual/streamwrapper.stream-read.php
451
   */
452
  public function stream_read($count) {
453
    return fread($this->handle, $count);
454
  }
455

    
456
  /**
457
   * Support for fwrite(), file_put_contents() etc.
458
   *
459
   * @param $data
460
   *   The string to be written.
461
   *
462
   * @return
463
   *   The number of bytes written (integer).
464
   *
465
   * @see http://php.net/manual/streamwrapper.stream-write.php
466
   */
467
  public function stream_write($data) {
468
    return fwrite($this->handle, $data);
469
  }
470

    
471
  /**
472
   * Support for feof().
473
   *
474
   * @return
475
   *   TRUE if end-of-file has been reached.
476
   *
477
   * @see http://php.net/manual/streamwrapper.stream-eof.php
478
   */
479
  public function stream_eof() {
480
    return feof($this->handle);
481
  }
482

    
483
  /**
484
   * Support for fseek().
485
   *
486
   * @param $offset
487
   *   The byte offset to got to.
488
   * @param $whence
489
   *   SEEK_SET, SEEK_CUR, or SEEK_END.
490
   *
491
   * @return
492
   *   TRUE on success.
493
   *
494
   * @see http://php.net/manual/streamwrapper.stream-seek.php
495
   */
496
  public function stream_seek($offset, $whence) {
497
    // fseek returns 0 on success and -1 on a failure.
498
    // stream_seek   1 on success and  0 on a failure.
499
    return !fseek($this->handle, $offset, $whence);
500
  }
501

    
502
  /**
503
   * Support for fflush().
504
   *
505
   * @return
506
   *   TRUE if data was successfully stored (or there was no data to store).
507
   *
508
   * @see http://php.net/manual/streamwrapper.stream-flush.php
509
   */
510
  public function stream_flush() {
511
    return fflush($this->handle);
512
  }
513

    
514
  /**
515
   * Support for ftell().
516
   *
517
   * @return
518
   *   The current offset in bytes from the beginning of file.
519
   *
520
   * @see http://php.net/manual/streamwrapper.stream-tell.php
521
   */
522
  public function stream_tell() {
523
    return ftell($this->handle);
524
  }
525

    
526
  /**
527
   * Support for fstat().
528
   *
529
   * @return
530
   *   An array with file status, or FALSE in case of an error - see fstat()
531
   *   for a description of this array.
532
   *
533
   * @see http://php.net/manual/streamwrapper.stream-stat.php
534
   */
535
  public function stream_stat() {
536
    return fstat($this->handle);
537
  }
538

    
539
  /**
540
   * Support for fclose().
541
   *
542
   * @return
543
   *   TRUE if stream was successfully closed.
544
   *
545
   * @see http://php.net/manual/streamwrapper.stream-close.php
546
   */
547
  public function stream_close() {
548
    return fclose($this->handle);
549
  }
550

    
551
  /**
552
   * Sets metadata on the stream.
553
   *
554
   * WARNING: Do not call this method directly! It will be called internally by
555
   * PHP itself when one of the following functions is called on a stream URL:
556
   *
557
   * @param string $uri
558
   *   A string containing the URI to the file to set metadata on.
559
   * @param int $option
560
   *   One of:
561
   *   - STREAM_META_TOUCH: The method was called in response to touch().
562
   *   - STREAM_META_OWNER_NAME: The method was called in response to chown()
563
   *     with string parameter.
564
   *   - STREAM_META_OWNER: The method was called in response to chown().
565
   *   - STREAM_META_GROUP_NAME: The method was called in response to chgrp().
566
   *   - STREAM_META_GROUP: The method was called in response to chgrp().
567
   *   - STREAM_META_ACCESS: The method was called in response to chmod().
568
   * @param mixed $value
569
   *   If option is:
570
   *   - STREAM_META_TOUCH: Array consisting of two arguments of the touch()
571
   *     function.
572
   *   - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner
573
   *     user/group as string.
574
   *   - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner
575
   *     user/group as integer.
576
   *   - STREAM_META_ACCESS: The argument of the chmod() as integer.
577
   *
578
   * @return bool
579
   *   Returns TRUE on success or FALSE on failure. If $option is not
580
   *   implemented, FALSE should be returned.
581
   *
582
   * @see touch()
583
   * @see chmod()
584
   * @see chown()
585
   * @see chgrp()
586
   * @link http://php.net/manual/streamwrapper.stream-metadata.php
587
   */
588
  public function stream_metadata($uri, $option, $value) {
589
    $target = $this->getLocalPath($uri);
590
    $return = FALSE;
591
    switch ($option) {
592
      case STREAM_META_TOUCH:
593
        if (!empty($value)) {
594
          $return = touch($target, $value[0], $value[1]);
595
        }
596
        else {
597
          $return = touch($target);
598
        }
599
        break;
600

    
601
      case STREAM_META_OWNER_NAME:
602
      case STREAM_META_OWNER:
603
        $return = chown($target, $value);
604
        break;
605

    
606
      case STREAM_META_GROUP_NAME:
607
      case STREAM_META_GROUP:
608
        $return = chgrp($target, $value);
609
        break;
610

    
611
      case STREAM_META_ACCESS:
612
        $return = chmod($target, $value);
613
        break;
614
    }
615
    if ($return) {
616
      // For convenience clear the file status cache of the underlying file,
617
      // since metadata operations are often followed by file status checks.
618
      clearstatcache(TRUE, $target);
619
    }
620
    return $return;
621
  }
622

    
623
  /**
624
   * Truncate stream.
625
   *
626
   * Will respond to truncation; e.g., through ftruncate().
627
   *
628
   * @param int $new_size
629
   *   The new size.
630
   *
631
   * @return bool
632
   *   TRUE on success, FALSE otherwise.
633
   */
634
  public function stream_truncate($new_size) {
635
    return ftruncate($this->handle, $new_size);
636
  }
637

    
638
  /**
639
   * Retrieve the underlying stream resource.
640
   *
641
   * This method is called in response to stream_select().
642
   *
643
   * @param int $cast_as
644
   *   Can be STREAM_CAST_FOR_SELECT when stream_select() is calling
645
   *   stream_cast() or STREAM_CAST_AS_STREAM when stream_cast() is called for
646
   *   other uses.
647
   *
648
   * @return resource|false
649
   *   The underlying stream resource or FALSE if stream_select() is not
650
   *   supported.
651
   *
652
   * @see stream_select()
653
   * @link http://php.net/manual/streamwrapper.stream-cast.php
654
   */
655
  public function stream_cast($cast_as) {
656
    return $this->handle ? $this->handle : FALSE;
657
  }
658

    
659
  /**
660
   * Change stream options.
661
   *
662
   * This method is called to set options on the stream.
663
   *
664
   * Since Windows systems do not allow it and it is not needed for most use
665
   * cases anyway, this method is not supported on local files and will trigger
666
   * an error and return false. If needed, custom subclasses can provide
667
   * OS-specific implementations for advanced use cases.
668
   *
669
   * @param int $option
670
   *   One of:
671
   *   - STREAM_OPTION_BLOCKING: The method was called in response to
672
   *     stream_set_blocking().
673
   *   - STREAM_OPTION_READ_TIMEOUT: The method was called in response to
674
   *     stream_set_timeout().
675
   *   - STREAM_OPTION_WRITE_BUFFER: The method was called in response to
676
   *     stream_set_write_buffer().
677
   * @param int $arg1
678
   *   If option is:
679
   *   - STREAM_OPTION_BLOCKING: The requested blocking mode:
680
   *     - 1 means blocking.
681
   *     - 0 means not blocking.
682
   *   - STREAM_OPTION_READ_TIMEOUT: The timeout in seconds.
683
   *   - STREAM_OPTION_WRITE_BUFFER: The buffer mode, STREAM_BUFFER_NONE or
684
   *     STREAM_BUFFER_FULL.
685
   * @param int $arg2
686
   *   If option is:
687
   *   - STREAM_OPTION_BLOCKING: This option is not set.
688
   *   - STREAM_OPTION_READ_TIMEOUT: The timeout in microseconds.
689
   *   - STREAM_OPTION_WRITE_BUFFER: The requested buffer size.
690
   *
691
   * @return bool
692
   *   TRUE on success, FALSE otherwise. If $option is not implemented, FALSE
693
   *   should be returned.
694
   */
695
  public function stream_set_option($option, $arg1, $arg2) {
696
    trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING);
697
    return FALSE;
698
  }
699

    
700
  /**
701
   * Support for unlink().
702
   *
703
   * @param $uri
704
   *   A string containing the URI to the resource to delete.
705
   *
706
   * @return
707
   *   TRUE if resource was successfully deleted.
708
   *
709
   * @see http://php.net/manual/streamwrapper.unlink.php
710
   */
711
  public function unlink($uri) {
712
    $this->uri = $uri;
713
    return drupal_unlink($this->getLocalPath());
714
  }
715

    
716
  /**
717
   * Support for rename().
718
   *
719
   * @param $from_uri,
720
   *   The URI to the file to rename.
721
   * @param $to_uri
722
   *   The new URI for file.
723
   *
724
   * @return
725
   *   TRUE if file was successfully renamed.
726
   *
727
   * @see http://php.net/manual/streamwrapper.rename.php
728
   */
729
  public function rename($from_uri, $to_uri) {
730
    return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
731
  }
732

    
733
  /**
734
   * Gets the name of the directory from a given path.
735
   *
736
   * This method is usually accessed through drupal_dirname(), which wraps
737
   * around the PHP dirname() function because it does not support stream
738
   * wrappers.
739
   *
740
   * @param $uri
741
   *   A URI or path.
742
   *
743
   * @return
744
   *   A string containing the directory name.
745
   *
746
   * @see drupal_dirname()
747
   */
748
  public function dirname($uri = NULL) {
749
    list($scheme, $target) = explode('://', $uri, 2);
750
    $target  = $this->getTarget($uri);
751
    $dirname = dirname($target);
752

    
753
    if ($dirname == '.') {
754
      $dirname = '';
755
    }
756

    
757
    return $scheme . '://' . $dirname;
758
  }
759

    
760
  /**
761
   * Support for mkdir().
762
   *
763
   * @param $uri
764
   *   A string containing the URI to the directory to create.
765
   * @param $mode
766
   *   Permission flags - see mkdir().
767
   * @param $options
768
   *   A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
769
   *
770
   * @return
771
   *   TRUE if directory was successfully created.
772
   *
773
   * @see http://php.net/manual/streamwrapper.mkdir.php
774
   */
775
  public function mkdir($uri, $mode, $options) {
776
    $this->uri = $uri;
777
    $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
778
    if ($recursive) {
779
      // $this->getLocalPath() fails if $uri has multiple levels of directories
780
      // that do not yet exist.
781
      $localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
782
    }
783
    else {
784
      $localpath = $this->getLocalPath($uri);
785
    }
786
    if ($options & STREAM_REPORT_ERRORS) {
787
      return mkdir($localpath, $mode, $recursive);
788
    }
789
    else {
790
      return @mkdir($localpath, $mode, $recursive);
791
    }
792
  }
793

    
794
  /**
795
   * Support for rmdir().
796
   *
797
   * @param $uri
798
   *   A string containing the URI to the directory to delete.
799
   * @param $options
800
   *   A bit mask of STREAM_REPORT_ERRORS.
801
   *
802
   * @return
803
   *   TRUE if directory was successfully removed.
804
   *
805
   * @see http://php.net/manual/streamwrapper.rmdir.php
806
   */
807
  public function rmdir($uri, $options) {
808
    $this->uri = $uri;
809
    if ($options & STREAM_REPORT_ERRORS) {
810
      return drupal_rmdir($this->getLocalPath());
811
    }
812
    else {
813
      return @drupal_rmdir($this->getLocalPath());
814
    }
815
  }
816

    
817
  /**
818
   * Support for stat().
819
   *
820
   * @param $uri
821
   *   A string containing the URI to get information about.
822
   * @param $flags
823
   *   A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
824
   *
825
   * @return
826
   *   An array with file status, or FALSE in case of an error - see fstat()
827
   *   for a description of this array.
828
   *
829
   * @see http://php.net/manual/streamwrapper.url-stat.php
830
   */
831
  public function url_stat($uri, $flags) {
832
    $this->uri = $uri;
833
    $path = $this->getLocalPath();
834
    // Suppress warnings if requested or if the file or directory does not
835
    // exist. This is consistent with PHP's plain filesystem stream wrapper.
836
    if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) {
837
      return @stat($path);
838
    }
839
    else {
840
      return stat($path);
841
    }
842
  }
843

    
844
  /**
845
   * Support for opendir().
846
   *
847
   * @param $uri
848
   *   A string containing the URI to the directory to open.
849
   * @param $options
850
   *   Unknown (parameter is not documented in PHP Manual).
851
   *
852
   * @return
853
   *   TRUE on success.
854
   *
855
   * @see http://php.net/manual/streamwrapper.dir-opendir.php
856
   */
857
  public function dir_opendir($uri, $options) {
858
    $this->uri = $uri;
859
    $this->handle = opendir($this->getLocalPath());
860

    
861
    return (bool) $this->handle;
862
  }
863

    
864
  /**
865
   * Support for readdir().
866
   *
867
   * @return
868
   *   The next filename, or FALSE if there are no more files in the directory.
869
   *
870
   * @see http://php.net/manual/streamwrapper.dir-readdir.php
871
   */
872
  public function dir_readdir() {
873
    return readdir($this->handle);
874
  }
875

    
876
  /**
877
   * Support for rewinddir().
878
   *
879
   * @return
880
   *   TRUE on success.
881
   *
882
   * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
883
   */
884
  public function dir_rewinddir() {
885
    rewinddir($this->handle);
886
    // We do not really have a way to signal a failure as rewinddir() does not
887
    // have a return value and there is no way to read a directory handler
888
    // without advancing to the next file.
889
    return TRUE;
890
  }
891

    
892
  /**
893
   * Support for closedir().
894
   *
895
   * @return
896
   *   TRUE on success.
897
   *
898
   * @see http://php.net/manual/streamwrapper.dir-closedir.php
899
   */
900
  public function dir_closedir() {
901
    closedir($this->handle);
902
    // We do not really have a way to signal a failure as closedir() does not
903
    // have a return value.
904
    return TRUE;
905
  }
906
}
907

    
908
/**
909
 * Drupal public (public://) stream wrapper class.
910
 *
911
 * Provides support for storing publicly accessible files with the Drupal file
912
 * interface.
913
 */
914
class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper {
915
  /**
916
   * Implements abstract public function getDirectoryPath()
917
   */
918
  public function getDirectoryPath() {
919
    return variable_get('file_public_path', conf_path() . '/files');
920
  }
921

    
922
  /**
923
   * Overrides getExternalUrl().
924
   *
925
   * Return the HTML URI of a public file.
926
   */
927
  function getExternalUrl() {
928
    $path = str_replace('\\', '/', $this->getTarget());
929
    return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path);
930
  }
931
}
932

    
933

    
934
/**
935
 * Drupal private (private://) stream wrapper class.
936
 *
937
 * Provides support for storing privately accessible files with the Drupal file
938
 * interface.
939
 */
940
class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper {
941
  /**
942
   * Implements abstract public function getDirectoryPath()
943
   */
944
  public function getDirectoryPath() {
945
    return variable_get('file_private_path', '');
946
  }
947

    
948
  /**
949
   * Overrides getExternalUrl().
950
   *
951
   * Return the HTML URI of a private file.
952
   */
953
  function getExternalUrl() {
954
    $path = str_replace('\\', '/', $this->getTarget());
955
    return url('system/files/' . $path, array('absolute' => TRUE));
956
  }
957
}
958

    
959
/**
960
 * Drupal temporary (temporary://) stream wrapper class.
961
 *
962
 * Provides support for storing temporarily accessible files with the Drupal
963
 * file interface.
964
 *
965
 * Extends DrupalPublicStreamWrapper.
966
 */
967
class DrupalTemporaryStreamWrapper extends DrupalLocalStreamWrapper {
968
  /**
969
   * Implements abstract public function getDirectoryPath()
970
   */
971
  public function getDirectoryPath() {
972
    return variable_get('file_temporary_path', file_directory_temp());
973
  }
974

    
975
  /**
976
   * Overrides getExternalUrl().
977
   */
978
  public function getExternalUrl() {
979
    $path = str_replace('\\', '/', $this->getTarget());
980
    return url('system/temporary/' . $path, array('absolute' => TRUE));
981
  }
982
}