This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php namespace nyx\diagnostics\debug; |
||
| 2 | |||
| 3 | // External dependencies |
||
| 4 | use nyx\core; |
||
| 5 | use nyx\utils; |
||
| 6 | |||
| 7 | /** |
||
| 8 | * Frame |
||
| 9 | * |
||
| 10 | * Represents a single frame from a diagnostic inspection, ie. from a stack trace. |
||
| 11 | * |
||
| 12 | * @package Nyx\Diagnostics\Debug |
||
| 13 | * @version 0.1.0 |
||
| 14 | * @author Michal Chojnacki <[email protected]> |
||
| 15 | * @copyright 2012-2016 Nyx Dev Team |
||
| 16 | * @link http://docs.muyo.io/nyx/diagnostics/debug.html |
||
| 17 | * @todo setData() validation. |
||
| 18 | */ |
||
| 19 | class Frame implements core\interfaces\Serializable |
||
| 20 | { |
||
| 21 | /** |
||
| 22 | * The traits of a Frame instance. |
||
| 23 | */ |
||
| 24 | use core\traits\Serializable; |
||
| 25 | use utils\traits\Jetget; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @var int The line in which the error occurred. |
||
| 29 | */ |
||
| 30 | private $line; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @var string The namespace the error occurred in, if any. |
||
| 34 | */ |
||
| 35 | private $namespace; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var string The fully-qualified name (ie. with namespace) of the class the error occurred in, if any. |
||
| 39 | */ |
||
| 40 | private $class; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @var string The short name (ie. without namespace) of the class the error occurred in, if any. |
||
| 44 | */ |
||
| 45 | private $shortClass; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @var string The name of the function/method the error occurred in. |
||
| 49 | */ |
||
| 50 | private $function; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @var string The type of the error. |
||
| 54 | */ |
||
| 55 | private $type; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @var array The context the error occurred in. |
||
| 59 | */ |
||
| 60 | private $args; |
||
| 61 | |||
| 62 | /** |
||
| 63 | * @var string The path to the file the error occurred in. |
||
| 64 | */ |
||
| 65 | private $file; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * @var int The nesting limit of flattened args. |
||
| 69 | */ |
||
| 70 | private $nestingLimit = 10; |
||
| 71 | |||
| 72 | /** |
||
| 73 | * @var array An array of cached file paths => file contents. Kept static to reduce IO overhead when |
||
| 74 | * multiple Frames are assigned to the same files. |
||
| 75 | */ |
||
| 76 | private static $files; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * Constructs the Frame. |
||
| 80 | * |
||
| 81 | * @param array $data The frame's data, in the format returned by \Exception::getTrace(). |
||
| 82 | */ |
||
| 83 | public function __construct(array $data) |
||
| 84 | { |
||
| 85 | $this->setData($data); |
||
| 86 | } |
||
| 87 | |||
| 88 | /** |
||
| 89 | * Returns the Frame's data as an array. |
||
| 90 | * |
||
| 91 | * @return array $data The Frame's data, in the same format as returned by \Exception::getTrace(). |
||
| 92 | */ |
||
| 93 | public function getData() : array |
||
| 94 | { |
||
| 95 | return [ |
||
| 96 | 'file' => $this->file, |
||
| 97 | 'line' => $this->line, |
||
| 98 | 'function' => $this->function, |
||
| 99 | 'class' => $this->class, |
||
| 100 | 'type' => $this->type, |
||
| 101 | 'args' => $this->args |
||
| 102 | ]; |
||
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Sets the Frame's data. |
||
| 107 | * |
||
| 108 | * @param array $data The Frame's data, in the same format as returned by \Exception::getTrace(). |
||
| 109 | */ |
||
| 110 | public function setData(array $data) |
||
| 111 | { |
||
| 112 | $this->line = isset($data['line']) ? $data['line'] : null; |
||
| 113 | $this->class = isset($data['class']) ? $data['class'] : null; |
||
| 114 | $this->function = isset($data['function']) ? $data['function'] : null; |
||
| 115 | $this->type = isset($data['type']) ? $data['type'] : null; |
||
| 116 | $this->args = isset($data['args']) ? (array) $data['args'] : []; |
||
| 117 | $this->file = isset($data['file']) ? $data['file'] : null; |
||
| 118 | |||
| 119 | // If we're dealing with a class, try to get its namespace and short name. |
||
| 120 | if (null !== $this->class) { |
||
| 121 | $parts = explode('\\', $this->class); |
||
| 122 | |||
| 123 | $this->shortClass = array_pop($parts); |
||
| 124 | $this->namespace = implode('\\', $parts); |
||
| 125 | } |
||
| 126 | } |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Returns the line which got executed resulting in the inspected trace. |
||
| 130 | * |
||
| 131 | * @return int|null |
||
| 132 | */ |
||
| 133 | public function getLine() |
||
| 134 | { |
||
| 135 | return $this->line; |
||
| 136 | } |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Returns the name of the class which got executed resulting in the inspected trace. |
||
| 140 | * |
||
| 141 | * @return string|null |
||
| 142 | */ |
||
| 143 | public function getClass() |
||
| 144 | { |
||
| 145 | return $this->class; |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Returns the namespace of the class which got executed resulting in the inspected trace. |
||
| 150 | * |
||
| 151 | * @return string|null |
||
| 152 | */ |
||
| 153 | public function getNamespace() |
||
| 154 | { |
||
| 155 | return $this->namespace; |
||
| 156 | } |
||
| 157 | |||
| 158 | /** |
||
| 159 | * Returns the shortened name (without the namespace) of the class which got executed resulting in the |
||
| 160 | * inspected trace. |
||
| 161 | * |
||
| 162 | * @return string|null |
||
| 163 | */ |
||
| 164 | public function getShortClass() |
||
| 165 | { |
||
| 166 | return $this->shortClass; |
||
| 167 | } |
||
| 168 | |||
| 169 | /** |
||
| 170 | * Returns the function/method which got executed resulting in the inspected trace. |
||
| 171 | * |
||
| 172 | * @return string|null |
||
| 173 | */ |
||
| 174 | public function getFunction() |
||
| 175 | { |
||
| 176 | return $this->function; |
||
| 177 | } |
||
| 178 | |||
| 179 | /** |
||
| 180 | * Returns the type of the function/method call (static or dynamic). |
||
| 181 | * |
||
| 182 | * @return string|null |
||
| 183 | */ |
||
| 184 | public function getType() |
||
| 185 | { |
||
| 186 | return $this->type; |
||
| 187 | } |
||
| 188 | |||
| 189 | /** |
||
| 190 | * Returns the arguments which were passed to the function resulting in the inspected trace. |
||
| 191 | * |
||
| 192 | * @return array |
||
| 193 | */ |
||
| 194 | public function getArgs() : array |
||
| 195 | { |
||
| 196 | return $this->args; |
||
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * Returns the path to the file assigned to the current frame. |
||
| 201 | * |
||
| 202 | * @return string |
||
| 203 | */ |
||
| 204 | public function getFile() |
||
| 205 | { |
||
| 206 | return $this->file; |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Returns the contents of the file assigned to this frame as a string. |
||
| 211 | * |
||
| 212 | * @return string|bool Returns either the contents of the assigned file or false if no file is assigned |
||
| 213 | * or its contents could not be fetched. |
||
| 214 | */ |
||
| 215 | public function getFileContents() |
||
| 216 | { |
||
| 217 | // No point in continuing if there is no file assigned to this frame. |
||
| 218 | if (!$this->file || $this->file === 'Unknown') { |
||
| 219 | return false; |
||
| 220 | } |
||
| 221 | |||
| 222 | // If the assigned file's contents are already cached, return them right away. |
||
| 223 | // Note: "False" might also be cached instead of the actual file contents if retrieval |
||
| 224 | // of the contents failed once already. |
||
| 225 | if (isset(static::$files[$this->file])) { |
||
|
0 ignored issues
–
show
|
|||
| 226 | return static::$files[$this->file]; |
||
|
0 ignored issues
–
show
Since
$files is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $files to at least protected.
Let’s assume you have a class which uses late-static binding: class YourClass
{
private static $someVariable;
public static function getSomeVariable()
{
return static::$someVariable;
}
}
The code above will run fine in your PHP runtime. However, if you now create a
sub-class and call the class YourSubClass extends YourClass { }
YourSubClass::getSomeVariable(); // Will cause an access error.
In the case above, it makes sense to update class SomeClass
{
private static $someVariable;
public static function getSomeVariable()
{
return self::$someVariable; // self works fine with private.
}
}
Loading history...
|
|||
| 227 | } |
||
| 228 | |||
| 229 | // Otherwise grab the contents, cache them and return them. |
||
| 230 | return static::$files[$this->file] = file_get_contents($this->file); |
||
|
0 ignored issues
–
show
Since
$files is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $files to at least protected.
Let’s assume you have a class which uses late-static binding: class YourClass
{
private static $someVariable;
public static function getSomeVariable()
{
return static::$someVariable;
}
}
The code above will run fine in your PHP runtime. However, if you now create a
sub-class and call the class YourSubClass extends YourClass { }
YourSubClass::getSomeVariable(); // Will cause an access error.
In the case above, it makes sense to update class SomeClass
{
private static $someVariable;
public static function getSomeVariable()
{
return self::$someVariable; // self works fine with private.
}
}
Loading history...
|
|||
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * Returns the contents of the file assigned to this frame as an array of lines optionally sliced from |
||
| 235 | * the given starting line to the given ending line. The arguments used work in exactly the same way as |
||
| 236 | * {@see array_splice()}. |
||
| 237 | * |
||
| 238 | * Note: Lines are 0-indexed. |
||
| 239 | * |
||
| 240 | * @param int $offset The starting offset. |
||
| 241 | * @param int $length The length of the resulting subset. |
||
| 242 | * @return string[]|null The resulting array of lines or an empty array when no file contents are available. |
||
| 243 | */ |
||
| 244 | public function getFileLines(int $offset = 0, int $length = null) : array |
||
| 245 | { |
||
| 246 | // Return null right away if we are not able to grab the file contents for any reason. |
||
| 247 | if (!$contents = $this->getFileContents()) { |
||
| 248 | return []; |
||
| 249 | } |
||
| 250 | |||
| 251 | // Explode the contents into an array by line breaks and return the slice. Note: Normally we'd simply read |
||
| 252 | // the contents into an array directly by using file(), but to avoid code duplication and to keep caching |
||
| 253 | // simple it's done as-is. |
||
| 254 | return array_slice(explode("\n", $contents), $offset, $length, true); |
||
| 255 | } |
||
| 256 | |||
| 257 | /** |
||
| 258 | * Returns the file path (if present) with the part common for all frames replaced by an ellipsis and slashes |
||
| 259 | * replaced by soft slashes for presentation purposes (word wrapping). |
||
| 260 | * |
||
| 261 | * @return string |
||
| 262 | * @todo Replace the dirname() calls with a required string parameter for the path which shall get cut out. |
||
| 263 | */ |
||
| 264 | public function getPrettyPath() |
||
| 265 | { |
||
| 266 | if ($path = $this->file) { |
||
| 267 | $dirname = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__)))))); |
||
| 268 | $path = str_replace($dirname, "…", $path); |
||
| 269 | } |
||
| 270 | |||
| 271 | return $path; |
||
| 272 | } |
||
| 273 | |||
| 274 | /** |
||
| 275 | * {@inheritDoc} |
||
| 276 | */ |
||
| 277 | public function serialize() |
||
| 278 | { |
||
| 279 | $data = $this->toArray(); |
||
| 280 | |||
| 281 | !empty($data['args']) && $data['args'] = $this->flattenArgs($data['args']); |
||
| 282 | |||
| 283 | return serialize($data); |
||
| 284 | } |
||
| 285 | |||
| 286 | /** |
||
| 287 | * {@inheritDoc} |
||
| 288 | */ |
||
| 289 | public function unserialize($data) |
||
| 290 | { |
||
| 291 | $this->setData(unserialize($data)); |
||
| 292 | } |
||
| 293 | |||
| 294 | /** |
||
| 295 | * {@inheritDoc} |
||
| 296 | */ |
||
| 297 | public function toArray() : array |
||
| 298 | { |
||
| 299 | return $this->getData(); |
||
| 300 | } |
||
| 301 | |||
| 302 | /** |
||
| 303 | * Flattens the args to make them easier to serialize. |
||
| 304 | * |
||
| 305 | * @param array $args The args to flatten. |
||
| 306 | * @param int $level The current nesting depth. |
||
| 307 | * @return array The flattened args. |
||
| 308 | */ |
||
| 309 | protected function flattenArgs(array $args, $level = 0) |
||
| 310 | { |
||
| 311 | $result = []; |
||
| 312 | |||
| 313 | foreach ($args as $key => $value) { |
||
| 314 | if (is_object($value)) { |
||
| 315 | $result[$key] = ['object', get_class($value)]; |
||
| 316 | } elseif (is_array($value)) { |
||
| 317 | if ($level > $this->nestingLimit) { |
||
| 318 | $result[$key] = ['array', '*DEEP NESTED ARRAY*']; |
||
| 319 | } else { |
||
| 320 | $result[$key] = ['array', $this->flattenArgs($value, ++$level)]; |
||
| 321 | } |
||
| 322 | } elseif (null === $value) { |
||
| 323 | $result[$key] = ['null', null]; |
||
| 324 | } elseif (is_bool($value)) { |
||
| 325 | $result[$key] = ['boolean', $value]; |
||
| 326 | } elseif (is_resource($value)) { |
||
| 327 | $result[$key] = ['resource', get_resource_type($value)]; |
||
| 328 | } |
||
| 329 | // Special case of object, is_object will return false. |
||
| 330 | elseif ($value instanceof \__PHP_Incomplete_Class) { |
||
| 331 | $array = new \ArrayObject($value); |
||
| 332 | $result[$key] = ['incomplete-object', $array['__PHP_Incomplete_Class_Name']]; |
||
| 333 | } else { |
||
| 334 | $result[$key] = ['string', (string) $value]; |
||
| 335 | } |
||
| 336 | } |
||
| 337 | |||
| 338 | return $result; |
||
| 339 | } |
||
| 340 | } |
||
| 341 |
Let’s assume you have a class which uses late-static binding:
The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the
getSomeVariable()on that sub-class, you will receive a runtime error:In the case above, it makes sense to update
SomeClassto useselfinstead: