Completed
Pull Request — master (#7)
by Thomas Mauro
05:04
created

Sentry::contextContainsException()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Facile\SentryModule\Log\Writer;
6
7
use Facile\SentryModule\Service\ClientInterface;
8
use Traversable;
9
use Zend\Log\Writer\AbstractWriter;
10
use Zend\Log\Logger;
11
use Raven_Client;
12
use Facile\SentryModule\Exception;
13
14
/**
15
 * Class Sentry.
16
 */
17
final class Sentry extends AbstractWriter
18
{
19
    /**
20
     * @var ClientInterface
21
     */
22
    private $client;
23
24
    /**
25
     * @var array
26
     */
27
    protected $priorityMap = [
28
        Logger::EMERG => Raven_Client::FATAL,
29
        Logger::ALERT => Raven_Client::ERROR,
30
        Logger::CRIT => Raven_Client::ERROR,
31
        Logger::ERR => Raven_Client::ERROR,
32
        Logger::WARN => Raven_Client::WARNING,
33
        Logger::NOTICE => Raven_Client::INFO,
34
        Logger::INFO => Raven_Client::INFO,
35
        Logger::DEBUG => Raven_Client::DEBUG,
36
    ];
37
38
    /**
39
     * Sentry constructor.
40
     *
41
     * @param array $options
42
     *
43
     * @throws \Zend\Log\Exception\InvalidArgumentException
44
     * @throws \Facile\SentryModule\Exception\InvalidArgumentException
45
     */
46 7
    public function __construct(array $options = null)
47
    {
48 7
        parent::__construct($options);
49
50 7
        if ($options instanceof Traversable) {
51
            $options = iterator_to_array($options);
52
        }
53
54 7
        if (! is_array($options) || ! array_key_exists('client', $options) || ! $options['client'] instanceof ClientInterface) {
55 1
            throw new Exception\InvalidArgumentException('No client specified in options');
56
        }
57
58 6
        $this->client = $options['client'];
59 6
    }
60
61
    /**
62
     * Write a message to the log.
63
     *
64
     * @param array $event log data event
65
     */
66 5
    protected function doWrite(array $event)
67
    {
68 5
        $priority = $this->priorityMap[$event['priority']];
69
70 5
        $extra = $event['extra'];
71 5
        if ($extra instanceof Traversable) {
72 1
            $extra = iterator_to_array($extra);
73 4
        } elseif (! is_array($extra)) {
74 1
            $extra = [];
75
        }
76
77 5
        if ($this->contextContainsException($extra)) {
78
            /** @var \Exception $exception */
79 2
            $exception = $extra['exception'];
80 2
            unset($extra['exception']);
81
82
            $data = [
83 2
                'extra' => $this->sanitizeContextData($extra),
84 2
                'level' => $priority,
85
            ];
86
87 2
            if ($event['message'] !== $exception->getMessage()) {
88 1
                $data['message'] = sprintf('%s :: %s', $event['message'], $exception->getMessage());
89
            }
90
91 2
            $this->client->getRaven()->captureException(
92
                $exception,
93 2
                $data
94
            );
95
96 2
            return;
97
        }
98
99 3
        $stack = isset($extra['stack']) && is_array($extra['stack']) && count($extra['stack'])
100 3
            ? $extra['stack'] : false;
101
102 3
        $this->client->getRaven()->captureMessage(
103 3
            $event['message'],
104 3
            [],
105
            [
106 3
                'extra' => $this->sanitizeContextData($extra),
107 3
                'level' => $priority,
108
            ],
109 3
            $stack
110
        );
111 3
    }
112
113
    /**
114
     * @param array $context
115
     *
116
     * @return array
117
     */
118 5
    protected function sanitizeContextData(array $context): array
119
    {
120 5
        array_walk_recursive($context, [$this, 'sanitizeContextItem']);
121
122 5
        return $context;
123
    }
124
125
    /**
126
     * @param mixed $value
127
     */
128 4
    protected function sanitizeContextItem(&$value)
129
    {
130 4
        if ($value instanceof Traversable) {
131 1
            $value = iterator_to_array($value);
132
        }
133
134 4
        if (is_array($value)) {
135 1
            $value = $this->sanitizeContextData($value);
136
        }
137
138 4
        if (is_object($value)) {
139 2
            $value = method_exists($value, '__toString') ? (string) $value : get_class($value);
140 4
        } elseif (is_resource($value)) {
141 2
            $value = get_resource_type($value);
142
        }
143 4
    }
144
145
    /**
146
     * @param mixed $object
147
     *
148
     * @return bool
149
     */
150 2
    protected function objectIsThrowable($object): bool
151
    {
152 2
        return $object instanceof \Throwable || $object instanceof \Exception;
153
    }
154
155
    /**
156
     * @param array $context
157
     *
158
     * @return bool
159
     */
160 5
    protected function contextContainsException(array $context): bool
161
    {
162 5
        if (! array_key_exists('exception', $context)) {
163 3
            return false;
164
        }
165
166 2
        return $this->objectIsThrowable($context['exception']);
167
    }
168
}
169