PageRenderTime 49ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/Protocol.php

https://github.com/bdelespierre/Core
PHP | 1064 lines | 345 code | 198 blank | 521 comment | 45 complexity | 2b743bb75806b4402dd6340ec5f6eff5 MD5 | raw file
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2013, Ivan Enderlin. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Core\Protocol {
  36. /**
  37. * Class \Hoa\Core\Protocol.
  38. *
  39. * Abstract class for all hoa://'s components.
  40. *
  41. * @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
  42. * @copyright Copyright © 2007-2013 Ivan Enderlin.
  43. * @license New BSD License
  44. */
  45. abstract class Protocol implements \ArrayAccess, \IteratorAggregate {
  46. /**
  47. * No resolution value.
  48. *
  49. * @const string
  50. */
  51. const NO_RESOLUTION = '/hoa/flatland';
  52. /**
  53. * Component's name.
  54. *
  55. * @var \Hoa\Core\Protocol string
  56. */
  57. protected $_name = null;
  58. /**
  59. * Path for the reach() method.
  60. *
  61. * @var \Hoa\Core\Protocol string
  62. */
  63. protected $_reach = null;
  64. /**
  65. * Collections of sub-components.
  66. *
  67. * @var \Hoa\Core\Protocol array
  68. */
  69. private $_components = array();
  70. /**
  71. * Cache of resolver.
  72. *
  73. * @var \Hoa\Core\Protocol array
  74. */
  75. private static $_cache = array();
  76. /**
  77. * Construct a protocol's component.
  78. * If it is not a data object (i.e. if it is not extend this class to
  79. * overload the $this->_name property), we can set the $this->_name property
  80. * dynamically. So usefull to create components on the fly…
  81. *
  82. * @access public
  83. * @param string $name Component's name.
  84. * @param string $reach Path for the reach() method.
  85. * @return void
  86. */
  87. public function __construct ( $name = null, $reach = null ) {
  88. if(null !== $name)
  89. $this->_name = $name;
  90. if(null !== $reach)
  91. $this->_reach = $reach;
  92. return;
  93. }
  94. /**
  95. * Add a component.
  96. *
  97. * @access public
  98. * @param string $name Component name. If null, will
  99. * be set to name of $component.
  100. * @param \Hoa\Core\Protocol $component Component to add.
  101. * @return \Hoa\Core\Protocol
  102. * @throws \Hoa\Core\Exception
  103. */
  104. public function offsetSet ( $name, $component ) {
  105. if(!($component instanceof Protocol))
  106. throw new \Hoa\Core\Exception(
  107. 'Component must extend %s.', 0, __CLASS__);
  108. if(empty($name))
  109. $name = $component->getName();
  110. if(empty($name))
  111. throw new \Hoa\Core\Exception(
  112. 'Cannot add a component to the protocol hoa:// without a name.',
  113. 1);
  114. $this->_components[$name] = $component;
  115. return;
  116. }
  117. /**
  118. * Get a specific component.
  119. *
  120. * @access public
  121. * @param string $name Component name.
  122. * @return \Hoa\Core\Protocol
  123. * @throw \Hoa\Core\Exception
  124. */
  125. public function offsetGet ( $name ) {
  126. if(!isset($this[$name]))
  127. throw new \Hoa\Core\Exception(
  128. 'Component %s does not exist.', 2, $name);
  129. return $this->_components[$name];
  130. }
  131. /**
  132. * Check if a component exists.
  133. *
  134. * @access public
  135. * @param string $name Component name.
  136. * @return bool
  137. */
  138. public function offsetExists ( $name ) {
  139. return array_key_exists($name, $this->_components);
  140. }
  141. /**
  142. * Remove a component.
  143. *
  144. * @access public
  145. * @param string $name Component name to remove.
  146. * @return \Hoa\Core\Protocol
  147. */
  148. public function offsetUnset ( $name ) {
  149. unset($this->_components[$name]);
  150. return;
  151. }
  152. /**
  153. * Front method for resolving a path. Please, look the $this->_resolve()
  154. * method.
  155. *
  156. * @access public
  157. * @param string $path Path to resolve.
  158. * @param bool $exists If true, try to find the first that exists,
  159. * else return the first solution.
  160. * @param bool $unfold Return all solutions instead of one.
  161. * @return mixed
  162. */
  163. public function resolve ( $path, $exists = true, $unfold = false ) {
  164. if(substr($path, 0, 6) !== 'hoa://')
  165. return $path;
  166. if(isset(self::$_cache[$path]))
  167. $handle = self::$_cache[$path];
  168. else {
  169. $out = $this->_resolve($path, $handle);
  170. // Not a path but a resource.
  171. if(!is_array($handle))
  172. return $out;
  173. self::$_cache[$path] = $handle;
  174. }
  175. if(true === $unfold) {
  176. if(true !== $exists)
  177. return $handle;
  178. $out = array();
  179. foreach($handle as $solution)
  180. if(file_exists($solution))
  181. $out[] = $solution;
  182. return $out;
  183. }
  184. if(true !== $exists)
  185. return $handle[0];
  186. foreach($handle as $solution)
  187. if(file_exists($solution))
  188. return $solution;
  189. return static::NO_RESOLUTION;
  190. }
  191. /**
  192. * Resolve a path, i.e. iterate the components tree and reach the queue of
  193. * the path.
  194. *
  195. * @access public
  196. * @param string $path Path to resolve.
  197. * @param array &$accumulator Combination of all possibles paths.
  198. * @param string $id ID.
  199. * @return mixed
  200. */
  201. protected function _resolve ( $path, &$accumulator, $id = null ) {
  202. if(substr($path, 0, 6) == 'hoa://')
  203. $path = substr($path, 6);
  204. if(empty($path))
  205. return null;
  206. if(null === $accumulator) {
  207. $accumulator = array();
  208. $posId = strpos($path, '#');
  209. if(false !== $posId) {
  210. $id = substr($path, $posId + 1);
  211. $path = substr($path, 0, $posId);
  212. }
  213. else
  214. $id = null;
  215. }
  216. $path = trim($path, '/');
  217. $pos = strpos($path, '/');
  218. if(false !== $pos)
  219. $next = substr($path, 0, $pos);
  220. else
  221. $next = $path;
  222. if(isset($this[$next])) {
  223. if(false === $pos) {
  224. if(null === $id) {
  225. $this->_resolveChoice($this[$next]->reach(), $accumulator);
  226. return true;
  227. }
  228. $accumulator = null;
  229. return $this[$next]->reachId($id);
  230. }
  231. $tnext = $this[$next];
  232. $this->_resolveChoice($tnext->reach(), $accumulator);
  233. return $tnext->_resolve(substr($path, $pos + 1), $accumulator, $id);
  234. }
  235. $this->_resolveChoice($this->reach($path), $accumulator);
  236. return true;
  237. }
  238. /**
  239. * Resolve choices, i.e. a reach value has a “;”.
  240. *
  241. * @access public
  242. * @param string $reach Reach value.
  243. * @param array &$accumulator Combination of all possibles paths.
  244. * @return void
  245. */
  246. protected function _resolveChoice ( $reach, Array &$accumulator ) {
  247. if(empty($accumulator)) {
  248. $accumulator = explode(';', $reach);
  249. return;
  250. }
  251. if(false === strpos($reach, ';')) {
  252. if(false !== $pos = strrpos($reach, "\r")) {
  253. $reach = substr($reach, $pos + 1);
  254. foreach($accumulator as &$entry)
  255. $entry = null;
  256. }
  257. foreach($accumulator as &$entry)
  258. $entry .= $reach;
  259. return;
  260. }
  261. $choices = explode(';', $reach);
  262. $ref = $accumulator;
  263. $accumulator = array();
  264. foreach($choices as $choice) {
  265. if(false !== $pos = strrpos($choice, "\r")) {
  266. $choice = substr($choice, $pos + 1);
  267. foreach($ref as $entry)
  268. $accumulator[] = $choice;
  269. }
  270. else
  271. foreach($ref as $entry)
  272. $accumulator[] = $entry . $choice;
  273. }
  274. unset($ref);
  275. return;
  276. }
  277. /**
  278. * Clear cache.
  279. *
  280. * @access public
  281. * @return void
  282. */
  283. public static function clearCache ( ) {
  284. self::$_cache = array();
  285. return;
  286. }
  287. /**
  288. * Queue of the component.
  289. * Generic one. Should be overload in children classes.
  290. *
  291. * @access public
  292. * @param string $queue Queue of the component (generally, a filename,
  293. * with probably a query).
  294. * @return mixed
  295. */
  296. public function reach ( $queue = null ) {
  297. return empty($queue) ? $this->_reach : $queue;
  298. }
  299. /**
  300. * ID of the component.
  301. * Generic one. Should be overload in children classes.
  302. *
  303. * @access public
  304. * @param string $id ID of the component.
  305. * @return mixed
  306. * @throw \Hoa\Core\Exception
  307. */
  308. public function reachId ( $id ) {
  309. throw new \Hoa\Core\Exception(
  310. 'The component %s has no ID support (tried to reach #%s).',
  311. 4, array($this->getName(), $id));
  312. }
  313. /**
  314. * Set a new reach value.
  315. *
  316. * @access public
  317. * @param string $reach Reach value.
  318. * @return string
  319. */
  320. public function setReach ( $reach ) {
  321. $old = $this->_reach;
  322. $this->_reach = $reach;
  323. return $old;
  324. }
  325. /**
  326. * Get component's name.
  327. *
  328. * @access public
  329. * @return string
  330. */
  331. public function getName ( ) {
  332. return $this->_name;
  333. }
  334. /**
  335. * Get reach's root.
  336. *
  337. * @access protected
  338. * @return string
  339. */
  340. protected function getReach ( ) {
  341. return $this->_reach;
  342. }
  343. /**
  344. * Get an iterator.
  345. *
  346. * @access public
  347. * @return \ArrayObject
  348. */
  349. public function getIterator ( ) {
  350. return new \ArrayObject($this->_components);
  351. }
  352. /**
  353. * Print a tree of component.
  354. *
  355. * @access public
  356. * @return string
  357. */
  358. public function __toString ( ) {
  359. static $i = 0;
  360. $out = str_repeat(' ', $i) . $this->getName() . "\n";
  361. foreach($this as $foo => $component) {
  362. $i++;
  363. $out .= $component;
  364. $i--;
  365. }
  366. return $out;
  367. }
  368. }
  369. /**
  370. * Make the alias automatically (because it's not imported with the import()
  371. * function).
  372. */
  373. class_alias('Hoa\Core\Protocol\Protocol', 'Hoa\Core\Protocol');
  374. /**
  375. * Class \Hoa\Core\Protocol\Generic.
  376. *
  377. * hoa://'s protocol's generic component.
  378. *
  379. * @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
  380. * @copyright Copyright © 2007-2013 Ivan Enderlin.
  381. * @license New BSD License
  382. */
  383. class Generic extends Protocol { }
  384. /**
  385. * Class \Hoa\Core\Protocol\Root.
  386. *
  387. * hoa://'s protocol's root.
  388. *
  389. * @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
  390. * @copyright Copyright © 2007-2013 Ivan Enderlin.
  391. * @license New BSD License
  392. */
  393. class Root extends Protocol {
  394. /**
  395. * Component's name.
  396. *
  397. * @var \Hoa\Core\Protocol\Root string
  398. */
  399. protected $_name = 'hoa://';
  400. }
  401. /**
  402. * Class \Hoa\Core\Protocol\Library.
  403. *
  404. * Library protocol's component.
  405. *
  406. * @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
  407. * @copyright Copyright © 2007-2013 Ivan Enderlin.
  408. * @license New BSD License
  409. */
  410. class Library extends Protocol {
  411. /**
  412. * Queue of the component.
  413. *
  414. * @access public
  415. * @param string $queue Queue of the component (generally, a filename,
  416. * with probably a query).
  417. * @return mixed
  418. */
  419. public function reach ( $queue = null ) {
  420. if(!WITH_COMPOSER)
  421. return parent::reach($queue);
  422. if(!empty($queue)) {
  423. $pos = strpos($queue, DS);
  424. $queue = strtolower(substr($queue, 0, $pos)) . substr($queue, $pos);
  425. }
  426. else {
  427. $out = array();
  428. foreach(explode(';', $this->_reach) as $part) {
  429. $pos = strrpos(rtrim($part, DS), DS) + 1;
  430. $head = substr($part, 0, $pos);
  431. $tail = substr($part, $pos);
  432. $out[] = $head . strtolower($tail);
  433. }
  434. $this->_reach = implode(';', $out);
  435. }
  436. return parent::reach($queue);
  437. }
  438. }
  439. /**
  440. * Class \Hoa\Core\Protocol\Wrapper.
  441. *
  442. * Wrapper for hoa://'s protocol.
  443. *
  444. * @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
  445. * @copyright Copyright © 2007-2013 Ivan Enderlin.
  446. * @license New BSD License
  447. */
  448. class Wrapper {
  449. /**
  450. * Opened stream.
  451. *
  452. * @var \Hoa\Core\Protocol\Wrapper resource
  453. */
  454. private $_stream = null;
  455. /**
  456. * Stream name (filename).
  457. *
  458. * @var \Hoa\Core\Protocol\Wrapper string
  459. */
  460. private $_streamName = null;
  461. /**
  462. * Stream context (given by the streamWrapper class).
  463. *
  464. * @var \Hoa\Core\Protocol\Wrapper resource
  465. */
  466. public $context = null;
  467. /**
  468. * Get the real path of the given URL.
  469. * Could return false if the path cannot be reached.
  470. *
  471. * @access public
  472. * @param string $path Path (or URL).
  473. * @param bool $exists If true, try to find the first that exists,
  474. * @return mixed
  475. */
  476. public static function realPath ( $path, $exists = true ) {
  477. return \Hoa\Core::getProtocol()->resolve($path, $exists);
  478. }
  479. /**
  480. * Close a resource.
  481. * This method is called in response to fclose().
  482. * All resources that were locked, or allocated, by the wrapper should be
  483. * released.
  484. *
  485. * @access public
  486. * @return void
  487. */
  488. public function stream_close ( ) {
  489. if(true === @fclose($this->getStream())) {
  490. $this->_stream = null;
  491. $this->_streamName = null;
  492. }
  493. return;
  494. }
  495. /**
  496. * Tests for end-of-file on a file pointer.
  497. * This method is called in response to feof().
  498. *
  499. * access public
  500. * @return bool
  501. */
  502. public function stream_eof ( ) {
  503. return feof($this->getStream());
  504. }
  505. /**
  506. * Flush the output.
  507. * This method is called in respond to fflush().
  508. * If we have cached data in our stream but not yet stored it into the
  509. * underlying storage, we should do so now.
  510. *
  511. * @access public
  512. * @return bool
  513. */
  514. public function stream_flush ( ) {
  515. return fflush($this->getStream());
  516. }
  517. /**
  518. * Advisory file locking.
  519. * This method is called in response to flock(), when file_put_contents()
  520. * (when flags contains LOCK_EX), stream_set_blocking() and when closing the
  521. * stream (LOCK_UN).
  522. *
  523. * @access public
  524. * @param int $operation Operation is one the following:
  525. * * LOCK_SH to acquire a shared lock (reader) ;
  526. * * LOCK_EX to acquire an exclusive lock (writer) ;
  527. * * LOCK_UN to release a lock (shared or exclusive) ;
  528. * * LOCK_NB if we don't want flock() to
  529. * block while locking (not supported on
  530. * Windows).
  531. * @return bool
  532. */
  533. public function stream_lock ( $operation ) {
  534. return flock($this->getStream(), $operation);
  535. }
  536. /**
  537. * Open file or URL.
  538. * This method is called immediately after the wrapper is initialized (f.e.
  539. * by fopen() and file_get_contents()).
  540. *
  541. * @access public
  542. * @param string $path Specifies the URL that was passed to the
  543. * original function.
  544. * @param string $mode The mode used to open the file, as
  545. * detailed for fopen().
  546. * @param int $options Holds additional flags set by the
  547. * streams API. It can hold one or more of
  548. * the following values OR'd together:
  549. * * STREAM_USE_PATH, if path is relative,
  550. * search for the resource using the
  551. * include_path;
  552. * * STREAM_REPORT_ERRORS, if this is
  553. * set, you are responsible for raising
  554. * errors using trigger_error during
  555. * opening the stream. If this is not
  556. * set, you should not raise any errors.
  557. * @param string &$openedPath If the $path is opened successfully, and
  558. * STREAM_USE_PATH is set in $options,
  559. * $openedPath should be set to the full
  560. * path of the file/resource that was
  561. * actually opened.
  562. * @return bool
  563. */
  564. public function stream_open ( $path, $mode, $options, &$openedPath ) {
  565. $path = self::realPath($path, 'r' === $mode[0]);
  566. if(Protocol::NO_RESOLUTION === $path)
  567. return false;
  568. if(null === $this->context)
  569. $openedPath = fopen($path, $mode, $options & STREAM_USE_PATH);
  570. else
  571. $openedPath = fopen(
  572. $path,
  573. $mode,
  574. $options & STREAM_USE_PATH,
  575. $this->context
  576. );
  577. $this->_stream = $openedPath;
  578. $this->_streamName = $path;
  579. return true;
  580. }
  581. /**
  582. * Read from stream.
  583. * This method is called in response to fread() and fgets().
  584. *
  585. * @access public
  586. * @param int $count How many bytes of data from the current
  587. * position should be returned.
  588. * @return string
  589. */
  590. public function stream_read ( $count ) {
  591. return fread($this->getStream(), $count);
  592. }
  593. /**
  594. * Seek to specific location in a stream.
  595. * This method is called in response to fseek().
  596. * The read/write position of the stream should be updated according to the
  597. * $offset and $whence.
  598. *
  599. * @access public
  600. * @param int $offset The stream offset to seek to.
  601. * @param int $whence Possible values:
  602. * * SEEK_SET to set position equal to $offset
  603. * bytes ;
  604. * * SEEK_CUR to set position to current
  605. * location plus $offsete ;
  606. * * SEEK_END to set position to end-of-file
  607. * plus $offset.
  608. * @return bool
  609. */
  610. public function stream_seek ( $offset, $whence = SEEK_SET ) {
  611. return fseek($this->getStream(), $offset, $whence);
  612. }
  613. /**
  614. * Retrieve information about a file resource.
  615. * This method is called in response to fstat().
  616. *
  617. * @access public
  618. * @return array
  619. */
  620. public function stream_stat ( ) {
  621. return fstat($this->getStream());
  622. }
  623. /**
  624. * Retrieve the current position of a stream.
  625. * This method is called in response to ftell().
  626. *
  627. * @access public
  628. * @return int
  629. */
  630. public function stream_tell ( ) {
  631. return ftell($this->getStream());
  632. }
  633. /**
  634. * Truncate a stream to a given length.
  635. *
  636. * @access public
  637. * @param int $size Size.
  638. * @return bool
  639. */
  640. public function stream_truncate ( $size ) {
  641. return ftruncate($this->getStream(), $size);
  642. }
  643. /**
  644. * Write to stream.
  645. * This method is called in response to fwrite().
  646. *
  647. * @access public
  648. * @param string $data Should be stored into the underlying stream.
  649. * @return int
  650. */
  651. public function stream_write ( $data ) {
  652. return fwrite($this->getStream(), $data);
  653. }
  654. /**
  655. * Close directory handle.
  656. * This method is called in to closedir().
  657. * Any resources which were locked, or allocated, during opening and use of
  658. * the directory stream should be released.
  659. *
  660. * @access public
  661. * @return bool
  662. */
  663. public function dir_closedir ( ) {
  664. if(true === $handle = @closedir($this->getStream())) {
  665. $this->_stream = null;
  666. $this->_streamName = null;
  667. }
  668. return $handle;
  669. }
  670. /**
  671. * Open directory handle.
  672. * This method is called in response to opendir().
  673. *
  674. * @access public
  675. * @param string $path Specifies the URL that was passed to opendir().
  676. * @param int $options Whether or not to enforce safe_mode (0x04).
  677. * It is not used here.
  678. * @return bool
  679. */
  680. public function dir_opendir ( $path, $options ) {
  681. $path = self::realPath($path);
  682. $handle = null;
  683. if(null === $this->context)
  684. $handle = @opendir($path);
  685. else
  686. $handle = @opendir($path, $this->context);
  687. if(false === $handle)
  688. return false;
  689. $this->_stream = $handle;
  690. $this->_streamName = $path;
  691. return true;
  692. }
  693. /**
  694. * Read entry from directory handle.
  695. * This method is called in response to readdir().
  696. *
  697. * @access public
  698. * @return mixed
  699. */
  700. public function dir_readdir ( ) {
  701. return readdir($this->getStream());
  702. }
  703. /**
  704. * Rewind directory handle.
  705. * This method is called in response to rewinddir().
  706. * Should reset the output generated by self::dir_readdir, i.e. the next
  707. * call to self::dir_readdir should return the first entry in the location
  708. * returned by self::dir_opendir.
  709. *
  710. * @access public
  711. * @return bool
  712. */
  713. public function dir_rewinddir ( ) {
  714. return rewinddir($this->getStream());
  715. }
  716. /**
  717. * Create a directory.
  718. * This method is called in response to mkdir().
  719. *
  720. * @access public
  721. * @param string $path Directory which should be created.
  722. * @param int $mode The value passed to mkdir().
  723. * @param int $options A bitwise mask of values.
  724. * @return bool
  725. */
  726. public function mkdir ( $path, $mode, $options ) {
  727. if(null === $this->context)
  728. return mkdir(
  729. self::realPath($path, false),
  730. $mode,
  731. $options | STREAM_MKDIR_RECURSIVE
  732. );
  733. return mkdir(
  734. self::realPath($path, false),
  735. $mode,
  736. $options | STREAM_MKDIR_RECURSIVE,
  737. $this->context
  738. );
  739. }
  740. /**
  741. * Rename a file or directory.
  742. * This method is called in response to rename().
  743. * Should attempt to rename $from to $to.
  744. *
  745. * @access public
  746. * @param string $from The URL to current file.
  747. * @param string $to The URL which $from should be renamed to.
  748. * @return bool
  749. */
  750. public function rename ( $from, $to ) {
  751. if(null === $this->context)
  752. return rename(self::realPath($from), self::realPath($to, false));
  753. return rename(
  754. self::realPath($from),
  755. self::realPath($to, false),
  756. $this->context
  757. );
  758. }
  759. /**
  760. * Remove a directory.
  761. * This method is called in response to rmdir().
  762. *
  763. * @access public
  764. * @param string $path The directory URL which should be removed.
  765. * @param int $options A bitwise mask of values. It is not used
  766. * here.
  767. * @return bool
  768. */
  769. public function rmdir ( $path, $options ) {
  770. if(null === $this->context)
  771. return rmdir(self::realPath($path));
  772. return rmdir(self::realPath($path), $this->context);
  773. }
  774. /**
  775. * Delete a file.
  776. * This method is called in response to unlink().
  777. *
  778. * @access public
  779. * @param string $path The file URL which should be deleted.
  780. * @return bool
  781. */
  782. public function unlink ( $path ) {
  783. if(null === $this->context)
  784. return unlink(self::realPath($path));
  785. return unlink(self::realPath($path), $this->context);
  786. }
  787. /**
  788. * Retrieve information about a file.
  789. * This method is called in response to all stat() related functions.
  790. *
  791. * @access public
  792. * @param string $path The file URL which should be retrieve
  793. * information about.
  794. * @param int $flags Holds additional flags set by the streams API.
  795. * It can hold one or more of the following
  796. * values OR'd together.
  797. * STREAM_URL_STAT_LINK: for resource with the
  798. * ability to link to other resource (such as an
  799. * HTTP location: forward, or a filesystem
  800. * symlink). This flag specified that only
  801. * information about the link itself should be
  802. * returned, not the resource pointed to by the
  803. * link. This flag is set in response to calls to
  804. * lstat(), is_link(), or filetype().
  805. * STREAM_URL_STAT_QUIET: if this flag is set,
  806. * our wrapper should not raise any errors. If
  807. * this flag is not set, we are responsible for
  808. * reporting errors using the trigger_error()
  809. * function during stating of the path.
  810. * @return array
  811. */
  812. public function url_stat ( $path, $flags ) {
  813. $path = self::realPath($path);
  814. if(Protocol::NO_RESOLUTION === $path)
  815. if($flags & STREAM_URL_STAT_QUIET)
  816. return 0;
  817. else
  818. return trigger_error(
  819. 'Path ' . $path . ' cannot be resolved.',
  820. E_WARNING
  821. );
  822. if($flags & STREAM_URL_STAT_LINK)
  823. return @lstat($path);
  824. return @stat($path);
  825. }
  826. /**
  827. * Get stream resource.
  828. *
  829. * @access protected
  830. * @return resource
  831. */
  832. protected function getStream ( ) {
  833. return $this->_stream;
  834. }
  835. /**
  836. * Get stream name.
  837. *
  838. * @access protected
  839. * @return resource
  840. */
  841. protected function getStreamName ( ) {
  842. return $this->_streamName;
  843. }
  844. }
  845. }
  846. namespace {
  847. /**
  848. * Alias of the \Hoa\Core::getInstance()->getProtocol()->resolve() method.
  849. * method.
  850. *
  851. * @access public
  852. * @param string $path Path to resolve.
  853. * @param bool $exists If true, try to find the first that exists,
  854. * else return the first solution.
  855. * @param bool $unfold Return all solutions instead of one.
  856. * @return mixed
  857. */
  858. if(!ƒ('resolve')) {
  859. function resolve ( $path, $exists = true, $unfold = false ) {
  860. return \Hoa\Core::getInstance()->getProtocol()->resolve($path, $exists, $unfold);
  861. }}
  862. /**
  863. * Register the hoa:// protocol.
  864. */
  865. stream_wrapper_register('hoa', '\Hoa\Core\Protocol\Wrapper');
  866. }