Completed
Branch master (5c1b13)
by Markus
184:11 queued 119:40
created

ExceptionStrategy::setDisplayExceptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Bright Answer ZendSentry
5
 *
6
 * This source file is part of the Bright Answer ZendSentry package
7
 *
8
 * @package    ZendSentry\Mvc\View\Console\ExceptionStrategy
9
 * @license    MIT License {@link /docs/LICENSE}
10
 * @copyright  Copyright (c) 2016, Bright Answer OÜ
11
 */
12
13
namespace ZendSentry\Mvc\View\Console;
14
15
use Zend\EventManager\AbstractListenerAggregate;
16
use Zend\EventManager\EventManagerInterface;
17
use Zend\Mvc\Application;
18
use Zend\Mvc\MvcEvent;
19
use Zend\Stdlib\ResponseInterface;
20
use Zend\View\Model\ConsoleModel;
21
22
/**
23
 * For the moment, this is just an augmented copy of the default ZF ExceptionStrategy
24
 * This is on purpose despite the duplication of code until the module stabilizes and it's clear what need exactly
25
 *
26
 * @package    ZendSentry\Mvc\View\Console\ExceptionStrategy
27
 */
28
class ExceptionStrategy extends AbstractListenerAggregate
29
{
30
    /**
31
     * Display exceptions?
32
     * @var bool
33
     */
34
    protected $displayExceptions = false;
35
36
    /**
37
     * Default Exception Message
38
     * @var string
39
     */
40
    protected $defaultExceptionMessage = "Oh no. Something went wrong, but we have been notified.\n";
41
42
    /**
43
     * A template for message to show in console when an exception has occurred.
44
     * @var string|callable
45
     */
46
    protected $message = <<<EOT
47
    ======================================================================
48
       The application has thrown an exception!
49
    ======================================================================
50
     :className
51
     :message
52
    ----------------------------------------------------------------------
53
    :file::line
54
    :stack
55
    ======================================================================
56
       Previous Exception(s):
57
    ======================================================================
58
    :previous
59
60
EOT;
61
62
    /**
63
     * {@inheritDoc}
64
     */
65
    public function attach(EventManagerInterface $events, $priority = 1)
66
    {
67
        $this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH_ERROR, [$this, 'prepareExceptionViewModel']);
68
        $this->listeners[] = $events->attach(MvcEvent::EVENT_RENDER_ERROR, [$this, 'prepareExceptionViewModel']);
69
    }
70
71
    /**
72
     * Flag: display exceptions in error pages?
73
     *
74
     * @param  bool $displayExceptions
75
     * @return ExceptionStrategy
76
     */
77
    public function setDisplayExceptions($displayExceptions): ExceptionStrategy
78
    {
79
        $this->displayExceptions = (bool) $displayExceptions;
80
        return $this;
81
    }
82
83
    /**
84
     * Should we display exceptions in error pages?
85
     *
86
     * @return bool
87
     */
88
    public function displayExceptions(): bool
89
    {
90
        return $this->displayExceptions;
91
    }
92
93
    /**
94
     * Get current template for message that will be shown in Console.
95
     *
96
     * @return string
97
     */
98
    public function getMessage(): string
99
    {
100
        return $this->message;
101
    }
102
103
    /**
104
     * Set the default exception message
105
     * @param string $defaultExceptionMessage
106
     * @return self
107
     */
108
    public function setDefaultExceptionMessage($defaultExceptionMessage): self
109
    {
110
        $this->defaultExceptionMessage = $defaultExceptionMessage;
111
        return $this;
112
    }
113
114
    /**
115
     * Set template for message that will be shown in Console.
116
     * The message can be a string (template) or a callable (i.e. a closure).
117
     *
118
     * The closure is expected to return a string and will be called with 2 parameters:
119
     *        Exception $exception           - the exception being thrown
120
     *        boolean   $displayExceptions   - whether to display exceptions or not
121
     *
122
     * If the message is a string, one can use the following template params:
123
     *
124
     *   :className   - full class name of exception instance
125
     *   :message     - exception message
126
     *   :code        - exception code
127
     *   :file        - the file where the exception has been thrown
128
     *   :line        - the line where the exception has been thrown
129
     *   :stack       - full exception stack
130
     *
131
     * @param string|callable  $message
132
     * @return ExceptionStrategy
133
     */
134
    public function setMessage($message): ExceptionStrategy
135
    {
136
        $this->message = $message;
137
        return $this;
138
    }
139
140
    /**
141
     * Create an exception view model, and set the console status code
142
     *
143
     * @param  MvcEvent $e
144
     * @return void
145
     */
146
    public function prepareExceptionViewModel(MvcEvent $e): void
147
    {
148
        // Do nothing if no error in the event
149
        $error = $e->getError();
150
        if (empty($error)) {
151
            return;
152
        }
153
154
        // Do nothing if the result is a response object
155
        $result = $e->getResult();
156
        if ($result instanceof ResponseInterface) {
157
            return;
158
        }
159
160
        // Proceed to showing an error page with or without exception
161
        switch ($error) {
162
            case Application::ERROR_CONTROLLER_NOT_FOUND:
163
            case Application::ERROR_CONTROLLER_INVALID:
164
            case Application::ERROR_ROUTER_NO_MATCH:
165
                // Specifically not handling these
166
                return;
167
168
            case Application::ERROR_EXCEPTION:
169
            default:
170
                // Prepare error message
171
                $exception = $e->getParam('exception');
172
173
                // Log exception to sentry by triggering an exception event
174
                $e->getApplication()->getEventManager()->trigger('logException', $this, ['exception' => $exception]);
175
176
                if (\is_callable($this->message)) {
177
                    $callback = $this->message;
178
                    $message = (string) $callback($exception, $this->displayExceptions);
179
                } elseif ($this->displayExceptions && $exception instanceof \Exception) {
180
                    /* @var $exception \Exception */
181
                    $message = str_replace(
182
                        [
183
                            ':className',
184
                            ':message',
185
                            ':code',
186
                            ':file',
187
                            ':line',
188
                            ':stack',
189
                            ':previous',
190
                        ], [
191
                        \get_class($exception),
192
                        $exception->getMessage(),
193
                        $exception->getCode(),
194
                        $exception->getFile(),
195
                        $exception->getLine(),
196
                        $exception->getTraceAsString(),
197
                        $exception->getPrevious(),
198
                    ],
199
                        $this->message
200
                    );
201
                } else {
202
                    $message = $this->defaultExceptionMessage;
203
                }
204
205
                // Prepare view model
206
                $model = new ConsoleModel();
207
                $model->setResult($message);
208
                $model->setErrorLevel(1);
209
210
                // Inject it into MvcEvent
211
                $e->setResult($model);
212
213
                break;
214
        }
215
    }
216
}