stream_wrappers.inc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. <?php
  2. /**
  3. * @file
  4. * Drupal stream wrapper interface.
  5. *
  6. * Provides a Drupal interface and classes to implement PHP stream wrappers for
  7. * public, private, and temporary files.
  8. *
  9. * A stream wrapper is an abstraction of a file system that allows Drupal to
  10. * use the same set of methods to access both local files and remote resources.
  11. *
  12. * Note that PHP 5.2 fopen() only supports URIs of the form "scheme://target"
  13. * despite the fact that according to RFC 3986 a URI's scheme component
  14. * delimiter is in general just ":", not "://". Because of this PHP limitation
  15. * and for consistency Drupal will only accept URIs of form "scheme://target".
  16. *
  17. * @see http://www.faqs.org/rfcs/rfc3986.html
  18. * @see http://bugs.php.net/bug.php?id=47070
  19. */
  20. /**
  21. * Stream wrapper bit flags that are the basis for composite types.
  22. *
  23. * Note that 0x0002 is skipped, because it was the value of a constant that has
  24. * since been removed.
  25. */
  26. /**
  27. * Stream wrapper bit flag -- a filter that matches all wrappers.
  28. */
  29. define('STREAM_WRAPPERS_ALL', 0x0000);
  30. /**
  31. * Stream wrapper bit flag -- refers to a local file system location.
  32. */
  33. define('STREAM_WRAPPERS_LOCAL', 0x0001);
  34. /**
  35. * Stream wrapper bit flag -- wrapper is readable (almost always true).
  36. */
  37. define('STREAM_WRAPPERS_READ', 0x0004);
  38. /**
  39. * Stream wrapper bit flag -- wrapper is writeable.
  40. */
  41. define('STREAM_WRAPPERS_WRITE', 0x0008);
  42. /**
  43. * Stream wrapper bit flag -- exposed in the UI and potentially web accessible.
  44. */
  45. define('STREAM_WRAPPERS_VISIBLE', 0x0010);
  46. /**
  47. * Composite stream wrapper bit flags that are usually used as the types.
  48. */
  49. /**
  50. * Stream wrapper type flag -- not visible in the UI or accessible via web,
  51. * but readable and writable. E.g. the temporary directory for uploads.
  52. */
  53. define('STREAM_WRAPPERS_HIDDEN', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE);
  54. /**
  55. * Stream wrapper type flag -- hidden, readable and writeable using local files.
  56. */
  57. define('STREAM_WRAPPERS_LOCAL_HIDDEN', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_HIDDEN);
  58. /**
  59. * Stream wrapper type flag -- visible, readable and writeable.
  60. */
  61. define('STREAM_WRAPPERS_WRITE_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | STREAM_WRAPPERS_VISIBLE);
  62. /**
  63. * Stream wrapper type flag -- visible and read-only.
  64. */
  65. define('STREAM_WRAPPERS_READ_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_VISIBLE);
  66. /**
  67. * Stream wrapper type flag -- the default when 'type' is omitted from
  68. * hook_stream_wrappers(). This does not include STREAM_WRAPPERS_LOCAL,
  69. * because PHP grants a greater trust level to local files (for example, they
  70. * can be used in an "include" statement, regardless of the "allow_url_include"
  71. * setting), so stream wrappers need to explicitly opt-in to this.
  72. */
  73. define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_WRITE_VISIBLE);
  74. /**
  75. * Stream wrapper type flag -- visible, readable and writeable using local files.
  76. */
  77. define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_NORMAL);
  78. /**
  79. * Generic PHP stream wrapper interface.
  80. *
  81. * @see http://www.php.net/manual/class.streamwrapper.php
  82. */
  83. interface StreamWrapperInterface {
  84. public function stream_open($uri, $mode, $options, &$opened_url);
  85. public function stream_close();
  86. public function stream_lock($operation);
  87. public function stream_read($count);
  88. public function stream_write($data);
  89. public function stream_eof();
  90. public function stream_seek($offset, $whence);
  91. public function stream_flush();
  92. public function stream_tell();
  93. public function stream_stat();
  94. public function unlink($uri);
  95. public function rename($from_uri, $to_uri);
  96. public function mkdir($uri, $mode, $options);
  97. public function rmdir($uri, $options);
  98. public function url_stat($uri, $flags);
  99. public function dir_opendir($uri, $options);
  100. public function dir_readdir();
  101. public function dir_rewinddir();
  102. public function dir_closedir();
  103. }
  104. /**
  105. * Drupal stream wrapper extension.
  106. *
  107. * Extend the StreamWrapperInterface with methods expected by Drupal stream
  108. * wrapper classes.
  109. */
  110. interface DrupalStreamWrapperInterface extends StreamWrapperInterface {
  111. /**
  112. * Set the absolute stream resource URI.
  113. *
  114. * This allows you to set the URI. Generally is only called by the factory
  115. * method.
  116. *
  117. * @param $uri
  118. * A string containing the URI that should be used for this instance.
  119. */
  120. public function setUri($uri);
  121. /**
  122. * Returns the stream resource URI.
  123. *
  124. * @return
  125. * Returns the current URI of the instance.
  126. */
  127. public function getUri();
  128. /**
  129. * Returns a web accessible URL for the resource.
  130. *
  131. * This function should return a URL that can be embedded in a web page
  132. * and accessed from a browser. For example, the external URL of
  133. * "youtube://xIpLd0WQKCY" might be
  134. * "http://www.youtube.com/watch?v=xIpLd0WQKCY".
  135. *
  136. * @return
  137. * Returns a string containing a web accessible URL for the resource.
  138. */
  139. public function getExternalUrl();
  140. /**
  141. * Returns the MIME type of the resource.
  142. *
  143. * @param $uri
  144. * The URI, path, or filename.
  145. * @param $mapping
  146. * An optional map of extensions to their mimetypes, in the form:
  147. * - 'mimetypes': a list of mimetypes, keyed by an identifier,
  148. * - 'extensions': the mapping itself, an associative array in which
  149. * the key is the extension and the value is the mimetype identifier.
  150. *
  151. * @return
  152. * Returns a string containing the MIME type of the resource.
  153. */
  154. public static function getMimeType($uri, $mapping = NULL);
  155. /**
  156. * Changes permissions of the resource.
  157. *
  158. * PHP lacks this functionality and it is not part of the official stream
  159. * wrapper interface. This is a custom implementation for Drupal.
  160. *
  161. * @param $mode
  162. * Integer value for the permissions. Consult PHP chmod() documentation
  163. * for more information.
  164. *
  165. * @return
  166. * Returns TRUE on success or FALSE on failure.
  167. */
  168. public function chmod($mode);
  169. /**
  170. * Returns canonical, absolute path of the resource.
  171. *
  172. * Implementation placeholder. PHP's realpath() does not support stream
  173. * wrappers. We provide this as a default so that individual wrappers may
  174. * implement their own solutions.
  175. *
  176. * @return
  177. * Returns a string with absolute pathname on success (implemented
  178. * by core wrappers), or FALSE on failure or if the registered
  179. * wrapper does not provide an implementation.
  180. */
  181. public function realpath();
  182. /**
  183. * Gets the name of the directory from a given path.
  184. *
  185. * This method is usually accessed through drupal_dirname(), which wraps
  186. * around the normal PHP dirname() function, which does not support stream
  187. * wrappers.
  188. *
  189. * @param $uri
  190. * An optional URI.
  191. *
  192. * @return
  193. * A string containing the directory name, or FALSE if not applicable.
  194. *
  195. * @see drupal_dirname()
  196. */
  197. public function dirname($uri = NULL);
  198. }
  199. /**
  200. * Drupal stream wrapper base class for local files.
  201. *
  202. * This class provides a complete stream wrapper implementation. URIs such as
  203. * "public://example.txt" are expanded to a normal filesystem path such as
  204. * "sites/default/files/example.txt" and then PHP filesystem functions are
  205. * invoked.
  206. *
  207. * DrupalLocalStreamWrapper implementations need to implement at least the
  208. * getDirectoryPath() and getExternalUrl() methods.
  209. */
  210. abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface {
  211. /**
  212. * Stream context resource.
  213. *
  214. * @var Resource
  215. */
  216. public $context;
  217. /**
  218. * A generic resource handle.
  219. *
  220. * @var Resource
  221. */
  222. public $handle = NULL;
  223. /**
  224. * Instance URI (stream).
  225. *
  226. * A stream is referenced as "scheme://target".
  227. *
  228. * @var String
  229. */
  230. protected $uri;
  231. /**
  232. * Gets the path that the wrapper is responsible for.
  233. * @TODO: Review this method name in D8 per http://drupal.org/node/701358
  234. *
  235. * @return
  236. * String specifying the path.
  237. */
  238. abstract function getDirectoryPath();
  239. /**
  240. * Base implementation of setUri().
  241. */
  242. function setUri($uri) {
  243. $this->uri = $uri;
  244. }
  245. /**
  246. * Base implementation of getUri().
  247. */
  248. function getUri() {
  249. return $this->uri;
  250. }
  251. /**
  252. * Returns the local writable target of the resource within the stream.
  253. *
  254. * This function should be used in place of calls to realpath() or similar
  255. * functions when attempting to determine the location of a file. While
  256. * functions like realpath() may return the location of a read-only file, this
  257. * method may return a URI or path suitable for writing that is completely
  258. * separate from the URI used for reading.
  259. *
  260. * @param $uri
  261. * Optional URI.
  262. *
  263. * @return
  264. * Returns a string representing a location suitable for writing of a file,
  265. * or FALSE if unable to write to the file such as with read-only streams.
  266. */
  267. protected function getTarget($uri = NULL) {
  268. if (!isset($uri)) {
  269. $uri = $this->uri;
  270. }
  271. list($scheme, $target) = explode('://', $uri, 2);
  272. // Remove erroneous leading or trailing, forward-slashes and backslashes.
  273. return trim($target, '\/');
  274. }
  275. /**
  276. * Base implementation of getMimeType().
  277. */
  278. static function getMimeType($uri, $mapping = NULL) {
  279. if (!isset($mapping)) {
  280. // The default file map, defined in file.mimetypes.inc is quite big.
  281. // We only load it when necessary.
  282. include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
  283. $mapping = file_mimetype_mapping();
  284. }
  285. $extension = '';
  286. $file_parts = explode('.', drupal_basename($uri));
  287. // Remove the first part: a full filename should not match an extension.
  288. array_shift($file_parts);
  289. // Iterate over the file parts, trying to find a match.
  290. // For my.awesome.image.jpeg, we try:
  291. // - jpeg
  292. // - image.jpeg, and
  293. // - awesome.image.jpeg
  294. while ($additional_part = array_pop($file_parts)) {
  295. $extension = strtolower($additional_part . ($extension ? '.' . $extension : ''));
  296. if (isset($mapping['extensions'][$extension])) {
  297. return $mapping['mimetypes'][$mapping['extensions'][$extension]];
  298. }
  299. }
  300. return 'application/octet-stream';
  301. }
  302. /**
  303. * Base implementation of chmod().
  304. */
  305. function chmod($mode) {
  306. $output = @chmod($this->getLocalPath(), $mode);
  307. // We are modifying the underlying file here, so we have to clear the stat
  308. // cache so that PHP understands that URI has changed too.
  309. clearstatcache();
  310. return $output;
  311. }
  312. /**
  313. * Base implementation of realpath().
  314. */
  315. function realpath() {
  316. return $this->getLocalPath();
  317. }
  318. /**
  319. * Returns the canonical absolute path of the URI, if possible.
  320. *
  321. * @param string $uri
  322. * (optional) The stream wrapper URI to be converted to a canonical
  323. * absolute path. This may point to a directory or another type of file.
  324. *
  325. * @return string|false
  326. * If $uri is not set, returns the canonical absolute path of the URI
  327. * previously set by the DrupalStreamWrapperInterface::setUri() function.
  328. * If $uri is set and valid for this class, returns its canonical absolute
  329. * path, as determined by the realpath() function. If $uri is set but not
  330. * valid, returns FALSE.
  331. */
  332. protected function getLocalPath($uri = NULL) {
  333. if (!isset($uri)) {
  334. $uri = $this->uri;
  335. }
  336. $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
  337. $realpath = realpath($path);
  338. if (!$realpath) {
  339. // This file does not yet exist.
  340. $realpath = realpath(dirname($path)) . '/' . drupal_basename($path);
  341. }
  342. $directory = realpath($this->getDirectoryPath());
  343. if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
  344. return FALSE;
  345. }
  346. return $realpath;
  347. }
  348. /**
  349. * Support for fopen(), file_get_contents(), file_put_contents() etc.
  350. *
  351. * @param $uri
  352. * A string containing the URI to the file to open.
  353. * @param $mode
  354. * The file mode ("r", "wb" etc.).
  355. * @param $options
  356. * A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
  357. * @param $opened_path
  358. * A string containing the path actually opened.
  359. *
  360. * @return
  361. * Returns TRUE if file was opened successfully.
  362. *
  363. * @see http://php.net/manual/streamwrapper.stream-open.php
  364. */
  365. public function stream_open($uri, $mode, $options, &$opened_path) {
  366. $this->uri = $uri;
  367. $path = $this->getLocalPath();
  368. $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
  369. if ((bool) $this->handle && $options & STREAM_USE_PATH) {
  370. $opened_path = $path;
  371. }
  372. return (bool) $this->handle;
  373. }
  374. /**
  375. * Support for flock().
  376. *
  377. * @param $operation
  378. * One of the following:
  379. * - LOCK_SH to acquire a shared lock (reader).
  380. * - LOCK_EX to acquire an exclusive lock (writer).
  381. * - LOCK_UN to release a lock (shared or exclusive).
  382. * - LOCK_NB if you don't want flock() to block while locking (not
  383. * supported on Windows).
  384. *
  385. * @return
  386. * Always returns TRUE at the present time.
  387. *
  388. * @see http://php.net/manual/streamwrapper.stream-lock.php
  389. */
  390. public function stream_lock($operation) {
  391. if (in_array($operation, array(LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB))) {
  392. return flock($this->handle, $operation);
  393. }
  394. return TRUE;
  395. }
  396. /**
  397. * Support for fread(), file_get_contents() etc.
  398. *
  399. * @param $count
  400. * Maximum number of bytes to be read.
  401. *
  402. * @return
  403. * The string that was read, or FALSE in case of an error.
  404. *
  405. * @see http://php.net/manual/streamwrapper.stream-read.php
  406. */
  407. public function stream_read($count) {
  408. return fread($this->handle, $count);
  409. }
  410. /**
  411. * Support for fwrite(), file_put_contents() etc.
  412. *
  413. * @param $data
  414. * The string to be written.
  415. *
  416. * @return
  417. * The number of bytes written (integer).
  418. *
  419. * @see http://php.net/manual/streamwrapper.stream-write.php
  420. */
  421. public function stream_write($data) {
  422. return fwrite($this->handle, $data);
  423. }
  424. /**
  425. * Support for feof().
  426. *
  427. * @return
  428. * TRUE if end-of-file has been reached.
  429. *
  430. * @see http://php.net/manual/streamwrapper.stream-eof.php
  431. */
  432. public function stream_eof() {
  433. return feof($this->handle);
  434. }
  435. /**
  436. * Support for fseek().
  437. *
  438. * @param $offset
  439. * The byte offset to got to.
  440. * @param $whence
  441. * SEEK_SET, SEEK_CUR, or SEEK_END.
  442. *
  443. * @return
  444. * TRUE on success.
  445. *
  446. * @see http://php.net/manual/streamwrapper.stream-seek.php
  447. */
  448. public function stream_seek($offset, $whence) {
  449. // fseek returns 0 on success and -1 on a failure.
  450. // stream_seek 1 on success and 0 on a failure.
  451. return !fseek($this->handle, $offset, $whence);
  452. }
  453. /**
  454. * Support for fflush().
  455. *
  456. * @return
  457. * TRUE if data was successfully stored (or there was no data to store).
  458. *
  459. * @see http://php.net/manual/streamwrapper.stream-flush.php
  460. */
  461. public function stream_flush() {
  462. return fflush($this->handle);
  463. }
  464. /**
  465. * Support for ftell().
  466. *
  467. * @return
  468. * The current offset in bytes from the beginning of file.
  469. *
  470. * @see http://php.net/manual/streamwrapper.stream-tell.php
  471. */
  472. public function stream_tell() {
  473. return ftell($this->handle);
  474. }
  475. /**
  476. * Support for fstat().
  477. *
  478. * @return
  479. * An array with file status, or FALSE in case of an error - see fstat()
  480. * for a description of this array.
  481. *
  482. * @see http://php.net/manual/streamwrapper.stream-stat.php
  483. */
  484. public function stream_stat() {
  485. return fstat($this->handle);
  486. }
  487. /**
  488. * Support for fclose().
  489. *
  490. * @return
  491. * TRUE if stream was successfully closed.
  492. *
  493. * @see http://php.net/manual/streamwrapper.stream-close.php
  494. */
  495. public function stream_close() {
  496. return fclose($this->handle);
  497. }
  498. /**
  499. * Sets metadata on the stream.
  500. *
  501. * WARNING: Do not call this method directly! It will be called internally by
  502. * PHP itself when one of the following functions is called on a stream URL:
  503. *
  504. * @param string $uri
  505. * A string containing the URI to the file to set metadata on.
  506. * @param int $option
  507. * One of:
  508. * - STREAM_META_TOUCH: The method was called in response to touch().
  509. * - STREAM_META_OWNER_NAME: The method was called in response to chown()
  510. * with string parameter.
  511. * - STREAM_META_OWNER: The method was called in response to chown().
  512. * - STREAM_META_GROUP_NAME: The method was called in response to chgrp().
  513. * - STREAM_META_GROUP: The method was called in response to chgrp().
  514. * - STREAM_META_ACCESS: The method was called in response to chmod().
  515. * @param mixed $value
  516. * If option is:
  517. * - STREAM_META_TOUCH: Array consisting of two arguments of the touch()
  518. * function.
  519. * - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner
  520. * user/group as string.
  521. * - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner
  522. * user/group as integer.
  523. * - STREAM_META_ACCESS: The argument of the chmod() as integer.
  524. *
  525. * @return bool
  526. * Returns TRUE on success or FALSE on failure. If $option is not
  527. * implemented, FALSE should be returned.
  528. *
  529. * @see touch()
  530. * @see chmod()
  531. * @see chown()
  532. * @see chgrp()
  533. * @link http://php.net/manual/streamwrapper.stream-metadata.php
  534. */
  535. public function stream_metadata($uri, $option, $value) {
  536. $target = $this->getLocalPath($uri);
  537. $return = FALSE;
  538. switch ($option) {
  539. case STREAM_META_TOUCH:
  540. if (!empty($value)) {
  541. $return = touch($target, $value[0], $value[1]);
  542. }
  543. else {
  544. $return = touch($target);
  545. }
  546. break;
  547. case STREAM_META_OWNER_NAME:
  548. case STREAM_META_OWNER:
  549. $return = chown($target, $value);
  550. break;
  551. case STREAM_META_GROUP_NAME:
  552. case STREAM_META_GROUP:
  553. $return = chgrp($target, $value);
  554. break;
  555. case STREAM_META_ACCESS:
  556. $return = chmod($target, $value);
  557. break;
  558. }
  559. if ($return) {
  560. // For convenience clear the file status cache of the underlying file,
  561. // since metadata operations are often followed by file status checks.
  562. clearstatcache(TRUE, $target);
  563. }
  564. return $return;
  565. }
  566. /**
  567. * Truncate stream.
  568. *
  569. * Will respond to truncation; e.g., through ftruncate().
  570. *
  571. * @param int $new_size
  572. * The new size.
  573. *
  574. * @return bool
  575. * TRUE on success, FALSE otherwise.
  576. */
  577. public function stream_truncate($new_size) {
  578. return ftruncate($this->handle, $new_size);
  579. }
  580. /**
  581. * Retrieve the underlying stream resource.
  582. *
  583. * This method is called in response to stream_select().
  584. *
  585. * @param int $cast_as
  586. * Can be STREAM_CAST_FOR_SELECT when stream_select() is calling
  587. * stream_cast() or STREAM_CAST_AS_STREAM when stream_cast() is called for
  588. * other uses.
  589. *
  590. * @return resource|false
  591. * The underlying stream resource or FALSE if stream_select() is not
  592. * supported.
  593. *
  594. * @see stream_select()
  595. * @link http://php.net/manual/streamwrapper.stream-cast.php
  596. */
  597. public function stream_cast($cast_as) {
  598. return $this->handle ? $this->handle : FALSE;
  599. }
  600. /**
  601. * Change stream options.
  602. *
  603. * This method is called to set options on the stream.
  604. *
  605. * Since Windows systems do not allow it and it is not needed for most use
  606. * cases anyway, this method is not supported on local files and will trigger
  607. * an error and return false. If needed, custom subclasses can provide
  608. * OS-specific implementations for advanced use cases.
  609. *
  610. * @param int $option
  611. * One of:
  612. * - STREAM_OPTION_BLOCKING: The method was called in response to
  613. * stream_set_blocking().
  614. * - STREAM_OPTION_READ_TIMEOUT: The method was called in response to
  615. * stream_set_timeout().
  616. * - STREAM_OPTION_WRITE_BUFFER: The method was called in response to
  617. * stream_set_write_buffer().
  618. * @param int $arg1
  619. * If option is:
  620. * - STREAM_OPTION_BLOCKING: The requested blocking mode:
  621. * - 1 means blocking.
  622. * - 0 means not blocking.
  623. * - STREAM_OPTION_READ_TIMEOUT: The timeout in seconds.
  624. * - STREAM_OPTION_WRITE_BUFFER: The buffer mode, STREAM_BUFFER_NONE or
  625. * STREAM_BUFFER_FULL.
  626. * @param int $arg2
  627. * If option is:
  628. * - STREAM_OPTION_BLOCKING: This option is not set.
  629. * - STREAM_OPTION_READ_TIMEOUT: The timeout in microseconds.
  630. * - STREAM_OPTION_WRITE_BUFFER: The requested buffer size.
  631. *
  632. * @return bool
  633. * TRUE on success, FALSE otherwise. If $option is not implemented, FALSE
  634. * should be returned.
  635. */
  636. public function stream_set_option($option, $arg1, $arg2) {
  637. trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING);
  638. return FALSE;
  639. }
  640. /**
  641. * Support for unlink().
  642. *
  643. * @param $uri
  644. * A string containing the URI to the resource to delete.
  645. *
  646. * @return
  647. * TRUE if resource was successfully deleted.
  648. *
  649. * @see http://php.net/manual/streamwrapper.unlink.php
  650. */
  651. public function unlink($uri) {
  652. $this->uri = $uri;
  653. return drupal_unlink($this->getLocalPath());
  654. }
  655. /**
  656. * Support for rename().
  657. *
  658. * @param $from_uri,
  659. * The URI to the file to rename.
  660. * @param $to_uri
  661. * The new URI for file.
  662. *
  663. * @return
  664. * TRUE if file was successfully renamed.
  665. *
  666. * @see http://php.net/manual/streamwrapper.rename.php
  667. */
  668. public function rename($from_uri, $to_uri) {
  669. return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
  670. }
  671. /**
  672. * Gets the name of the directory from a given path.
  673. *
  674. * This method is usually accessed through drupal_dirname(), which wraps
  675. * around the PHP dirname() function because it does not support stream
  676. * wrappers.
  677. *
  678. * @param $uri
  679. * A URI or path.
  680. *
  681. * @return
  682. * A string containing the directory name.
  683. *
  684. * @see drupal_dirname()
  685. */
  686. public function dirname($uri = NULL) {
  687. list($scheme, $target) = explode('://', $uri, 2);
  688. $target = $this->getTarget($uri);
  689. $dirname = dirname($target);
  690. if ($dirname == '.') {
  691. $dirname = '';
  692. }
  693. return $scheme . '://' . $dirname;
  694. }
  695. /**
  696. * Support for mkdir().
  697. *
  698. * @param $uri
  699. * A string containing the URI to the directory to create.
  700. * @param $mode
  701. * Permission flags - see mkdir().
  702. * @param $options
  703. * A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
  704. *
  705. * @return
  706. * TRUE if directory was successfully created.
  707. *
  708. * @see http://php.net/manual/streamwrapper.mkdir.php
  709. */
  710. public function mkdir($uri, $mode, $options) {
  711. $this->uri = $uri;
  712. $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
  713. if ($recursive) {
  714. // $this->getLocalPath() fails if $uri has multiple levels of directories
  715. // that do not yet exist.
  716. $localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
  717. }
  718. else {
  719. $localpath = $this->getLocalPath($uri);
  720. }
  721. if ($options & STREAM_REPORT_ERRORS) {
  722. return mkdir($localpath, $mode, $recursive);
  723. }
  724. else {
  725. return @mkdir($localpath, $mode, $recursive);
  726. }
  727. }
  728. /**
  729. * Support for rmdir().
  730. *
  731. * @param $uri
  732. * A string containing the URI to the directory to delete.
  733. * @param $options
  734. * A bit mask of STREAM_REPORT_ERRORS.
  735. *
  736. * @return
  737. * TRUE if directory was successfully removed.
  738. *
  739. * @see http://php.net/manual/streamwrapper.rmdir.php
  740. */
  741. public function rmdir($uri, $options) {
  742. $this->uri = $uri;
  743. if ($options & STREAM_REPORT_ERRORS) {
  744. return drupal_rmdir($this->getLocalPath());
  745. }
  746. else {
  747. return @drupal_rmdir($this->getLocalPath());
  748. }
  749. }
  750. /**
  751. * Support for stat().
  752. *
  753. * @param $uri
  754. * A string containing the URI to get information about.
  755. * @param $flags
  756. * A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
  757. *
  758. * @return
  759. * An array with file status, or FALSE in case of an error - see fstat()
  760. * for a description of this array.
  761. *
  762. * @see http://php.net/manual/streamwrapper.url-stat.php
  763. */
  764. public function url_stat($uri, $flags) {
  765. $this->uri = $uri;
  766. $path = $this->getLocalPath();
  767. // Suppress warnings if requested or if the file or directory does not
  768. // exist. This is consistent with PHP's plain filesystem stream wrapper.
  769. if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) {
  770. return @stat($path);
  771. }
  772. else {
  773. return stat($path);
  774. }
  775. }
  776. /**
  777. * Support for opendir().
  778. *
  779. * @param $uri
  780. * A string containing the URI to the directory to open.
  781. * @param $options
  782. * Unknown (parameter is not documented in PHP Manual).
  783. *
  784. * @return
  785. * TRUE on success.
  786. *
  787. * @see http://php.net/manual/streamwrapper.dir-opendir.php
  788. */
  789. public function dir_opendir($uri, $options) {
  790. $this->uri = $uri;
  791. $this->handle = opendir($this->getLocalPath());
  792. return (bool) $this->handle;
  793. }
  794. /**
  795. * Support for readdir().
  796. *
  797. * @return
  798. * The next filename, or FALSE if there are no more files in the directory.
  799. *
  800. * @see http://php.net/manual/streamwrapper.dir-readdir.php
  801. */
  802. public function dir_readdir() {
  803. return readdir($this->handle);
  804. }
  805. /**
  806. * Support for rewinddir().
  807. *
  808. * @return
  809. * TRUE on success.
  810. *
  811. * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
  812. */
  813. public function dir_rewinddir() {
  814. rewinddir($this->handle);
  815. // We do not really have a way to signal a failure as rewinddir() does not
  816. // have a return value and there is no way to read a directory handler
  817. // without advancing to the next file.
  818. return TRUE;
  819. }
  820. /**
  821. * Support for closedir().
  822. *
  823. * @return
  824. * TRUE on success.
  825. *
  826. * @see http://php.net/manual/streamwrapper.dir-closedir.php
  827. */
  828. public function dir_closedir() {
  829. closedir($this->handle);
  830. // We do not really have a way to signal a failure as closedir() does not
  831. // have a return value.
  832. return TRUE;
  833. }
  834. }
  835. /**
  836. * Drupal public (public://) stream wrapper class.
  837. *
  838. * Provides support for storing publicly accessible files with the Drupal file
  839. * interface.
  840. */
  841. class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper {
  842. /**
  843. * Implements abstract public function getDirectoryPath()
  844. */
  845. public function getDirectoryPath() {
  846. return variable_get('file_public_path', conf_path() . '/files');
  847. }
  848. /**
  849. * Overrides getExternalUrl().
  850. *
  851. * Return the HTML URI of a public file.
  852. */
  853. function getExternalUrl() {
  854. $path = str_replace('\\', '/', $this->getTarget());
  855. return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path);
  856. }
  857. }
  858. /**
  859. * Drupal private (private://) stream wrapper class.
  860. *
  861. * Provides support for storing privately accessible files with the Drupal file
  862. * interface.
  863. */
  864. class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper {
  865. /**
  866. * Implements abstract public function getDirectoryPath()
  867. */
  868. public function getDirectoryPath() {
  869. return variable_get('file_private_path', '');
  870. }
  871. /**
  872. * Overrides getExternalUrl().
  873. *
  874. * Return the HTML URI of a private file.
  875. */
  876. function getExternalUrl() {
  877. $path = str_replace('\\', '/', $this->getTarget());
  878. return url('system/files/' . $path, array('absolute' => TRUE));
  879. }
  880. }
  881. /**
  882. * Drupal temporary (temporary://) stream wrapper class.
  883. *
  884. * Provides support for storing temporarily accessible files with the Drupal
  885. * file interface.
  886. *
  887. * Extends DrupalPublicStreamWrapper.
  888. */
  889. class DrupalTemporaryStreamWrapper extends DrupalLocalStreamWrapper {
  890. /**
  891. * Implements abstract public function getDirectoryPath()
  892. */
  893. public function getDirectoryPath() {
  894. return variable_get('file_temporary_path', file_directory_temp());
  895. }
  896. /**
  897. * Overrides getExternalUrl().
  898. */
  899. public function getExternalUrl() {
  900. $path = str_replace('\\', '/', $this->getTarget());
  901. return url('system/temporary/' . $path, array('absolute' => TRUE));
  902. }
  903. }