filp /
whoops
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 |
||
| 2 | /** |
||
| 3 | * Whoops - php errors for cool kids |
||
| 4 | * @author Filipe Dobreira <http://github.com/filp> |
||
| 5 | */ |
||
| 6 | |||
| 7 | namespace Whoops\Exception; |
||
| 8 | |||
| 9 | use InvalidArgumentException; |
||
| 10 | use Serializable; |
||
| 11 | |||
| 12 | class Frame implements Serializable |
||
| 13 | { |
||
| 14 | /** |
||
| 15 | * @var array |
||
| 16 | */ |
||
| 17 | protected $frame; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * @var string |
||
| 21 | */ |
||
| 22 | protected $fileContentsCache; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * @var array[] |
||
| 26 | */ |
||
| 27 | protected $comments = []; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * @var bool |
||
| 31 | */ |
||
| 32 | protected $application; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * @param array[] |
||
| 36 | */ |
||
| 37 | 1 | public function __construct(array $frame) |
|
| 38 | { |
||
| 39 | 1 | $this->frame = $frame; |
|
| 40 | 1 | } |
|
| 41 | |||
| 42 | /** |
||
| 43 | * @param bool $shortened |
||
| 44 | * @return string|null |
||
| 45 | */ |
||
| 46 | 2 | public function getFile($shortened = false) |
|
| 47 | { |
||
| 48 | 2 | if (empty($this->frame['file'])) { |
|
| 49 | return null; |
||
| 50 | } |
||
| 51 | |||
| 52 | 2 | $file = $this->frame['file']; |
|
| 53 | |||
| 54 | // Check if this frame occurred within an eval(). |
||
| 55 | // @todo: This can be made more reliable by checking if we've entered |
||
| 56 | // eval() in a previous trace, but will need some more work on the upper |
||
| 57 | // trace collector(s). |
||
| 58 | 2 | if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert) code$/', $file, $matches)) { |
|
| 59 | $file = $this->frame['file'] = $matches[1]; |
||
| 60 | $this->frame['line'] = (int) $matches[2]; |
||
| 61 | } |
||
| 62 | |||
| 63 | 2 | if ($shortened && is_string($file)) { |
|
| 64 | // Replace the part of the path that all frames have in common, and add 'soft hyphens' for smoother line-breaks. |
||
| 65 | $dirname = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__)))))); |
||
| 66 | if ($dirname !== '/') { |
||
| 67 | $file = str_replace($dirname, "…", $file); |
||
| 68 | } |
||
| 69 | $file = str_replace("/", "/­", $file); |
||
| 70 | } |
||
| 71 | |||
| 72 | 2 | return $file; |
|
| 73 | } |
||
| 74 | |||
| 75 | /** |
||
| 76 | * @return int|null |
||
| 77 | */ |
||
| 78 | 2 | public function getLine() |
|
| 79 | { |
||
| 80 | 2 | return isset($this->frame['line']) ? $this->frame['line'] : null; |
|
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @return string|null |
||
| 85 | */ |
||
| 86 | 2 | public function getClass() |
|
| 87 | { |
||
| 88 | 2 | return isset($this->frame['class']) ? $this->frame['class'] : null; |
|
| 89 | } |
||
| 90 | |||
| 91 | /** |
||
| 92 | * @return string|null |
||
| 93 | */ |
||
| 94 | 2 | public function getFunction() |
|
| 95 | { |
||
| 96 | 2 | return isset($this->frame['function']) ? $this->frame['function'] : null; |
|
| 97 | } |
||
| 98 | |||
| 99 | /** |
||
| 100 | * @return array |
||
| 101 | */ |
||
| 102 | 1 | public function getArgs() |
|
| 103 | { |
||
| 104 | 1 | return isset($this->frame['args']) ? (array) $this->frame['args'] : []; |
|
| 105 | } |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Returns the full contents of the file for this frame, |
||
| 109 | * if it's known. |
||
| 110 | * @return string|null |
||
| 111 | */ |
||
| 112 | 3 | public function getFileContents() |
|
| 113 | { |
||
| 114 | 3 | if ($this->fileContentsCache === null && $filePath = $this->getFile()) { |
|
| 115 | // Leave the stage early when 'Unknown' or '[internal]' is passed |
||
| 116 | // this would otherwise raise an exception when |
||
| 117 | // open_basedir is enabled. |
||
| 118 | 3 | if ($filePath === "Unknown" || $filePath === '[internal]') { |
|
| 119 | 2 | return null; |
|
| 120 | } |
||
| 121 | |||
| 122 | try { |
||
| 123 | 1 | $this->fileContentsCache = file_get_contents($filePath); |
|
| 124 | 1 | } catch (ErrorException $exception) { |
|
| 125 | // Internal file paths of PHP extensions cannot be opened |
||
| 126 | } |
||
| 127 | 1 | } |
|
| 128 | |||
| 129 | 1 | return $this->fileContentsCache; |
|
| 130 | } |
||
| 131 | |||
| 132 | /** |
||
| 133 | * Adds a comment to this frame, that can be received and |
||
| 134 | * used by other handlers. For example, the PrettyPage handler |
||
| 135 | * can attach these comments under the code for each frame. |
||
| 136 | * |
||
| 137 | * An interesting use for this would be, for example, code analysis |
||
| 138 | * & annotations. |
||
| 139 | * |
||
| 140 | * @param string $comment |
||
| 141 | * @param string $context Optional string identifying the origin of the comment |
||
| 142 | */ |
||
| 143 | 2 | public function addComment($comment, $context = 'global') |
|
| 144 | { |
||
| 145 | 2 | $this->comments[] = [ |
|
| 146 | 2 | 'comment' => $comment, |
|
| 147 | 2 | 'context' => $context, |
|
| 148 | ]; |
||
| 149 | 2 | } |
|
| 150 | |||
| 151 | /** |
||
| 152 | * Returns all comments for this frame. Optionally allows |
||
| 153 | * a filter to only retrieve comments from a specific |
||
| 154 | * context. |
||
| 155 | * |
||
| 156 | * @param string $filter |
||
| 157 | * @return array[] |
||
| 158 | */ |
||
| 159 | 2 | public function getComments($filter = null) |
|
| 160 | { |
||
| 161 | 2 | $comments = $this->comments; |
|
| 162 | |||
| 163 | 2 | if ($filter !== null) { |
|
| 164 | 1 | $comments = array_filter($comments, function ($c) use ($filter) { |
|
| 165 | 1 | return $c['context'] == $filter; |
|
| 166 | 1 | }); |
|
| 167 | 1 | } |
|
| 168 | |||
| 169 | 2 | return $comments; |
|
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Returns the array containing the raw frame data from which |
||
| 174 | * this Frame object was built |
||
| 175 | * |
||
| 176 | * @return array |
||
| 177 | */ |
||
| 178 | public function getRawFrame() |
||
| 179 | { |
||
| 180 | return $this->frame; |
||
| 181 | } |
||
| 182 | |||
| 183 | /** |
||
| 184 | * Returns the contents of the file for this frame as an |
||
| 185 | * array of lines, and optionally as a clamped range of lines. |
||
| 186 | * |
||
| 187 | * NOTE: lines are 0-indexed |
||
| 188 | * |
||
| 189 | * @example |
||
| 190 | * Get all lines for this file |
||
| 191 | * $frame->getFileLines(); // => array( 0 => '<?php', 1 => '...', ...) |
||
| 192 | * @example |
||
| 193 | * Get one line for this file, starting at line 10 (zero-indexed, remember!) |
||
| 194 | * $frame->getFileLines(9, 1); // array( 9 => '...' ) |
||
| 195 | * |
||
| 196 | * @throws InvalidArgumentException if $length is less than or equal to 0 |
||
| 197 | * @param int $start |
||
| 198 | * @param int $length |
||
| 199 | * @return string[]|null |
||
| 200 | */ |
||
| 201 | 2 | public function getFileLines($start = 0, $length = null) |
|
| 202 | { |
||
| 203 | 2 | if (null !== ($contents = $this->getFileContents())) { |
|
| 204 | 2 | $lines = explode("\n", $contents); |
|
| 205 | |||
| 206 | // Get a subset of lines from $start to $end |
||
| 207 | 2 | if ($length !== null) { |
|
| 208 | 1 | $start = (int) $start; |
|
| 209 | 1 | $length = (int) $length; |
|
| 210 | 1 | if ($start < 0) { |
|
| 211 | $start = 0; |
||
| 212 | } |
||
| 213 | |||
| 214 | 1 | if ($length <= 0) { |
|
| 215 | throw new InvalidArgumentException( |
||
| 216 | "\$length($length) cannot be lower or equal to 0" |
||
| 217 | ); |
||
| 218 | } |
||
| 219 | |||
| 220 | 1 | $lines = array_slice($lines, $start, $length, true); |
|
| 221 | 1 | } |
|
| 222 | |||
| 223 | 2 | return $lines; |
|
| 224 | } |
||
| 225 | } |
||
| 226 | |||
| 227 | /** |
||
| 228 | * Implements the Serializable interface, with special |
||
| 229 | * steps to also save the existing comments. |
||
| 230 | * |
||
| 231 | * @see Serializable::serialize |
||
| 232 | * @return string |
||
| 233 | */ |
||
| 234 | 1 | public function serialize() |
|
| 235 | { |
||
| 236 | 1 | $frame = $this->frame; |
|
| 237 | 1 | if (!empty($this->comments)) { |
|
| 238 | 1 | $frame['_comments'] = $this->comments; |
|
| 239 | 1 | } |
|
| 240 | |||
| 241 | 1 | return serialize($frame); |
|
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Unserializes the frame data, while also preserving |
||
| 246 | * any existing comment data. |
||
| 247 | * |
||
| 248 | * @see Serializable::unserialize |
||
| 249 | * @param string $serializedFrame |
||
| 250 | */ |
||
| 251 | 1 | public function unserialize($serializedFrame) |
|
| 252 | { |
||
| 253 | 1 | $frame = unserialize($serializedFrame); |
|
| 254 | |||
| 255 | 1 | if (!empty($frame['_comments'])) { |
|
| 256 | 1 | $this->comments = $frame['_comments']; |
|
| 257 | 1 | unset($frame['_comments']); |
|
| 258 | 1 | } |
|
| 259 | |||
| 260 | 1 | $this->frame = $frame; |
|
| 261 | 1 | } |
|
| 262 | |||
| 263 | /** |
||
| 264 | * Compares Frame against one another |
||
| 265 | * @param Frame $frame |
||
| 266 | * @return bool |
||
| 267 | */ |
||
| 268 | 1 | public function equals(Frame $frame) |
|
| 269 | { |
||
| 270 | 1 | if (!$this->getFile() || $this->getFile() === 'Unknown' || !$this->getLine()) { |
|
|
0 ignored issues
–
show
The expression
$this->getLine() of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||
| 271 | return false; |
||
| 272 | } |
||
| 273 | 1 | return $frame->getFile() === $this->getFile() && $frame->getLine() === $this->getLine(); |
|
| 274 | } |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Returns whether this frame belongs to the application or not. |
||
| 278 | * |
||
| 279 | * @return boolean |
||
| 280 | */ |
||
| 281 | public function isApplication() |
||
| 282 | { |
||
| 283 | return $this->application; |
||
| 284 | } |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Mark as an frame belonging to the application. |
||
| 288 | * |
||
| 289 | * @param boolean $application |
||
| 290 | */ |
||
| 291 | public function setApplication($application) |
||
| 292 | { |
||
| 293 | $this->application = $application; |
||
| 294 | } |
||
| 295 | } |
||
| 296 |
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: