private static function _parse_object(&$variable, kintVariableData $variableData) { if (function_exists('spl_object_hash')) { $hash = spl_object_hash($variable); } else { ob_start(); var_dump($variable); preg_match('[#(\\d+)]', ob_get_clean(), $match); $hash = $match[1]; } $castedArray = (array) $variable; $variableData->type = 'object'; $variableData->subtype = get_class($variable); $variableData->size = count($castedArray); if (isset(self::$_objects[$hash])) { $variableData->value = '*RECURSION*'; return false; } if (self::_checkDepth()) { $variableData->extendedValue = "*DEPTH TOO GREAT*"; return false; } # ArrayObject (and maybe ArrayIterator, did not try yet) unsurprisingly consist of mainly dark magic. # What bothers me most, var_dump sees no problem with it, and ArrayObject also uses a custom, # undocumented serialize function, so you can see the properties in internal functions, but # can never iterate some of them if the flags are not STD_PROP_LIST. Fun stuff. if ($variableData->subtype === 'ArrayObject' || is_subclass_of($variable, 'ArrayObject')) { $arrayObjectFlags = $variable->getFlags(); $variable->setFlags(ArrayObject::STD_PROP_LIST); } self::$_objects[$hash] = true; // todo store reflectorObject here for alternatives cache $reflector = new \ReflectionObject($variable); if (Kint::$mode !== 'cli' && Kint::$mode !== 'whitespace' && Kint::$fileLinkFormat && $reflector->isUserDefined()) { list($url) = Kint::shortenPath($reflector->getFileName(), $reflector->getStartLine(), false); $_ = strpos($url, 'http://') === 0 ? 'class="kint-ide-link" ' : ''; $variableData->subtype = "<a {$_}href=\"{$url}\">{$variableData->subtype}</a>"; } $variableData->size = 0; $extendedValue = array(); $encountered = array(); # copy the object as an array as it provides more info than Reflection (depends) foreach ($castedArray as $key => $value) { if (Kint::$keyFilterCallback && call_user_func_array(Kint::$keyFilterCallback, array($key, $value)) === false) { continue; } /* casting object to array: * integer properties are inaccessible; * private variables have the class name prepended to the variable name; * protected variables have a '*' prepended to the variable name. * These prepended values have null bytes on either side. * http://www.php.net/manual/en/language.types.array.php#language.types.array.casting */ if ($key[0] === "") { $access = $key[1] === "*" ? "protected" : "private"; // Remove the access level from the variable name $key = substr($key, strrpos($key, "") + 1); } else { $access = "public"; } $encountered[$key] = true; $output = kintParser::factory($value, self::_escape($key)); $output->access = $access; $output->operator = '->'; $extendedValue[] = $output; $variableData->size++; } foreach ($reflector->getProperties() as $property) { $name = $property->name; if ($property->isStatic() || isset($encountered[$name])) { continue; } if ($property->isProtected()) { $property->setAccessible(true); $access = "protected"; } elseif ($property->isPrivate()) { $property->setAccessible(true); $access = "private"; } else { $access = "public"; } $value = $property->getValue($variable); if (Kint::$keyFilterCallback && call_user_func_array(Kint::$keyFilterCallback, array($name, $value)) === false) { continue; } $output = kintParser::factory($value, self::_escape($name)); $output->access = $access; $output->operator = '->'; $extendedValue[] = $output; $variableData->size++; } if (isset($arrayObjectFlags)) { $variable->setFlags($arrayObjectFlags); } if ($variableData->size) { $variableData->extendedValue = $extendedValue; } }
<?php class C { } $r1 = new ReflectionObject(new stdClass()); $r2 = new ReflectionObject(new ReflectionClass('C')); $r3 = new ReflectionObject(new ReflectionProperty('Exception', 'message')); $r4 = new ReflectionObject(new Exception()); $r5 = new ReflectionObject(new C()); var_dump($r1->isUserDefined(), $r2->isUserDefined(), $r3->isUserDefined(), $r4->isUserDefined(), $r5->isUserDefined());
<?php $r1 = new ReflectionObject(new stdClass()); var_dump($r1->isUserDefined('X')); var_dump($r1->isUserDefined('X', true));
/** * @param $param * @param \ReflectionObject $reflection */ private function buildFromObject($param, $reflection = null) { foreach ($param as $key => $value) { $this->object['Object default'][$key] = $value; } // Get info on the object $this->object['Reflection']['In namespace'] = $reflection->inNamespace() ? 'Yes' : 'No'; if ($reflection->inNamespace()) { $this->object['Class namespace'] = $reflection->getNamespaceName(); } $this->object['Reflection']['Class name'] = $reflection->getName(); $this->object['Reflection']['Is internal'] = $reflection->isInternal() ? 'Yes' : 'No'; $this->object['Reflection']['Is iterable'] = $reflection->isIterateable() ? 'Yes' : 'No'; $this->object['Reflection']['Is abstract'] = $reflection->isAbstract() ? 'Yes' : 'No'; $this->object['Reflection']['Is final'] = $reflection->isFinal() ? 'Yes' : 'No'; $this->object['Reflection']['Is user defined'] = $reflection->isUserDefined() ? 'Yes' : 'No'; $this->object['Reflection']['Is instantiable'] = $reflection->isInstantiable() ? 'Yes' : 'No'; $this->object['Reflection']['Is clonable'] = $reflection->isCloneable() ? 'Yes' : 'No'; $this->object['Reflection']['Is interface'] = $reflection->isInterface() ? 'Yes' : 'No'; $this->object['Reflection']['Class constants'] = !empty($reflection->getConstants()) ? $reflection->getConstants() : 'Class has no constants'; $this->object['Reflection']['Class static properties'] = !empty($reflection->getStaticProperties()) ? $reflection->getStaticProperties() : 'Class has no static properties'; $this->object['Reflection']['Class default properties'] = !empty($reflection->getDefaultProperties()) ? $reflection->getDefaultProperties() : 'Class has no default properties'; if (null === $reflection->getConstructor()) { $this->object['Reflection']['Class construct'] = 'Class has no construct.'; } else { $this->object['Reflection']['Class construct'] = $reflection->getConstructor(); } $this->object['Reflection']['Class interfaces'] = !empty($reflection->getInterfaces()) ? $reflection->getInterfaces() : 'Class implements no interfaces'; $this->object['Reflection']['Class traits'] = !empty($reflection->getTraits()) ? $reflection->getTraits() : 'Class has no traits'; $this->object['Reflection']['Class parent'] = $reflection->getParentClass() !== false ? $reflection->getParentClass() : 'Class has no parent'; if (false === $reflection->getFileName()) { $this->object['Reflection']['Defined in'] = 'Class is internal, no definition to provide.'; } else { $this->object['Reflection']['Defined in'] = $reflection->getFileName(); } if (false === $reflection->getFileName()) { $this->object['Reflection']['Start line'] = 'Class is internal, no start line to provide.'; } else { $this->object['Reflection']['Start line'] = $reflection->getFileName(); } if (false === $reflection->getEndLine()) { $this->object['Reflection']['End line'] = 'Class is internal, no end line to provide.'; } else { $this->object['Reflection']['End line'] = $reflection->getEndLine(); } if (false === $reflection->getDocComment()) { $this->object['Reflection']['Doc comments'] = 'No documents to provide.'; } else { $this->object['Reflection']['Doc comments'] = $reflection->getDocComment(); } // End get info $this->html .= "<span class=\"js-parent-object\">"; if (!empty($this->object['Object default'])) { $this->html .= "<div class=\"js-object-default-tab \"><button class=\"button-reflection button\">Show reflection</button></div>"; $this->html .= "<div class=\"js-object-default \">"; $this->buildFromObjectIterationInformationRecursive($this->object['Object default']); $this->html .= "</div>"; } if ($param instanceof \Closure) { $this->html .= "<div class=\"js-object-default-tab \"><button class=\"button-reflection button\">Show reflection</button></div>"; $this->html .= "<div class=\"js-object-default \">"; $this->html .= "<span class=\"css-type-string\">Nothing here...</span>"; $this->html .= "</div>"; } $this->html .= "<div class=\"js-object-reflection-tab hide\"><button class=\"button-class-default button\">Show default</button></div>"; $this->html .= "<div class=\"js-object-reflection hide\">"; $this->buildFromObjectReflectionInformationRecursive($this->object['Reflection']); $this->html .= "</div>"; $this->html .= "</span>"; $this->object = []; }
private function parseObject(&$var, Kint_Object $o) { if (KINT_PHP53 || function_exists('spl_object_hash')) { $hash = spl_object_hash($var); } else { ob_start(); var_dump($var); preg_match('/[#(\\d+)]/', ob_get_clean(), $match); $hash = $match[1]; } $values = (array) $var; $object = $o->transplant(new Kint_Object_Instance()); $object->classname = get_class($var); $object->hash = $hash; if (isset($this->object_hashes[$hash])) { $object->hints[] = 'recursion'; return $object; } if ($this->max_depth && $o->depth >= $this->max_depth) { $object->hints[] = 'depth_limit'; return $object; } $object->size = 0; $this->object_hashes[$hash] = $object; // ArrayObject (and maybe ArrayIterator, did not try yet) unsurprisingly // consist of mainly dark magic. What bothers me most, var_dump sees no // problem with it, and ArrayObject also uses a custom, undocumented // serialize function, so you can see the properties in internal functions, // but can never iterate some of them if the flags are not STD_PROP_LIST. Fun stuff. if ($var instanceof ArrayObject) { $ArrayObject_flags_stash = $var->getFlags(); $var->setFlags(ArrayObject::STD_PROP_LIST); } $reflector = new ReflectionObject($var); if ($reflector->isUserDefined()) { $object->filename = $reflector->getFileName(); $object->startline = $reflector->getStartLine(); } $rep = new Kint_Object_Representation('Properties'); // Casting the object to an array can provide more information // than reflection. Notably, parent classes' private properties // don't show with reflection's getProperties() foreach ($values as $key => &$val) { // casting object to array: // integer properties are inaccessible; // private variables have the class name prepended to the variable name; // protected variables have a '*' prepended to the variable name. // These prepended values have null bytes on either side. // http://www.php.net/manual/en/language.types.array.php#language.types.array.casting $child = new Kint_Object(); $child->depth = $object->depth + 1; $child->owner_class = $object->classname; $child->operator = Kint_Object::OPERATOR_OBJECT; $split_key = explode("", $key); if (count($split_key) === 3 && $split_key[0] === '') { $child->name = $split_key[2]; if ($split_key[1] === '*') { $child->access = Kint_Object::ACCESS_PROTECTED; } else { $child->access = Kint_Object::ACCESS_PRIVATE; $child->owner_class = $split_key[1]; } } else { $child->name = $key; $child->access = Kint_Object::ACCESS_PUBLIC; } if ($this->childHasPath($object, $child)) { if ($object->depth === 0 && substr($object->access_path, 0, 4) === 'new ') { // This syntax is available from 5.4.0, but we'll show it before too since // it gets the point across, and there's no oneline way to do it otherwise $child->access_path = '(' . $object->access_path . ')'; } else { $child->access_path = $object->access_path; } if (preg_match('/^[A-Za-z0-9_]+$/', $child->name)) { $child->access_path .= '->' . $child->name; } else { $child->access_path .= '->{' . var_export($child->name, true) . '}'; } } $rep->contents[] = $this->parse($val, $child); ++$object->size; } foreach ($reflector->getProperties() as $property) { if ($property->isStatic()) { continue; } if ($property->isProtected() && isset($values["*" . $property->name])) { continue; } elseif ($property->isPrivate() && isset($values["" . $property->getDeclaringClass()->name . "" . $property->name])) { continue; } elseif (isset($values[$property->name])) { continue; } $child = new Kint_Object(); $child->depth = $object->depth + 1; $child->owner_class = $object->classname; $child->name = $property->name; $child->operator = Kint_Object::OPERATOR_OBJECT; if ($property->isProtected()) { $property->setAccessible(true); $child->owner_class = $property->getDeclaringClass()->name; $child->access = Kint_Object::ACCESS_PROTECTED; } elseif ($property->isPrivate()) { $child->owner_class = $property->getDeclaringClass()->name; $property->setAccessible(true); $child->access = Kint_Object::ACCESS_PRIVATE; } else { $child->access = Kint_Object::ACCESS_PUBLIC; } if ($this->childHasPath($object, $child)) { if ($object->depth === 0 && substr($object->access_path, 0, 4) === 'new ') { // This syntax is available from 5.4.0, but we'll show it before too since // it gets the point across, and there's no oneline way to do it otherwise $child->access_path = '(' . $object->access_path . ')'; } else { $child->access_path = $object->access_path; } if (preg_match('/^[A-Za-z0-9_]+$/', $child->name)) { $child->access_path .= '->' . $child->name; } else { $child->access_path .= '->{' . var_export($child->name, true) . '}'; } } $val = $property->getValue($var); $rep->contents[] = $this->parse($val, $child); unset($val); ++$object->size; } if (isset($ArrayObject_flags_stash)) { $var->setFlags($ArrayObject_flags_stash); } unset($this->object_hashes[$hash]); usort($rep->contents, array('Kint_Parser', 'sortObjectProperties')); $object->addRepresentation($rep); return $object; }