Issues (308)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/diagnostics/debug/Frame.php (4 issues)

Upgrade to new PHP Analysis Engine

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 number of the line which got executed resulting in the inspected trace.
90
     *
91
     * @return  int The number of the line, or 0 if the line is unknown.
92
     */
93
    public function getLine() : int
94
    {
95
        return $this->line;
96
    }
97
98
    /**
99
     * Returns the fully qualified name of the class which contained the code that resulted in the inspected trace.
100
     *
101
     * @return  string  The fully qualified name of the class, or an empty string if the class is unknown or the
102
     *                  code was not contained in a class.
103
     */
104
    public function getClass() : string
105
    {
106
        return $this->class;
107
    }
108
109
    /**
110
     * Returns the namespace of the class which contained the code that resulted in the inspected trace.
111
     *
112
     * @return  string  The namespace, or an empty string if: the namespace is unknown; the class is unknown
113
     *                  or was contained in the global namespace; the code was not contained in a class.
114
     */
115
    public function getNamespace() : string
116
    {
117
        return $this->namespace;
118
    }
119
120
    /**
121
     * Returns the shortened name (without the namespace) of the class which contained the code that
122
     * resulted in the inspected trace.
123
     *
124
     * @return  string  The shortened name of the class, or an empty string if the class is unknown or the
125
     *                  code was not contained in a class.
126
     */
127
    public function getShortClass() : string
128
    {
129
        return $this->shortClass;
130
    }
131
132
    /**
133
     * Returns the name of the function/method which got executed resulting in the inspected trace.
134
     *
135
     * @return  string  The name of the function/method, or an empty string if the name is unknown or the
136
     *                  code was not executed inside a function/method.
137
     */
138
    public function getFunction() : string
139
    {
140
        return $this->function;
141
    }
142
143
    /**
144
     * Returns the type of the function/method call (static or dynamic).
145
     *
146
     * @return  string  The type of the call, or an empty string if the type is unknown.
147
     */
148
    public function getType() : string
149
    {
150
        return $this->type;
151
    }
152
153
    /**
154
     * Returns the arguments which were passed to the function resulting in the inspected trace.
155
     *
156
     * @return  array   The arguments (context) of the call, or an empty array if there were none or
157
     *                  they are unknown.
158
     */
159
    public function getArgs() : array
160
    {
161
        return $this->args;
162
    }
163
164
    /**
165
     * Returns the path to the file assigned to the current frame.
166
     *
167
     * @return  string  The path, or an empty string if the path is unknown.
168
     */
169
    public function getFile() : string
170
    {
171
        return $this->file;
172
    }
173
174
    /**
175
     * Returns the contents of the file assigned to this frame as a string.
176
     *
177
     * @return  string  Returns either the contents of the assigned file or an empty string if no file is assigned
178
     *                  or its contents could not be fetched.
179
     */
180
    public function getFileContents() : string
181
    {
182
        // No point in continuing if there is no file assigned to this frame.
183
        if (!$this->file || $this->file === 'Unknown') {
184
            return '';
185
        }
186
187
        // If the contents aren't cached yet, grab them into our rudimentary in-memory cache.
188
        // Note: This may cache a boolean false if the retrieval fails for whatever reason. We're gonna handle
189
        // that afterwards and return an empty string instead.
190
        if (!isset(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 getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
191
            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 getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
192
        }
193
194
        // Return the cached contents of the file or an empty string if no contents could be fetched.
195
        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 getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
196
    }
197
198
    /**
199
     * Returns the contents of the file assigned to this frame as an array of lines optionally sliced from
200
     * the given starting line to the given ending line. The arguments used work in exactly the same way as
201
     * {@see array_splice()}.
202
     *
203
     * Note: Lines are 0-indexed.
204
     *
205
     * @param   int $offset     The starting offset.
206
     * @param   int $length     The length of the resulting subset.
207
     * @return  string[]|null   The resulting array of lines or an empty array when no file contents are available.
0 ignored issues
show
Should the return type not be array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
208
     */
209
    public function getFileLines(int $offset = 0, int $length = null) : array
210
    {
211
        // Return null right away if we are not able to grab the file contents for any reason.
212
        if (!$contents = $this->getFileContents()) {
213
            return [];
214
        }
215
216
        // Explode the contents into an array by line breaks and return the slice. Note: Normally we'd simply read
217
        // the contents into an array directly by using file(), but to avoid code duplication and to keep caching
218
        // simple it's done as-is.
219
        return array_slice(explode("\n", $contents), $offset, $length, true);
220
    }
221
222
    /**
223
     * {@inheritDoc}
224
     */
225
    public function serialize() : string
226
    {
227
        $data = $this->toArray();
228
229
        if (!empty($data['args'])) {
230
            $data['args'] = $this->flattenArgs($data['args']);
231
        }
232
233
        return serialize($data);
234
    }
235
236
    /**
237
     * {@inheritDoc}
238
     */
239
    public function unserialize($data)
240
    {
241
        $this->setData(unserialize($data));
242
    }
243
244
    /**
245
     * {@inheritDoc}
246
     */
247
    public function toArray() : array
248
    {
249
        return [
250
            'file'     => $this->file,
251
            'line'     => $this->line,
252
            'function' => $this->function,
253
            'class'    => $this->class,
254
            'type'     => $this->type,
255
            'args'     => $this->args
256
        ];
257
    }
258
259
    /**
260
     * Sets the Frame's data.
261
     *
262
     * @param   array   $data   The Frame's data, in the same format as returned by \Exception::getTrace().
263
     * @return  $this
264
     */
265
    protected function setData(array $data) : Frame
266
    {
267
        $this->file     = $data['file']     ?? '';
268
        $this->line     = $data['line']     ?? 0;
269
        $this->class    = $data['class']    ?? '';
270
        $this->function = $data['function'] ?? '';
271
        $this->type     = $data['type']     ?? '';
272
        $this->args     = $data['args']     ?? [];
273
274
        // If we're dealing with a class, try to get its namespace and short name.
275
        if ($this->class) {
276
            $parts = explode('\\', $this->class);
277
278
            $this->shortClass = array_pop($parts);
279
            $this->namespace  = implode('\\', $parts);
280
        } else {
281
            // Otherwise initialize with zero-values.
282
            $this->shortClass = '';
283
            $this->namespace  = '';
284
        }
285
286
        return $this;
287
    }
288
289
    /**
290
     * Flattens the args to make them easier to serialize.
291
     *
292
     * @param   array   $args   The args to flatten.
293
     * @param   int     $depth  The current nesting depth.
294
     * @return  array           The flattened args.
295
     */
296
    protected function flattenArgs(array $args, int $depth = 0) : array
297
    {
298
        $result = [];
299
300
        foreach ($args as $key => $value) {
301
            if (is_object($value)) {
302
                $result[$key] = ['object', get_class($value)];
303
            } elseif (is_array($value)) {
304
                if ($depth > $this->nestingLimit) {
305
                    $result[$key] = ['array', '*DEEP NESTED ARRAY*'];
306
                } else {
307
                    $result[$key] = ['array', $this->flattenArgs($value, ++$depth)];
308
                }
309
            } elseif (null === $value) {
310
                $result[$key] = ['null', null];
311
            } elseif (is_bool($value)) {
312
                $result[$key] = ['boolean', $value];
313
            } elseif (is_resource($value)) {
314
                $result[$key] = ['resource', get_resource_type($value)];
315
            } elseif ($value instanceof \__PHP_Incomplete_Class) {
316
                // Special case of object - is_object() will return false.
317
                $array = new \ArrayObject($value);
318
                $result[$key] = ['incomplete-object', $array['__PHP_Incomplete_Class_Name']];
319
            } else {
320
                $result[$key] = ['string', (string) $value];
321
            }
322
        }
323
324
        return $result;
325
    }
326
}
327