Frame::getArgs()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
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, "&hellip;", $file);
68
            }
69
            $file = str_replace("/", "/&shy;", $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
Bug Best Practice introduced by
The expression $this->getFile() of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
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 ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

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