/** * For internal use. * * @param Component $component * @param bool $deep * @throws ComponentException */ private static function _inspect(Component $component, $deep = true) { if (self::$recursionMap->contains($component)) { echo "<i>recursion</i>"; return; } self::$recursionMap->attach($component); $COLOR_BIND = '#5AA'; $COLOR_CONST = '#5A5'; $COLOR_INFO = '#CCC'; $COLOR_PROP = '#B00'; $COLOR_TAG = '#000;font-weight:bold'; $COLOR_TYPE = '#55A'; $COLOR_VALUE = '#333'; $COLOR_SHADOW_DOM = '#5AA;font-weight:bold'; $Q = "<i style='color:#CCC'>\"</i>"; $tag = $component->getTagName(); $hasContent = false; echo "<div class=__component><span style='color:{$COLOR_TAG}'><{$tag}</span>"; $isDoc = $component instanceof DocumentFragment; if (!$component->parent && !$isDoc) { echo " <span style='color:{$COLOR_INFO}'>(detached)</span>"; } $type = typeOf($component) . ' #' . Debug::objectId($component); echo "<span class='icon hint--rounded hint--right' data-hint='Component:\n{$type}'><i class='fa fa-info-circle'></i></span>"; $type1 = str_pad('#' . Debug::objectId($component->context), 4, ' ', STR_PAD_LEFT); $type2 = str_pad('#' . Debug::objectId($component->getDataBinder()), 4, ' ', STR_PAD_LEFT); $type3 = str_pad('#' . Debug::objectId($component->getViewModel()), 4, ' ', STR_PAD_LEFT); $type4 = str_pad('#' . Debug::objectId($component->getDataBinder()->getViewModel()), 4, ' ', STR_PAD_LEFT); $type5 = str_pad('#' . Debug::objectId($component->getDataBinder()->getProps()), 4, ' ', STR_PAD_LEFT); echo "<span class='icon hint--rounded hint--bottom' data-hint='Context: {$type1} Data binder: {$type2}\nView model: {$type3} Binder view model: {$type4}\nProperties: {$type5}'><i class='fa fa-database'></i></span>"; // Handle text node if ($component instanceof Text) { echo "<span style='color:{$COLOR_TAG}'>></span><div style='margin:0 0 0 15px'>"; try { if ($component->isBound('value')) { /** @var Expression $exp */ $exp = $component->getBinding('value'); $exp = self::inspectString((string) $exp); echo "<span style='color:{$COLOR_BIND}'>{$exp}</span> = "; $v = self::getBindingValue('value', $component, $error); if ($error) { echo $v; return; } if (!is_string($v)) { echo Debug::typeInfoOf($v); return; } } else { $v = $component->props->value; } $v = strlen(trim($v)) ? HtmlSyntaxHighlighter::highlight($v) : "<i>'{$v}'</i>"; echo $v; } finally { echo "</div><span style='color:{$COLOR_TAG}'></{$tag}><br></span></div>"; } return; } // Handle other node types if ($component instanceof DocumentFragment) { self::inspectViewModel($component); } if ($component->supportsProperties()) { /** @var ComponentProperties $propsObj */ $propsObj = $component->props; if ($propsObj) { $props = $propsObj->getAll(); } else { $props = null; } if ($props) { ksort($props); } if ($props) { $type = typeOf($propsObj); $tid = Debug::objectId($propsObj); echo "<span class='icon hint--rounded hint--right' data-hint='Properties: #{$tid}\n{$type}'><i class='fa fa-list'></i></span>"; echo "<table class='__console-table' style='color:{$COLOR_VALUE}'>"; // Display all scalar properties. foreach ($props as $k => $v) { $t = $component->props->getTypeOf($k); $isModified = $component->props->isModified($k); $modifStyle = $isModified ? ' class=__modified' : ' class=__original'; if ($t != type::content && $t != type::collection && $t != type::metadata) { $tn = $component->props->getTypeNameOf($k); echo "<tr{$modifStyle}><td style='color:{$COLOR_PROP}'>{$k}<td><i style='color:{$COLOR_TYPE}'>{$tn}</i><td>"; // Display data-binding if ($component->isBound($k)) { /** @var Expression $exp */ $exp = $component->getBinding($k); $exp = self::inspectString((string) $exp); echo "<span style='color:{$COLOR_BIND}'>{$exp}</span> = "; $v = self::getBindingValue($k, $component, $error); if ($error) { break; } } if (is_null($v)) { echo "<i style='color:{$COLOR_INFO}'>null</i>"; } else { switch ($t) { case type::bool: echo "<i style='color:{$COLOR_CONST}'>" . ($v ? 'true' : 'false') . '</i>'; break; case type::id: echo "{$Q}{$v}{$Q}"; break; case type::number: echo $v; break; case type::string: echo "{$Q}<span style='white-space: pre-wrap'>" . self::inspectString(strval($v)) . "</span>{$Q}"; break; default: if (is_object($v)) { echo sprintf("<i style='color:{$COLOR_CONST}'>%s</i>", Debug::typeInfoOf($v)); } elseif (is_array($v)) { echo sprintf("<i style='color:{$COLOR_CONST}'>array(%d)</i>", count($v)); } else { $v = _e($v); echo "{$Q}{$v}{$Q}"; } } } } } // Display all slot properties. foreach ($props as $k => $v) { $t = $component->props->getTypeOf($k); if ($t == type::content || $t == type::collection || $t == type::metadata) { $tn = $component->props->getTypeNameOf($k); $isModified = $component->props->isModified($k); $modifStyle = $isModified ? ' style="background:#FFE"' : ' style="opacity:0.5"'; echo "<tr{$modifStyle}><td style='color:{$COLOR_PROP}'>{$k}<td><i style='color:{$COLOR_TYPE}'>{$tn}</i><td>"; /** @var Expression $exp */ $exp = $component->getBinding($k); if (isset($exp)) { $exp = self::inspectString((string) $exp); echo "<span style='color:{$COLOR_BIND}'>{$exp}</span> = "; $v = self::getBindingValue($k, $component, $error); if ($error) { echo $v; break; } } if ($v && ($v instanceof Component || is_array($v))) { switch ($t) { case type::content: if ($v) { echo "<tr><td><td colspan=2>"; self::_inspect($v, $deep); } else { echo "<i style='color:{$COLOR_INFO}'>null</i>"; } break; case type::metadata: if ($v) { echo "<tr><td><td colspan=2>"; self::_inspect($v, $deep); } else { echo "<i style='color:{$COLOR_INFO}'>null</i>"; } break; case type::collection: echo "of <i style='color:{$COLOR_TYPE}'>", $component->props->getRelatedTypeNameOf($k), '</i>'; if ($v) { echo "<tr><td><td colspan=2>"; self::_inspectSet($v, true); } else { echo " = <i style='color:{$COLOR_INFO}'>[]</i>"; } break; } } else { if (is_array($v)) { echo "<i style='color:{$COLOR_INFO}'>[]</i>"; } else { if (isset($v)) { printf("<b style='color:red'>WRONG TYPE: %s</b>", Debug::typeInfoOf($v)); } else { echo "<i style='color:{$COLOR_INFO}'>null</i>"; } } } echo '</tr>'; } } echo "</table>"; } } // If deep inspection is enabled, recursively inspect all children components. if ($deep) { $content = $shadowDOM = null; if ($component->hasChildren()) { $content = $component->getChildren(); } if ($component instanceof CompositeComponent) { $shadowDOM = $component->provideShadowDOM(); } if ($content || $shadowDOM) { echo "<span style='color:{$COLOR_TAG}'>></span><div style=\"margin:0 0 0 15px\">"; if ($content) { self::_inspectSet($content, $deep); } if ($shadowDOM) { echo "<span style='color:{$COLOR_SHADOW_DOM}'><Shadow DOM></span><div style=\"margin:0 0 0 15px\">"; self::_inspect($shadowDOM, $deep); echo "</div><span style='color:{$COLOR_SHADOW_DOM}'></Shadow DOM></span>"; } echo '</div>'; $hasContent = true; } } echo "<span style='color:{$COLOR_TAG}'>" . ($hasContent ? "</{$tag}><br>" : "/><br>") . "</span></div>"; }
/** * Returns an object's unique identifier (a short version), useful for debugging. * * @param object $o * @return $this */ function objectId($o) { return $this->write(Debug::objectId($o)); }