Hoa central
Protocol.php
Go to the documentation of this file.
1 <?php
2 
36 namespace Hoa\Core\Protocol {
37 
38 use Hoa\Core;
39 
40 /*
41  * Class \Hoa\Core\Protocol.
42  *
43  * Abstract class for all hoa://'s components.
44  *
45  * @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
46  * @copyright Copyright © 2007-2015 Ivan Enderlin.
47  * @license New BSD License
48  */
49 
50 abstract class Protocol implements \ArrayAccess, \IteratorAggregate
51 {
57  const NO_RESOLUTION = '/hoa/flatland';
58 
64  protected $_name = null;
65 
71  protected $_reach = null;
72 
78  private $_components = [];
79 
85  private static $_cache = [];
86 
87 
88 
99  public function __construct($name = null, $reach = null)
100  {
101  if (null !== $name) {
102  $this->_name = $name;
103  }
104 
105  if (null !== $reach) {
106  $this->_reach = $reach;
107  }
108 
109  return;
110  }
111 
121  public function offsetSet($name, $component)
122  {
123  if (!($component instanceof self)) {
124  throw new Core\Exception(
125  'Component must extend %s.',
126  0,
127  __CLASS__
128  );
129  }
130 
131  if (empty($name)) {
132  $name = $component->getName();
133  }
134 
135  if (empty($name)) {
136  throw new Core\Exception(
137  'Cannot add a component to the protocol hoa:// without a name.',
138  1
139  );
140  }
141 
142  $this->_components[$name] = $component;
143 
144  return;
145  }
146 
154  public function offsetGet($name)
155  {
156  if (!isset($this[$name])) {
157  throw new Core\Exception(
158  'Component %s does not exist.',
159  2,
160  $name
161  );
162  }
163 
164  return $this->_components[$name];
165  }
166 
173  public function offsetExists($name)
174  {
175  return array_key_exists($name, $this->_components);
176  }
177 
184  public function offsetUnset($name)
185  {
186  unset($this->_components[$name]);
187 
188  return;
189  }
190 
201  public function resolve($path, $exists = true, $unfold = false)
202  {
203  if (substr($path, 0, 6) !== 'hoa://') {
204  return $path;
205  }
206 
207  if (isset(self::$_cache[$path])) {
208  $handle = self::$_cache[$path];
209  } else {
210  $out = $this->_resolve($path, $handle);
211 
212  // Not a path but a resource.
213  if (!is_array($handle)) {
214  return $out;
215  }
216 
217  $handle = array_values(array_unique($handle, SORT_REGULAR));
218 
219  self::$_cache[$path] = $handle;
220  }
221 
222  if (true === $unfold) {
223  if (true !== $exists) {
224  return $handle;
225  }
226 
227  $out = [];
228 
229  foreach ($handle as $solution) {
230  if (file_exists($solution)) {
231  $out[] = $solution;
232  }
233  }
234 
235  return $out;
236  }
237 
238  if (true !== $exists) {
239  return $handle[0];
240  }
241 
242  foreach ($handle as $solution) {
243  if (file_exists($solution)) {
244  return $solution;
245  }
246  }
247 
248  return static::NO_RESOLUTION;
249  }
250 
260  protected function _resolve($path, &$accumulator, $id = null)
261  {
262  if (substr($path, 0, 6) == 'hoa://') {
263  $path = substr($path, 6);
264  }
265 
266  if (empty($path)) {
267  return null;
268  }
269 
270  if (null === $accumulator) {
271  $accumulator = [];
272  $posId = strpos($path, '#');
273 
274  if (false !== $posId) {
275  $id = substr($path, $posId + 1);
276  $path = substr($path, 0, $posId);
277  } else {
278  $id = null;
279  }
280  }
281 
282  $path = trim($path, '/');
283  $pos = strpos($path, '/');
284 
285  if (false !== $pos) {
286  $next = substr($path, 0, $pos);
287  } else {
288  $next = $path;
289  }
290 
291  if (isset($this[$next])) {
292  if (false === $pos) {
293  if (null === $id) {
294  $this->_resolveChoice($this[$next]->reach(), $accumulator);
295 
296  return true;
297  }
298 
299  $accumulator = null;
300 
301  return $this[$next]->reachId($id);
302  }
303 
304  $tnext = $this[$next];
305  $this->_resolveChoice($tnext->reach(), $accumulator);
306 
307  return $tnext->_resolve(substr($path, $pos + 1), $accumulator, $id);
308  }
309 
310  $this->_resolveChoice($this->reach($path), $accumulator);
311 
312  return true;
313  }
314 
322  protected function _resolveChoice($reach, Array &$accumulator)
323  {
324  if (empty($accumulator)) {
325  $accumulator = explode(';', $reach);
326 
327  return;
328  }
329 
330  if (false === strpos($reach, ';')) {
331  if (false !== $pos = strrpos($reach, "\r")) {
332  $reach = substr($reach, $pos + 1);
333 
334  foreach ($accumulator as &$entry) {
335  $entry = null;
336  }
337  }
338 
339  foreach ($accumulator as &$entry) {
340  $entry .= $reach;
341  }
342 
343  return;
344  }
345 
346  $choices = explode(';', $reach);
347  $ref = $accumulator;
348  $accumulator = [];
349 
350  foreach ($choices as $choice) {
351  if (false !== $pos = strrpos($choice, "\r")) {
352  $choice = substr($choice, $pos + 1);
353 
354  foreach ($ref as $entry) {
355  $accumulator[] = $choice;
356  }
357  } else {
358  foreach ($ref as $entry) {
359  $accumulator[] = $entry . $choice;
360  }
361  }
362  }
363 
364  unset($ref);
365 
366  return;
367  }
368 
374  public static function clearCache()
375  {
376  self::$_cache = [];
377 
378  return;
379  }
380 
389  public function reach($queue = null)
390  {
391  return empty($queue) ? $this->_reach : $queue;
392  }
393 
402  public function reachId($id)
403  {
404  throw new Core\Exception(
405  'The component %s has no ID support (tried to reach #%s).',
406  4,
407  [$this->getName(), $id]
408  );
409  }
410 
417  public function setReach($reach)
418  {
419  $old = $this->_reach;
420  $this->_reach = $reach;
421 
422  return $old;
423  }
424 
430  public function getName()
431  {
432  return $this->_name;
433  }
434 
440  protected function getReach()
441  {
442  return $this->_reach;
443  }
444 
450  public function getIterator()
451  {
452  return new \ArrayIterator($this->_components);
453  }
454 
460  public function __toString()
461  {
462  static $i = 0;
463 
464  $out = str_repeat(' ', $i) . $this->getName() . "\n";
465 
466  foreach ($this as $foo => $component) {
467  ++$i;
468  $out .= $component;
469  --$i;
470  }
471 
472  return $out;
473  }
474 }
475 
479 class_alias('Hoa\Core\Protocol\Protocol', 'Hoa\Core\Protocol');
480 
489 class Generic extends Protocol
490 {
491 }
492 
501 class Root extends Protocol
502 {
508  protected $_name = 'hoa://';
509 }
510 
519 class Library extends Protocol
520 {
528  public function reach($queue = null)
529  {
530  if (!WITH_COMPOSER) {
531  return parent::reach($queue);
532  }
533 
534  if (!empty($queue)) {
535  $head = $queue;
536 
537  if (false !== $pos = strpos($queue, '/')) {
538  $head = substr($head, 0, $pos);
539  $queue = DS . substr($queue, $pos + 1);
540  } else {
541  $queue = null;
542  }
543 
544  $out = [];
545 
546  foreach (explode(';', $this->_reach) as $part) {
547  $out[] = "\r" . $part . strtolower($head) . $queue;
548  }
549 
550  $out[] = "\r" . dirname(dirname(dirname(__DIR__))) . $queue;
551 
552  return implode(';', $out);
553  }
554 
555  $out = [];
556 
557  foreach (explode(';', $this->_reach) as $part) {
558  $pos = strrpos(rtrim($part, DS), DS) + 1;
559  $head = substr($part, 0, $pos);
560  $tail = substr($part, $pos);
561  $out[] = $head . strtolower($tail);
562  }
563 
564  $this->_reach = implode(';', $out);
565 
566  return parent::reach($queue);
567  }
568 }
569 
578 class Wrapper
579 {
585  private $_stream = null;
586 
592  private $_streamName = null;
593 
599  public $context = null;
600 
601 
602 
611  public static function realPath($path, $exists = true)
612  {
613  return Core::getProtocol()->resolve($path, $exists);
614  }
615 
625  public function stream_cast($castAs)
626  {
627  return false;
628  }
629 
638  public function stream_close()
639  {
640  if (true === @fclose($this->getStream())) {
641  $this->_stream = null;
642  $this->_streamName = null;
643  }
644 
645  return;
646  }
647 
655  public function stream_eof()
656  {
657  return feof($this->getStream());
658  }
659 
668  public function stream_flush()
669  {
670  return fflush($this->getStream());
671  }
672 
688  public function stream_lock($operation)
689  {
690  return flock($this->getStream(), $operation);
691  }
692 
710  public function stream_metadata($path, $option, $values)
711  {
712  $path = static::realPath($path, false);
713 
714  switch ($option) {
715  case STREAM_META_TOUCH:
716  $arity = count($values);
717 
718  if (0 === $arity) {
719  $out = touch($path);
720  } elseif (1 === $arity) {
721  $out = touch($path, $values[0]);
722  } else {
723  $out = touch($path, $values[0], $values[1]);
724  }
725 
726  break;
727 
728  case STREAM_META_OWNER_NAME:
729  case STREAM_META_OWNER:
730  $out = chown($path, $values);
731 
732  break;
733 
734  case STREAM_META_GROUP_NAME:
735  case STREAM_META_GROUP:
736  $out = chgrp($path, $values);
737 
738  break;
739 
740  case STREAM_META_ACCESS:
741  $out = chmod($path, $values);
742 
743  break;
744  }
745 
746  return $out;
747  }
748 
776  public function stream_open($path, $mode, $options, &$openedPath)
777  {
778  $path = static::realPath($path, 'r' === $mode[0]);
779 
780  if (Protocol::NO_RESOLUTION === $path) {
781  return false;
782  }
783 
784  if (null === $this->context) {
785  $openedPath = fopen($path, $mode, $options & STREAM_USE_PATH);
786  } else {
787  $openedPath = fopen(
788  $path,
789  $mode,
790  $options & STREAM_USE_PATH,
791  $this->context
792  );
793  }
794 
795  if (false === is_resource($openedPath)) {
796  return false;
797  }
798 
799  $this->_stream = $openedPath;
800  $this->_streamName = $path;
801 
802  return true;
803  }
804 
813  public function stream_read($count)
814  {
815  return fread($this->getStream(), $count);
816  }
817 
834  public function stream_seek($offset, $whence = SEEK_SET)
835  {
836  return 0 === fseek($this->getStream(), $offset, $whence);
837  }
838 
845  public function stream_stat()
846  {
847  return fstat($this->getStream());
848  }
849 
856  public function stream_tell()
857  {
858  return ftell($this->getStream());
859  }
860 
867  public function stream_truncate($size)
868  {
869  return ftruncate($this->getStream(), $size);
870  }
871 
879  public function stream_write($data)
880  {
881  return fwrite($this->getStream(), $data);
882  }
883 
892  public function dir_closedir()
893  {
894  if (true === $handle = @closedir($this->getStream())) {
895  $this->_stream = null;
896  $this->_streamName = null;
897  }
898 
899  return $handle;
900  }
901 
911  public function dir_opendir($path, $options)
912  {
913  $path = static::realPath($path);
914  $handle = null;
915 
916  if (null === $this->context) {
917  $handle = @opendir($path);
918  } else {
919  $handle = @opendir($path, $this->context);
920  }
921 
922  if (false === $handle) {
923  return false;
924  }
925 
926  $this->_stream = $handle;
927  $this->_streamName = $path;
928 
929  return true;
930  }
931 
938  public function dir_readdir()
939  {
940  return readdir($this->getStream());
941  }
942 
952  public function dir_rewinddir()
953  {
954  return rewinddir($this->getStream());
955  }
956 
966  public function mkdir($path, $mode, $options)
967  {
968  if (null === $this->context) {
969  return mkdir(
970  static::realPath($path, false),
971  $mode,
972  $options | STREAM_MKDIR_RECURSIVE
973  );
974  }
975 
976  return mkdir(
977  static::realPath($path, false),
978  $mode,
979  $options | STREAM_MKDIR_RECURSIVE,
980  $this->context
981  );
982  }
983 
993  public function rename($from, $to)
994  {
995  if (null === $this->context) {
996  return rename(static::realPath($from), static::realPath($to, false));
997  }
998 
999  return rename(
1000  static::realPath($from),
1001  static::realPath($to, false),
1002  $this->context
1003  );
1004  }
1005 
1015  public function rmdir($path, $options)
1016  {
1017  if (null === $this->context) {
1018  return rmdir(static::realPath($path));
1019  }
1020 
1021  return rmdir(static::realPath($path), $this->context);
1022  }
1023 
1031  public function unlink($path)
1032  {
1033  if (null === $this->context) {
1034  return unlink(static::realPath($path));
1035  }
1036 
1037  return unlink(static::realPath($path), $this->context);
1038  }
1039 
1064  public function url_stat($path, $flags)
1065  {
1066  $path = static::realPath($path);
1067 
1068  if (Protocol::NO_RESOLUTION === $path) {
1069  if ($flags & STREAM_URL_STAT_QUIET) {
1070  return 0;
1071  } else {
1072  return trigger_error(
1073  'Path ' . $path . ' cannot be resolved.',
1074  E_WARNING
1075  );
1076  }
1077  }
1078 
1079  if ($flags & STREAM_URL_STAT_LINK) {
1080  return @lstat($path);
1081  }
1082 
1083  return @stat($path);
1084  }
1085 
1091  protected function getStream()
1092  {
1093  return $this->_stream;
1094  }
1095 
1101  protected function getStreamName()
1102  {
1103  return $this->_streamName;
1104  }
1105 }
1106 
1107 }
1108 
1109 namespace {
1110 
1121 if (!function_exists('resolve')) {
1122  function resolve($path, $exists = true, $unfold = false)
1123  {
1124  return Hoa\Core::getInstance()->getProtocol()->resolve(
1125  $path,
1126  $exists,
1127  $unfold
1128  );
1129  }
1130 }
1131 
1135 stream_wrapper_register('hoa', '\Hoa\Core\Protocol\Wrapper');
1136 
1137 }
rmdir($path, $options)
Definition: Protocol.php:1015
stream_metadata($path, $option, $values)
Definition: Protocol.php:710
offsetSet($name, $component)
Definition: Protocol.php:121
url_stat($path, $flags)
Definition: Protocol.php:1064
dir_opendir($path, $options)
Definition: Protocol.php:911
stream_seek($offset, $whence=SEEK_SET)
Definition: Protocol.php:834
static realPath($path, $exists=true)
Definition: Protocol.php:611
__construct($name=null, $reach=null)
Definition: Protocol.php:99
static getProtocol()
Definition: Core.php:352
static getInstance()
Definition: Core.php:193
_resolve($path, &$accumulator, $id=null)
Definition: Protocol.php:260
resolve($path, $exists=true, $unfold=false)
Definition: Protocol.php:201
mkdir($path, $mode, $options)
Definition: Protocol.php:966
stream_open($path, $mode, $options, &$openedPath)
Definition: Protocol.php:776
_resolveChoice($reach, Array &$accumulator)
Definition: Protocol.php:322