Completed
Push — master ( e69821...8f6b93 )
by Thomas Mauro
02:08
created

Sentry   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 96.34%

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 4
dl 0
loc 186
ccs 79
cts 82
cp 0.9634
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
C doWrite() 0 47 9
C cleanBacktrace() 0 34 7
A sanitizeContextData() 0 6 1
B sanitizeContextItem() 0 11 5
A objectIsThrowable() 0 4 2
A contextContainsException() 0 8 2
1
<?php
2
3
namespace Facile\SentryModule\Log\Writer;
4
5
use Facile\SentryModule\Service\Client;
6
use Zend\Log\Writer\AbstractWriter;
7
use Zend\Log\Logger;
8
use Raven_Client;
9
10
/**
11
 * Class Sentry.
12
 */
13
class Sentry extends AbstractWriter
14
{
15
    /**
16
     * @var Client
17
     */
18
    protected $client;
19
20
    /**
21
     * @var array
22
     */
23
    protected $priorityMap = [
24
        Logger::EMERG => Raven_Client::FATAL,
25
        Logger::ALERT => Raven_Client::ERROR,
26
        Logger::CRIT => Raven_Client::ERROR,
27
        Logger::ERR => Raven_Client::ERROR,
28
        Logger::WARN => Raven_Client::WARNING,
29
        Logger::NOTICE => Raven_Client::INFO,
30
        Logger::INFO => Raven_Client::INFO,
31
        Logger::DEBUG => Raven_Client::DEBUG,
32
    ];
33
34
    /**
35
     * Sentry constructor.
36
     *
37
     * @param array $options
38
     *
39
     * @throws \RuntimeException
40
     * @throws \Zend\Log\Exception\InvalidArgumentException
41
     */
42 7
    public function __construct(array $options)
43
    {
44 7
        parent::__construct($options);
45
46 7
        if (!array_key_exists('client', $options)) {
47 1
            throw new \RuntimeException('No client specified in options');
48
        }
49
50 6
        $this->client = $options['client'];
51 6
    }
52
53
    /**
54
     * Write a message to the log.
55
     *
56
     * @param array $event log data event
57
     */
58 5
    protected function doWrite(array $event)
59
    {
60 5
        $priority = $this->priorityMap[$event['priority']];
61
62 5
        $extra = $event['extra'];
63 5
        if ($extra instanceof \Traversable) {
64 1
            $extra = iterator_to_array($extra);
65 5
        } elseif (!is_array($extra)) {
66 1
            $extra = [];
67 1
        }
68
69 5
        if ($this->contextContainsException($extra)) {
70
            /** @var \Throwable $exception */
71 2
            $exception = $extra['exception'];
72 2
            unset($extra['exception']);
73
74 2
            if ($event['message'] !== $exception->getMessage()) {
75 1
                $exception = new ContextException($event['message'], $exception->getCode(), $exception);
76 1
            }
77
78 2
            $this->client->getRaven()->captureException(
79 2
                $exception,
80
                [
81 2
                    'extra' => $this->sanitizeContextData($extra),
82 2
                    'level' => $priority,
83
                ]
84 2
            );
85
86 2
            return;
87
        }
88
89 3
        $stack = isset($extra['stack']) && is_array($extra['stack']) ? $extra['stack'] : null;
90
91 3
        if (!$stack) {
92 3
            $stack = $this->cleanBacktrace(debug_backtrace());
93 3
            if (!count($stack)) {
94
                $stack = false;
95
            }
96 3
        }
97
98 3
        $this->client->getRaven()->captureMessage(
99 3
            $event['message'],
100 3
            $this->sanitizeContextData($extra),
101 3
            $priority,
102
            $stack
0 ignored issues
show
Bug introduced by
It seems like $stack can also be of type array; however, Raven_Client::captureMessage() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
103 3
        );
104 3
    }
105
106
    /**
107
     * Remove first backtrace items until it founds something different from loggers.
108
     *
109
     * @param array $backtrace
110
     *
111
     * @return array
112
     */
113 3
    protected function cleanBacktrace(array $backtrace)
114
    {
115
        $excludeNamespaces = [
116 3
            'Facile\SentryModule\Log\\',
117 3
            'Psr\Log\\',
118 3
            'Zend\Log\\',
119 3
        ];
120
121 3
        $lastItem = null;
122 3
        while (count($backtrace)) {
123 3
            $item = $backtrace[0];
124 3
            if (!array_key_exists('class', $item)) {
125
                break;
126
            }
127 3
            $exclude = false;
128 3
            foreach ($excludeNamespaces as $namespace) {
129 3
                if (0 === strpos($item['class'], $namespace)) {
130 3
                    $exclude = true;
131 3
                    break;
132
                }
133 3
            }
134 3
            if (!$exclude) {
135 3
                break;
136
            }
137
138 3
            $lastItem = array_shift($backtrace);
139 3
        }
140
141 3
        if ($lastItem) {
142 3
            array_unshift($backtrace, $lastItem);
143 3
        }
144
145 3
        return $backtrace;
146
    }
147
148
    /**
149
     * @param array $context
150
     *
151
     * @return array
152
     */
153 5
    protected function sanitizeContextData(array $context)
154
    {
155 5
        array_walk_recursive($context, [$this, 'sanitizeContextItem']);
156
157 5
        return $context;
158
    }
159
160
    /**
161
     * @param mixed $value
162
     */
163 4
    protected function sanitizeContextItem(&$value)
164
    {
165 4
        if ($value instanceof \Traversable) {
166 1
            $value = $this->sanitizeContextData(iterator_to_array($value));
167 1
        }
168 4
        if (is_object($value)) {
169 2
            $value = method_exists($value, '__toString') ? (string) $value : get_class($value);
170 4
        } elseif (is_resource($value)) {
171 2
            $value = get_resource_type($value);
172 2
        }
173 4
    }
174
175
    /**
176
     * @param mixed $object
177
     *
178
     * @return bool
179
     */
180 2
    protected function objectIsThrowable($object)
181
    {
182 2
        return $object instanceof \Throwable || $object instanceof \Exception;
183
    }
184
185
    /**
186
     * @param array $context
187
     *
188
     * @return bool
189
     */
190 5
    protected function contextContainsException(array $context)
191
    {
192 5
        if (!array_key_exists('exception', $context)) {
193 3
            return false;
194
        }
195
196 2
        return $this->objectIsThrowable($context['exception']);
197
    }
198
}
199