Hoa central
Consistency.php
Go to the documentation of this file.
1 <?php
2 
38 
39 use Hoa\Core;
40 use Hoa\Stream;
41 
45 define('PATH_EVENT', __DIR__ . DIRECTORY_SEPARATOR . 'Event.php');
46 define('PATH_EXCEPTION', __DIR__ . DIRECTORY_SEPARATOR . 'Exception.php');
47 define('PATH_DATA', __DIR__ . DIRECTORY_SEPARATOR . 'Data.php');
48 
58 {
64  private static $_multiton = [];
65 
71  protected $_from = null;
72 
78  protected $_roots = [];
79 
85  protected static $_cache = [];
86 
92  protected static $_class = [
93  // Hard-preload.
94  'Hoa\Core\Event' => [
95  'path' => PATH_EVENT,
96  'alias' => null
97  ],
98  'Hoa\Core\Exception' => [
99  'path' => PATH_EXCEPTION,
100  'alias' => null
101  ],
102  'Hoa\Core\Data' => [
103  'path' => PATH_DATA,
104  'alias' => null
105  ]
106  ];
107 
114  protected $__class = [];
115 
116 
117 
124  private function __construct($from)
125  {
126  $this->_from = preg_split('#\s*(,|or)\s*#', trim($from, '()'));
127  $parameters = Core::getInstance()->getParameters();
128  $wildcard = $parameters->getFormattedParameter('namespace.prefix.*');
129 
130  foreach ($this->_from as $f) {
131  $this->setRoot(
132  $parameters->getFormattedParameter('namespace.prefix.' . $f)
133  ?: $wildcard,
134  $f
135  );
136  }
137 
138  return;
139  }
140 
147  public static function from($namespace)
148  {
149  if (!isset(static::$_multiton[$namespace])) {
150  static::$_multiton[$namespace] = new static($namespace);
151  }
152 
153  return static::$_multiton[$namespace];
154  }
155 
163  public function import($pattern, $load = false)
164  {
165  foreach ($this->_from as $from) {
166  $this->_import($from . '.' . $pattern, $load);
167  }
168 
169  return $this;
170  }
171 
179  public function foreachImport($pattern, $callback)
180  {
181  foreach ($this->_from as $from) {
182  $this->_import($from . '.' . $pattern, false, $callback);
183  }
184 
185  return $this;
186  }
187 
196  protected function _import($pattern, $load, $callback = null)
197  {
198  $parts = explode('.', $pattern);
199 
200  if (!isset($parts[1])) {
201  return false;
202  }
203 
204  if (false !== strpos($pattern, '~')) {
205  $handle = null;
206 
207  foreach ($parts as &$part) {
208  if (null !== $handle && '*' !== $handle) {
209  $part = str_replace('~', $handle, $part);
210  }
211 
212  $handle = $part;
213  }
214  }
215 
216  if (false !== strpos($pattern, '*')) {
217  if ('Hoa' !== $parts[0] && 'Hoathis' !== $parts[0]) {
218  return false;
219  }
220 
221  $glob = new \AppendIterator();
222  $ds = preg_quote(DS);
223  $_pattern = '#' . $ds . $parts[0] . $ds . $parts[1] . $ds . '?$#i';
224 
225  foreach (resolve('hoa://Library/' . $parts[1], true, true) as $path) {
226  if (0 !== preg_match($_pattern, $path)) {
227  $glob->append(new \CallbackFilterIterator(
228  new \GlobIterator(
229  $path . DS . implode(DS, array_slice($parts, 2)) . '.php',
230  \GlobIterator::KEY_AS_PATHNAME
231  | \GlobIterator::CURRENT_AS_SELF
232  | \GlobIterator::SKIP_DOTS
233  ),
234  function ($current, $key) use ($path, $parts) {
235  $current->__hoa_pattern =
236  $parts[0] .
237  '.' .
238  $parts[1] .
239  '.' .
240  str_replace(
241  DS,
242  '.',
243  substr($key, strlen($path) + 1, -4)
244  );
245 
246  return true;
247  }
248  ));
249  }
250  }
251 
252  $out = true;
253 
254  foreach ($glob as $filesystem) {
255  $out &= $this->_import($filesystem->__hoa_pattern, $load, $callback);
256  }
257 
258  return (bool) $out;
259  }
260 
261  $classname = implode('\\', $parts);
262  $imported = array_key_exists($classname, static::$_class);
263 
264  if (false === $imported) {
265  static::$_class[$classname] = [
266  'path' => null,
267  'alias' => null
268  ];
269 
270  $count = count($parts);
271 
272  if ($parts[$count - 2] === $parts[$count - 1]) {
273  $alias = implode('\\', array_slice($parts, 0, -1));
274 
275  static::$_class[$classname]['alias'] = $alias;
276  static::$_class[$alias] = $classname;
277  $this->__class[$alias] = &static::$_class[$alias];
278  }
279  }
280 
281  $this->__class[$classname] = &static::$_class[$classname];
282 
283  if (true === $load &&
284  false === static::entityExists($classname, false)) {
285  spl_autoload_call($classname);
286 
287  if (null !== $callback &&
288  true === static::entityExists($classname, false)) {
289  $callback($classname);
290  }
291 
292  return true;
293  }
294 
295  if (null !== $callback) {
296  $callback($classname);
297  }
298 
299  return true;
300  }
301 
308  public static function autoload($classname)
309  {
310  if (false === strpos($classname, '\\')) {
311  return false;
312  }
313 
314  $classname = ltrim($classname, '\\');
315 
316  // Hard-preload.
317  if ('Hoa\Core' === substr($classname, 0, 8) &&
318  false !== ($pos = strpos($classname, '\\', 10)) &&
319  'Bin\\' !== substr($classname, 9, 4)) {
320  require static::$_class[substr($classname, 0, $pos)]['path'];
321 
322  return true;
323  }
324 
325  $head = substr($classname, 0, strpos($classname, '\\'));
326 
327  if (false === array_key_exists($classname, static::$_class)) {
328  $_classname = str_replace('\\', '.', $classname);
329  $out = from($head)->_import($_classname, true);
330 
331  if (false === static::entityExists($classname)) {
332  $out = from($head)->_import($_classname . '.~', true);
333  }
334 
335  return $out;
336  } elseif (is_string($original = static::$_class[$classname])) {
337  spl_autoload_call($original);
338 
339  return true;
340  }
341 
342  $roots = from($head)->getRoot();
343  $classpath = str_replace('\\', DS, $classname) . '.php';
344  $classpathExtended = str_replace(
345  '\\',
346  DS,
347  $classname . substr($classname, strrpos('\\', $classname, 1))
348  ) . '.php';
349 
350  $gotcha = false;
351 
352  foreach ($roots as $vendor => $_roots) {
353  foreach ($_roots as $root) {
354  if (true === file_exists($path = $root . $classpath) ||
355  true === file_exists($path = $root . $classpathExtended)) {
356  $gotcha = true;
357  require $path;
358  static::$_class[$classname]['path'] = $path;
359 
360  break 2;
361  }
362  }
363  }
364 
365  return $gotcha;
366  }
367 
376  public static function dnew($classname, Array $arguments = [])
377  {
378  $classname = ltrim($classname, '\\');
379 
380  if (!class_exists($classname, false)) {
381  $head = substr($classname, 0, $pos = strpos($classname, '\\'));
382  $tail = str_replace('\\', '.', substr($classname, $pos + 1));
383  $from = from($head);
384 
385  foreach ([$tail, $tail . '.~'] as $_tail) {
386  foreach ($from->getFroms() as $_from) {
387  $break = false;
388  $from->_import(
389  $_from . '.' . $_tail,
390  true,
391  function ($_classname) use (&$break, &$classname) {
392  $classname = $_classname;
393  $break = true;
394  }
395  );
396 
397  if (true === $break) {
398  break 2;
399  }
400  }
401  }
402  }
403 
404  $class = new \ReflectionClass($classname);
405 
406  if (empty($arguments) || false === $class->hasMethod('__construct')) {
407  return $class->newInstance();
408  }
409 
410  return $class->newInstanceArgs($arguments);
411  }
412 
421  public function setRoot($root, $from = null)
422  {
423  if (null === $from) {
424  $from = $this->_from[0];
425  }
426 
427  if (!isset($this->_roots[$from])) {
428  $this->_roots[$from] = [];
429  }
430 
431  foreach (explode(';', $root) as $r) {
432  $this->_roots[$from][] = rtrim($r, '/\\') . DS;
433  }
434 
435  return $this;
436  }
437 
443  public function getRoot()
444  {
445  return $this->_roots;
446  }
447 
453  public function getFroms()
454  {
455  return $this->_from;
456  }
457 
463  public function getImportedClasses()
464  {
465  return $this->__class;
466  }
467 
473  public static function getAllImportedClasses()
474  {
475  return static::$_class;
476  }
477 
484  public static function getEntityShortestName($entityName)
485  {
486  $parts = explode('\\', $entityName);
487  $count = count($parts);
488 
489  if ($parts[$count - 2] === $parts[$count - 1]) {
490  return implode('\\', array_slice($parts, 0, -1));
491  }
492 
493  return $entityName;
494  }
495 
503  public static function entityExists($entityName, $autoloader = false)
504  {
505  return
506  class_exists($entityName, $autoloader) ||
507  interface_exists($entityName, false) ||
508  trait_exists($entityName, false);
509  }
510 
517  public static function flexEntity($entityName)
518  {
519  return class_alias(
520  $entityName,
521  static::getEntityShortestName($entityName)
522  );
523  }
524 
531  public static function isKeyword($word)
532  {
533  static $_list = [
534  // PHP keywords.
535  '__halt_compiler',
536  'abstract',
537  'and',
538  'array',
539  'as',
540  'bool',
541  'break',
542  'callable',
543  'case',
544  'catch',
545  'class',
546  'clone',
547  'const',
548  'continue',
549  'declare',
550  'default',
551  'die',
552  'do',
553  'echo',
554  'else',
555  'elseif',
556  'empty',
557  'enddeclare',
558  'endfor',
559  'endforeach',
560  'endif',
561  'endswitch',
562  'endwhile',
563  'eval',
564  'exit',
565  'extends',
566  'false',
567  'final',
568  'float',
569  'for',
570  'foreach',
571  'function',
572  'global',
573  'goto',
574  'if',
575  'implements',
576  'include',
577  'include_once',
578  'instanceof',
579  'insteadof',
580  'int',
581  'interface',
582  'isset',
583  'list',
584  'mixed',
585  'namespace',
586  'new',
587  'null',
588  'numeric',
589  'object',
590  'or',
591  'print',
592  'private',
593  'protected',
594  'public',
595  'require',
596  'require_once',
597  'resource',
598  'return',
599  'static',
600  'string',
601  'switch',
602  'throw',
603  'trait',
604  'true',
605  'try',
606  'unset',
607  'use',
608  'var',
609  'while',
610  'xor',
611  'yield',
612 
613  // Compile-time constants.
614  '__class__',
615  '__dir__',
616  '__file__',
617  '__function__',
618  '__line__',
619  '__method__',
620  '__namespace__',
621  '__trait__'
622  ];
623 
624  return in_array(strtolower($word), $_list);
625  }
626 
633  public static function isIdentifier($id)
634  {
635  return 0 !== preg_match(
636  '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#',
637  $id
638  );
639  }
640 }
641 
653 {
659  protected $_callback = null;
660 
666  protected $_hash = null;
667 
668 
669 
686  public function __construct($call, $able = '')
687  {
688  if (null === $call) {
689  return null;
690  }
691 
692  if ($call instanceof \Closure) {
693  $this->_callback = $call;
694 
695  return;
696  }
697 
698  if (!is_string($able)) {
699  throw new Core\Exception(
700  'Bad callback form.',
701  0
702  );
703  }
704 
705  if ('' === $able) {
706  if (is_string($call)) {
707  if (false === strpos($call, '::')) {
708  if (!function_exists($call)) {
709  throw new Core\Exception(
710  'Bad callback form.',
711  1
712  );
713  }
714 
715  $this->_callback = $call;
716 
717  return;
718  }
719 
720  list($call, $able) = explode('::', $call);
721  } elseif (is_object($call)) {
722  if ($call instanceof Stream\IStream\Out) {
723  $able = null;
724  } elseif (method_exists($call, '__invoke')) {
725  $able = '__invoke';
726  } else {
727  throw new Core\Exception(
728  'Bad callback form.',
729  2
730  );
731  }
732  } elseif (is_array($call) && isset($call[0])) {
733  if (!isset($call[1])) {
734  return $this->__construct($call[0]);
735  }
736 
737  return $this->__construct($call[0], $call[1]);
738  } else {
739  throw new Core\Exception(
740  'Bad callback form.',
741  3
742  );
743  }
744  }
745 
746  $this->_callback = [$call, $able];
747 
748  return;
749  }
750 
757  public function __invoke()
758  {
759  $arguments = func_get_args();
760  $valid = $this->getValidCallback($arguments);
761 
762  return call_user_func_array($valid, $arguments);
763  }
764 
771  public function distributeArguments(Array $arguments)
772  {
773  return call_user_func_array([$this, '__invoke'], $arguments);
774  }
775 
783  public function getValidCallback(Array &$arguments = [])
784  {
785  $callback = $this->_callback;
786  $head = null;
787 
788  if (isset($arguments[0])) {
789  $head = &$arguments[0];
790  }
791 
792  // If method is undetermined, we find it (we understand event bucket and
793  // stream).
794  if (null !== $head &&
795  is_array($callback) &&
796  null === $callback[1]) {
797  if ($head instanceof Core\Event\Bucket) {
798  $head = $head->getData();
799  }
800 
801  switch ($type = gettype($head)) {
802  case 'string':
803  if (1 === strlen($head)) {
804  $method = 'writeCharacter';
805  } else {
806  $method = 'writeString';
807  }
808 
809  break;
810 
811  case 'boolean':
812  case 'integer':
813  case 'array':
814  $method = 'write' . ucfirst($type);
815 
816  break;
817 
818  case 'double':
819  $method = 'writeFloat';
820 
821  break;
822 
823  default:
824  $method = 'writeAll';
825  $head = $head . "\n";
826  }
827 
828  $callback[1] = $method;
829  }
830 
831  return $callback;
832  }
833 
844  public function getHash()
845  {
846  if (null !== $this->_hash) {
847  return $this->_hash;
848  }
849 
850  $_ = &$this->_callback;
851 
852  if (is_string($_)) {
853  return $this->_hash = 'function#' . $_;
854  }
855 
856  if (is_array($_)) {
857  return
858  $this->_hash =
859  (is_object($_[0])
860  ? 'object(' . spl_object_hash($_[0]) . ')' .
861  '#' . get_class($_[0])
862  : 'class#' . $_[0]) .
863  '::' .
864  (null !== $_[1]
865  ? $_[1]
866  : '???');
867  }
868 
869  return $this->_hash = 'closure(' . spl_object_hash($_) . ')';
870  }
871 
878  public function getReflection()
879  {
880  $arguments = func_get_args();
881  $valid = $this->getValidCallback($arguments);
882 
883  if (is_string($valid)) {
884  return new \ReflectionFunction($valid);
885  }
886 
887  if ($valid instanceof \Closure) {
888  return new \ReflectionFunction($valid);
889  }
890 
891  if (is_array($valid)) {
892  if (is_string($valid[0])) {
893  if (false === method_exists($valid[0], $valid[1])) {
894  return new \ReflectionClass($valid[0]);
895  }
896 
897  return new \ReflectionMethod($valid[0], $valid[1]);
898  }
899 
900  $object = new \ReflectionObject($valid[0]);
901 
902  if (null === $valid[1]) {
903  return $object;
904  }
905 
906  return $object->getMethod($valid[1]);
907  }
908  }
909 
915  public function __toString()
916  {
917  return $this->getHash();
918  }
919 }
920 
921 }
922 
923 namespace {
924 
925 
933 if (!function_exists('trait_exists')) {
934  function trait_exists($traitname, $autoload = true)
935  {
936  if (true == $autoload) {
937  class_exists($traitname, true);
938  }
939 
940  return false;
941  }
942 }
943 
944 if (70000 > PHP_VERSION_ID) {
948  interface Throwable
949  {
950  public function getMessage();
951  public function getCode();
952  public function getFile();
953  public function getLine();
954  public function getTrace();
955  public function getPrevious();
956  public function getTraceAsString();
957  public function __toString();
958  }
959 }
960 
967 if (!function_exists('from')) {
968  function from($namespace)
969  {
970  return Hoa\Core\Consistency::from($namespace);
971  }
972 }
973 
981 if (!function_exists('dnew')) {
982  function dnew($classname, Array $arguments = [])
983  {
984  return Hoa\Core\Consistency::dnew($classname, $arguments);
985  }
986 }
987 
995 if (!function_exists('xcallable')) {
996  function xcallable($call, $able = '')
997  {
998  if ($call instanceof Hoa\Core\Consistency\Xcallable) {
999  return $call;
1000  }
1001 
1002  return new Hoa\Core\Consistency\Xcallable($call, $able);
1003  }
1004 }
1005 
1026 if (!function_exists('curry')) {
1027  function curry($callable)
1028  {
1029  $arguments = func_get_args();
1030  array_shift($arguments);
1031  $ii = array_keys($arguments, …, true);
1032 
1033  return function () use ($callable, $arguments, $ii) {
1034  return call_user_func_array(
1035  $callable,
1036  array_replace($arguments, array_combine($ii, func_get_args()))
1037  );
1038  };
1039  }
1040 }
1041 
1049 if (!function_exists('curry_ref')) {
1050  function curry_ref(
1051  &$callable,
1052  &$a = null,
1053  &$b = null,
1054  &$c = null,
1055  &$d = null,
1056  &$e = null,
1057  &$f = null,
1058  &$g = null,
1059  &$h = null,
1060  &$i = null,
1061  &$j = null,
1062  &$k = null,
1063  &$l = null,
1064  &$m = null,
1065  &$n = null,
1066  &$o = null,
1067  &$p = null,
1068  &$q = null,
1069  &$r = null,
1070  &$s = null,
1071  &$t = null,
1072  &$u = null,
1073  &$v = null,
1074  &$w = null,
1075  &$x = null,
1076  &$y = null,
1077  &$z = null
1078  ) {
1079  $arguments = [];
1080 
1081  for ($i = 0, $max = func_num_args() - 1; $i < $max; ++$i) {
1082  $arguments[] = &${chr(97 + $i)};
1083  }
1084 
1085  $ii = array_keys($arguments, …, true);
1086 
1087  return function () use (&$callable, &$arguments, $ii) {
1088  return call_user_func_array(
1089  $callable,
1090  array_replace($arguments, array_combine($ii, func_get_args()))
1091  );
1092  };
1093  }
1094 }
1095 
1100 class_alias('Hoa\Core\Consistency\Consistency', 'Hoa\Core\Consistency');
1101 
1105 spl_autoload_register('\Hoa\Core\Consistency::autoload');
1106 
1107 }
_import($pattern, $load, $callback=null)
getValidCallback(Array &$arguments=[])
static dnew($classname, Array $arguments=[])
static entityExists($entityName, $autoloader=false)
static getInstance()
Definition: Core.php:193
distributeArguments(Array $arguments)
foreachImport($pattern, $callback)
static flexEntity($entityName)
static getEntityShortestName($entityName)
__construct($call, $able= '')