Hoa central
Concrete.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Xyl\Element;
38 
39 use Hoa\Stream;
41 use Hoa\Xml;
42 use Hoa\Xyl;
43 
53 abstract class Concrete extends Xml\Element\Concrete implements Element
54 {
61 
68 
74  const ATTRIBUTE_TYPE_ID = 2;
75 
82 
89 
95  const ATTRIBUTE_TYPE_LINK = 16;
96 
102  private $_bucket = ['data' => null];
103 
109  private $_attributeBucket = null;
110 
116  protected $_visibility = true;
117 
123  protected $_transientValue = null;
124 
132  protected static $_attributes = [
133  'id' => self::ATTRIBUTE_TYPE_ID,
134  'title' => self::ATTRIBUTE_TYPE_NORMAL,
135  'lang' => self::ATTRIBUTE_TYPE_NORMAL,
136  'translate' => self::ATTRIBUTE_TYPE_NORMAL,
137  'dir' => self::ATTRIBUTE_TYPE_NORMAL,
138  'class' => self::ATTRIBUTE_TYPE_LIST,
139  'style' => self::ATTRIBUTE_TYPE_NORMAL,
140  'data' => self::ATTRIBUTE_TYPE_CUSTOM,
141  'aria' => self::ATTRIBUTE_TYPE_CUSTOM,
142  'role' => self::ATTRIBUTE_TYPE_NORMAL
143  ];
144 
150  protected $_lastIteration = false;
151 
152 
153 
162  public function computeDataBinding(Array &$data, Array &$parent = null)
163  {
164  $executable = $this instanceof Executable;
165  $bindable = $this->abstract->attributeExists('bind');
166 
167  if (false === $bindable) {
168  foreach (static::getDeclaredAttributes() as $attribute => $type) {
169  $bindable |=
170  0 !== preg_match(
171  '#\(\?[^\)]+\)#',
172  $this->abstract->readAttribute($attribute)
173  );
174  }
175 
176  $bindable = (bool) $bindable;
177  }
178 
179  // Propagate binding.
180  if (false === $bindable) {
181  $executable and $this->preExecute();
182 
183  foreach ($this as $element) {
184  $element->computeDataBinding($data, $parent);
185  }
186 
187  $executable and $this->postExecute();
188 
189  return;
190  }
191 
192  // Inner-binding.
193  if (false === $this->abstract->attributeExists('bind')) {
194  if (null === $parent) {
195  $parent = [
196  'parent' => null,
197  'current' => 0,
198  'branche' => '_',
199  'data' => [[
200  '_' => $data
201  ]]
202  ];
203  }
204 
205  $this->_attributeBucket = &$parent;
206  $executable and $this->preExecute();
207 
208  foreach ($this as $element) {
209  $element->computeDataBinding($data, $parent);
210  }
211 
212  $executable and $this->postExecute();
213 
214  return;
215  }
216 
217  // Binding.
218  $this->_bucket['parent'] = &$parent;
219  $this->_bucket['current'] = 0;
220  $this->_bucket['branche'] = $bind = $this->selectData(
221  $this->abstract->readAttribute('bind'),
222  $data
223  );
224 
225  if (null === $parent) {
226  $this->_bucket['data'] = $data;
227  }
228 
229  $bindable and $this->_attributeBucket = &$parent;
230  $executable and $this->preExecute();
231 
232  if (isset($data[0][$bind])) {
233  if (is_string($data[0][$bind])) {
234  return;
235  }
236 
237  foreach ($this as $element) {
238  $element->computeDataBinding($data[0][$bind], $this->_bucket);
239  }
240  }
241 
242  $executable and $this->postExecute();
243 
244  return;
245  }
246 
257  protected function selectData($expression, Array &$bucket)
258  {
259  switch (Xyl::getSelector($expression, $matches)) {
260  case Xyl::SELECTOR_PATH:
261  $split = preg_split(
262  '#(?<!\\\)\/#',
263  $matches[1]
264  );
265 
266  foreach ($split as &$s) {
267  $s = str_replace('\/', '/', $s);
268  }
269 
270  $branche = array_pop($split);
271  $handle = &$bucket;
272 
273  foreach ($split as $part) {
274  $handle = &$bucket[0][$part];
275  }
276 
277  $bucket = $handle;
278 
279  return $branche;
280 
281  case Xyl::SELECTOR_QUERY:
282  var_dump('*** QUERY');
283  var_dump($matches);
284 
285  break;
286 
287  case Xyl::SELECTOR_XPATH:
288  var_dump('*** XPATH');
289  var_dump($matches);
290 
291  break;
292  }
293 
294  return null;
295  }
296 
302  protected function getCurrentData()
303  {
304  if (empty($this->_bucket['data'])) {
305  return;
306  }
307 
308  $current = $this->_bucket['data'][$this->_bucket['current']];
309 
310  if (!isset($current[$this->_bucket['branche']])) {
311  return null;
312  }
313 
314  return $current[$this->_bucket['branche']];
315  }
316 
322  private function firstUpdate()
323  {
324  if (!isset($this->_bucket['parent'])) {
325  return;
326  }
327 
328  $parent = &$this->_bucket['parent'];
329  $this->_bucket['data'] = &$parent['data'][$parent['current']][$parent['branche']];
330  reset($this->_bucket['data']);
331  $this->_bucket['current'] = 0;
332 
333  if (!isset($this->_bucket['data'][0])) {
334  unset($this->_bucket['data']);
335  $this->_bucket['data'] = [
336  &$parent['data'][$parent['current']][$parent['branche']]
337  ];
338  }
339 
340  return;
341  }
342 
348  private function update()
349  {
350  if (!is_array($this->_bucket['data'])) {
351  return false;
352  }
353 
354  $this->_bucket['current'] = key($this->_bucket['data']);
355  $handle = current($this->_bucket['data']);
356 
357  return isset($handle[$this->_bucket['branche']]);
358  }
359 
366  public function render(Stream\IStream\Out $out)
367  {
368  if (false === $this->getVisibility()) {
369  return;
370  }
371 
372  $this->firstUpdate();
373 
374  if (isset($this->_bucket['branche']) &&
375  (empty($this->_bucket['data']) ||
376  empty($this->_bucket['data'][$this->_bucket['current']][$this->_bucket['branche']]))) {
377  return;
378  }
379 
380  $data = &$this->_bucket['data'];
381 
382  do {
383  $next = is_array($data) ? next($data) : false;
384  $this->_lastIteration = false === $next;
385 
386  $this->paint($out);
387  $next = $next && $this->update();
388  } while (false !== $next);
389 
390  return;
391  }
392 
399  abstract protected function paint(Stream\IStream\Out $out);
400 
410  public function computeValue(Stream\IStream\Out $out = null)
411  {
412  $data = false;
413 
414  if (true === $this->abstract->attributeExists('bind')) {
415  $data = $this->_transientValue = $this->getCurrentData();
416  }
417 
418  if (null === $out) {
419  if (false !== $data) {
420  return $data;
421  } else {
422  return $data = $this->_transientValue = $this->abstract->readAll();
423  }
424  }
425 
426  if (0 === count($this)) {
427  if (false !== $data) {
428  $out->writeAll($data);
429  } else {
430  $out->writeAll($this->abstract->readAll());
431  }
432 
433  return;
434  }
435 
436  foreach ($this as $child) {
437  $child->render($out);
438  }
439 
440  return;
441  }
442 
451  public function computeTransientValue(Stream\IStream\Out $out = null)
452  {
453  $data = $this->_transientValue;
454 
455  if (null === $data) {
456  return $this->computeValue($out);
457  }
458 
459  if (null === $out) {
460  return $data;
461  }
462 
463  $out->writeAll($data);
464 
465  return;
466  }
467 
473  protected function cleanTransientValue()
474  {
475  $this->_transientValue = null;
476 
477  return;
478  }
479 
488  public function computeAttributeValue(
489  $value,
490  $type = self::ATTRIBUTE_TYPE_UNKNOWN,
491  $name = null
492  ) {
493  /*
494  // (!variable).
495  $value = preg_replace_callback(
496  '#\(\!([^\)]+)\)#',
497  function ( Array $matches ) use ( &$variables ) {
498 
499  if(!isset($variables[$matches[1]]))
500  return '';
501 
502  return $variables[$matches[1]];
503  },
504  $value
505  );
506  */
507 
508  // (?inner-bind).
509  if (null !== $this->_attributeBucket ||
510  !empty($this->_bucket['data'])) {
511  if (null === $this->_attributeBucket) {
512  $handle = &$this->_bucket;
513  $data = $handle['data'][$handle['current']];
514  } else {
515  $handle = &$this->_attributeBucket;
516  $data = $handle['data'][$handle['current']][$handle['branche']];
517  }
518 
519  if (is_array($data) && isset($data[0])) {
520  $data = $data[0];
521  }
522 
523  $value = preg_replace_callback(
524  '#\(\?(?:p(?:ath)?:)?([^\)]+)\)#',
525  function (Array $matches) use (&$data) {
526  if (!is_array($data) || !isset($data[$matches[1]])) {
527  return '';
528  }
529 
530  return $data[$matches[1]];
531  },
532  $value
533  );
534  }
535 
536  // Link.
537  if (self::ATTRIBUTE_TYPE_LINK === $type ||
538  self::ATTRIBUTE_TYPE_UNKNOWN === $type) {
539  $value = $this->getAbstractElementSuperRoot()->computeLink($value);
540  }
541 
542  // Formatter.
543  if (null !== $name &&
544  true === $this->abstract->attributeExists($name . '-formatter')) {
545  $value = $this->formatValue($value, $name . '-');
546  }
547 
548  return $value;
549  }
550 
563  protected function formatValue($value, $name = null)
564  {
565  $_formatter = $name . 'formatter';
566  $formatter = $this->abstract->readAttribute($_formatter);
567  $arguments = $this->abstract->readCustomAttributes($_formatter);
568 
569  foreach ($arguments as &$argument) {
570  $argument = $this->_formatValue(
571  $this->computeAttributeValue($argument)
572  );
573  }
574 
575  $reflection = xcallable($formatter)->getReflection();
576  $distribution = [];
577  $placeholder = $this->_formatValue($value);
578 
579  foreach ($reflection->getParameters() as $parameter) {
580  $name = strtolower($parameter->getName());
581 
582  if (true === array_key_exists($name, $arguments)) {
583  $distribution[$name] = $arguments[$name];
584 
585  continue;
586  } elseif (null !== $placeholder) {
587  $distribution[$name] = $placeholder;
588  $placeholder = null;
589  }
590  }
591 
592  if ($reflection instanceof \ReflectionMethod) {
593  $value = $reflection->invokeArgs(null, $distribution);
594  } else {
595  $value = $reflection->invokeArgs($distribution);
596  }
597 
598  return $value;
599  }
600 
607  protected function _formatValue($value)
608  {
609  if (ctype_digit($value)) {
610  $value = intval($value);
611  } elseif (is_numeric($value)) {
612  $value = floatval($value);
613  } elseif ('true' === $value) {
614  $value = true;
615  } elseif ('false' === $value) {
616  $value = false;
617  } elseif ('null' === $value) {
618  $value = null;
619  }
620  // what about constants?
621 
622  return $value;
623  }
624 
630  protected function computeFromString($xyl)
631  {
632  if (0 < count($this)) {
633  return null;
634  }
635 
636  $stringBuffer = new Stringbuffer\ReadWrite();
637  $stringBuffer->initializeWith(
638  '<?xml version="1.0" encoding="utf-8"?>' .
639  '<fragment xmlns="' . \Hoa\Xyl::NAMESPACE_ID . '">' .
640  '<snippet id="h"><yield>' . $xyl . '</yield></snippet>' .
641  '</fragment>'
642  );
643 
644  $root = $this->getAbstractElementSuperRoot();
645  $fragment = $root->open($stringBuffer->getStreamName());
646  $fragment->render($fragment->getSnippet('h'));
647 
648  return;
649  }
650 
656  public function isLastIteration()
657  {
658  return $this->_lastIteration;
659  }
660 
666  protected function getDeclaredAttributes()
667  {
668  $out = static::_getDeclaredAttributes();
670 
671  foreach ($out as $attr => $type) {
672  if (self::ATTRIBUTE_TYPE_CUSTOM === $type) {
673  foreach ($abstract->readCustomAttributes($attr) as $a => $_) {
674  $out[$attr . '-' . $a] = self::ATTRIBUTE_TYPE_UNKNOWN;
675  }
676  }
677  }
678 
679  return $out;
680  }
681 
687  protected static function _getDeclaredAttributes()
688  {
689  $out = [];
690  $parent = get_called_class();
691 
692  do {
693  if (!isset($parent::$_attributes)) {
694  continue;
695  }
696 
697  $out = array_merge($out, $parent::$_attributes);
698  } while (false !== ($parent = get_parent_class($parent)));
699 
700  return $out;
701  }
702 
709  public function setVisibility($visibility)
710  {
711  $old = $this->_visibility;
712  $this->_visibility = $visibility;
713 
714  return $old;
715  }
716 
722  public function getVisibility()
723  {
724  return $this->_visibility;
725  }
726 }
formatValue($value, $name=null)
Definition: Concrete.php:563
static getSelector($selector, &$matches=false)
Definition: Xyl.php:1819
const SELECTOR_QUERY
Definition: Xyl.php:108
const NAMESPACE_ID
Definition: Xyl.php:66
setVisibility($visibility)
Definition: Concrete.php:709
computeValue(Stream\IStream\Out $out=null)
Definition: Concrete.php:410
computeDataBinding(Array &$data, Array &$parent=null)
Definition: Concrete.php:162
static _getDeclaredAttributes()
Definition: Concrete.php:687
paint(Stream\IStream\Out $out)
const SELECTOR_PATH
Definition: Xyl.php:101
computeTransientValue(Stream\IStream\Out $out=null)
Definition: Concrete.php:451
render(Stream\IStream\Out $out)
Definition: Concrete.php:366
selectData($expression, Array &$bucket)
Definition: Concrete.php:257
const SELECTOR_XPATH
Definition: Xyl.php:115
computeAttributeValue($value, $type=self::ATTRIBUTE_TYPE_UNKNOWN, $name=null)
Definition: Concrete.php:488