Completed
Pull Request — master (#54)
by Christoffer
02:45 queued 40s
created

highlightSourceAtLocation()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 20
nc 4
nop 2
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Error;
4
5
use Digia\GraphQL\Language\Source;
6
use Digia\GraphQL\Language\SourceLocation;
7
8
/**
9
 * @param GraphQLException $error
10
 * @return string
11
 */
12
function printError(GraphQLException $error): string
13
{
14
    $printedLocations = [];
15
    $nodes            = $error->getNodes();
16
17
    if (!empty($nodes)) {
18
        foreach ($nodes as $node) {
19
            $location = $node->getLocation();
20
            if (null !== $location) {
21
                $printedLocations[] = highlightSourceAtLocation(
22
                    $location->getSource(),
23
                    SourceLocation::fromSource($location->getSource(), $location->getStart())
24
                );
25
            }
26
        }
27
    } elseif ($error->hasSource() && $error->hasLocations()) {
28
        foreach ($error->getLocations() as $location) {
29
            $printedLocations[] = highlightSourceAtLocation($error->getSource(), $location);
30
        }
31
    }
32
33
    return empty($printedLocations)
34
        ? $error->getMessage()
35
        : implode("\n\n", array_merge([$error->getMessage()], $printedLocations)) . "\n";
36
}
37
38
/**
39
 * @param Source         $source
40
 * @param SourceLocation $location
41
 * @return string
42
 */
43
function highlightSourceAtLocation(Source $source, SourceLocation $location): string
44
{
45
    $line           = $location->getLine();
46
    $locationOffset = $source->getLocationOffset();
47
    $lineOffset     = $locationOffset->getLine() - 1;
48
    $columnOffset   = getColumnOffset($source, $location);
49
    $contextLine    = $line + $lineOffset;
50
    $contextColumn  = $location->getColumn() + $columnOffset;
51
    $prevLineNum    = (string)($contextLine - 1);
52
    $lineNum        = (string)$contextLine;
53
    $nextLineNum    = (string)($contextLine + 1);
54
    $padLen         = mb_strlen($nextLineNum);
55
    $lines          = preg_split("/\r\n|[\n\r]/", $source->getBody());
56
    $lines[0]       = whitespace($locationOffset->getColumn() - 1) . $lines[0];
57
    $outputLines = [
58
        sprintf('%s (%s:%s)', $source->getName(), $contextLine, $contextColumn),
59
        $line >= 2 ? leftPad($padLen, $prevLineNum) . ': ' . $lines[$line - 2] : null,
60
        leftPad($padLen, $lineNum) . ': ' . $lines[$line - 1],
61
        whitespace(2 + $padLen + $contextColumn - 1) . '^',
62
        $line < \count($lines) ? leftPad($padLen, $nextLineNum) . ': ' . $lines[$line] : null,
0 ignored issues
show
Bug introduced by
It seems like $lines can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
        $line < \count(/** @scrutinizer ignore-type */ $lines) ? leftPad($padLen, $nextLineNum) . ': ' . $lines[$line] : null,
Loading history...
63
    ];
64
65
    return implode("\n", array_filter($outputLines, function ($line) {
66
        return null !== $line;
67
    }));
68
}
69
70
/**
71
 * @param Source         $source
72
 * @param SourceLocation $location
73
 * @return int
74
 */
75
function getColumnOffset(Source $source, SourceLocation $location): int
76
{
77
    return $location->getLine() === 1 ? $source->getLocationOffset()->getColumn() - 1 : 0;
78
}
79
80
/**
81
 * @param int $length
82
 * @return string
83
 */
84
function whitespace(int $length): string
85
{
86
    return str_repeat(' ', $length);
87
}
88
89
/**
90
 * @param int    $length
91
 * @param string $str
92
 * @return string
93
 */
94
function leftPad(int $length, string $str): string
95
{
96
    return whitespace($length - mb_strlen($str)) . $str;
97
}
98