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; } }
/** * @return string */ private static function _toPhp(&$var, &$list = array(), $level = 0, &$line = 1) { if (is_float($var)) { $var = str_replace(',', '.', "{$var}"); return strpos($var, '.') === FALSE ? $var . '.0' : $var; } elseif (is_bool($var)) { return $var ? 'TRUE' : 'FALSE'; } elseif (is_string($var) && (preg_match('#[^\\x09\\x20-\\x7E\\xA0-\\x{10FFFF}]#u', $var) || preg_last_error())) { static $table; if ($table === NULL) { foreach (array_merge(range("", ""), range("", "ÿ")) as $ch) { $table[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT); } $table['\\'] = '\\\\'; $table["\r"] = '\\r'; $table["\n"] = '\\n'; $table["\t"] = '\\t'; $table['$'] = '\\$'; $table['"'] = '\\"'; } return '"' . strtr($var, $table) . '"'; } elseif (is_array($var)) { $space = str_repeat("\t", $level); static $marker; if ($marker === NULL) { $marker = uniqid("", TRUE); } if (empty($var)) { $out = ''; } elseif ($level > self::$maxDepth || isset($var[$marker])) { return '/* Nesting level too deep or recursive dependency */'; } else { $out = "\n{$space}"; $outShort = ''; $var[$marker] = TRUE; $oldLine = $line; $line++; $counter = 0; foreach ($var as $k => &$v) { if ($k !== $marker) { $item = ($k === $counter ? '' : self::_toPhp($k, $list, $level + 1, $line) . ' => ') . self::_toPhp($v, $list, $level + 1, $line); $counter = is_int($k) ? max($k + 1, $counter) : $counter; $outShort .= ($outShort === '' ? '' : ', ') . $item; $out .= "\t{$item},\n{$space}"; $line++; } } unset($var[$marker]); if (strpos($outShort, "\n") === FALSE && strlen($outShort) < self::$maxLength) { $line = $oldLine; $out = $outShort; } } return 'array(' . $out . ')'; } elseif ($var instanceof \Closure) { $rc = new \ReflectionFunction($var); return "/* Closure defined in file {$rc->getFileName()} on line {$rc->getStartLine()} */"; } elseif (is_object($var)) { if (PHP_VERSION_ID >= 70000 && ($rc = new \ReflectionObject($var)) && $rc->isAnonymous()) { return "/* Anonymous class defined in file {$rc->getFileName()} on line {$rc->getStartLine()} */"; } $arr = (array) $var; $space = str_repeat("\t", $level); $class = get_class($var); $used =& $list[spl_object_hash($var)]; if (empty($arr)) { $out = ''; } elseif ($used) { return "/* {$class} dumped on line {$used} */"; } elseif ($level > self::$maxDepth) { return '/* Nesting level too deep */'; } else { $out = "\n"; $used = $line; $line++; foreach ($arr as $k => &$v) { if ($k[0] === "") { $k = substr($k, strrpos($k, "") + 1); } $out .= "{$space}\t" . self::_toPhp($k, $list, $level + 1, $line) . ' => ' . self::_toPhp($v, $list, $level + 1, $line) . ",\n"; $line++; } $out .= $space; } $hash = self::hash($var); return $class === 'stdClass' ? "(object) /* {$hash} */ array({$out})" : "{$class}::__set_state(/* {$hash} */ array({$out}))"; } elseif (is_resource($var)) { return '/* resource ' . get_resource_type($var) . ' */'; } else { $res = var_export($var, TRUE); $line += substr_count($res, "\n"); return $res; } }
<?php $rc = new ReflectionObject(new C()); var_dump($rc->getFileName()); var_dump($rc->getStartLine()); var_dump($rc->getEndLine()); $rc = new ReflectionObject(new stdclass()); var_dump($rc->getFileName()); var_dump($rc->getStartLine()); var_dump($rc->getEndLine()); class C { }
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; }