Passed
Push — master ( c49a71...71afca )
by Tom
04:13
created

ExceptionHandler::debugInfoOfPipelinesItself()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 7
ccs 6
cts 6
cp 1
crap 2
rs 10
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Utility;
6
7
use Exception;
8
use Ktomk\Pipelines\Cli\ArgsException;
9
use Ktomk\Pipelines\Cli\Streams;
10
use Ktomk\Pipelines\File\ParseException;
11
12
class ExceptionHandler
13
{
14
    /**
15
     * @var bool
16
     */
17
    private $showStacktraceFlag;
18
19
    /**
20
     * @var Help
21
     */
22
    private $help;
23
    /**
24
     * @var Streams
25
     */
26
    private $streams;
27
28
    /**
29
     * ExceptionHandler constructor.
30
     *
31
     * @param Streams $streams
32
     * @param Help $help
33
     * @param bool $showStacktrace
34
     */
35 1
    public function __construct(Streams $streams, Help $help, $showStacktrace)
36
    {
37 1
        $this->streams = $streams;
38 1
        $this->help = $help;
39 1
        $this->showStacktraceFlag = $showStacktrace;
40 1
    }
41
42
    /**
43
     * @param Runnable $runnable
44
     *
45
     * @return mixed|void
46
     */
47 3
    public function handle(Runnable $runnable)
48
    {
49
        try {
50 3
            $status = $runnable->run();
51 2
        } catch (Exception $e) {
52 2
            $status = $this->handleException($e);
53
        }
54
55 3
        return $status;
56
    }
57
58
    /**
59
     * @param Exception $e
60
     *
61
     * @return int
62
     */
63 2
    private function handleException(Exception $e)
64
    {
65 2
        $status = $e->getCode();
66 2
        $message = $e->getMessage();
67
68
        // "catch" unexpected exceptions for user-friendly message
69 2
        if (!is_int($status) || $this->isUnexpectedException($e)) {
70 1
            $status = 2;
71 1
            $message = sprintf('fatal: %s', $e->getMessage());
72
        }
73
74 2
        $this->showError($status, $message);
75 2
        $this->showUsage($e);
76 2
        $this->showStacktrace($e);
77
78 2
        return $status;
79
    }
80
81
    /**
82
     * Show error message
83
     *
84
     * @param int $status
85
     * @param string $message
86
     *
87
     * @return void
88
     */
89 2
    private function showError($status, $message)
90
    {
91 2
        if (0 !== $status && '' !== $message) {
92 2
            $this->error(sprintf('pipelines: %s', $message));
93
        }
94 2
    }
95
96
    /**
97
     * Some exceptions can show usage
98
     *
99
     * @param Exception $e
100
     *
101
     * @return void
102
     */
103 2
    private function showUsage(Exception $e)
104
    {
105 2
        if ($e instanceof ArgsException) {
106 1
            $this->help->showUsage();
107
        }
108 2
    }
109
110
    /**
111
     * Show a stacktrace for debugging purposes (`--debug`` flag)
112
     *
113
     * @param Exception $e
114
     *
115
     * @return void
116
     */
117 2
    private function showStacktrace(Exception $e)
118
    {
119 2
        if ($this->showStacktraceFlag) {
120 2
            $this->debugException($e);
121
        }
122 2
    }
123
124
    /**
125
     * Some exceptions are not unexpected
126
     *
127
     * The pipelines utility uses *some* exceptions to signal program
128
     * flow in context of the command line utility. E.g. to not call
129
     * exit() somewhere deep in the code (StatusException), lazy command
130
     * line argument handling (ArgsException) and lazy pipelines.yml
131
     * file parsing (ParseException).
132
     *
133
     * @param Exception $e
134
     *
135
     * @return bool
136
     */
137 2
    private function isUnexpectedException(Exception $e)
138
    {
139
        return (
140 2
            !($e instanceof ArgsException)
141 2
            && !($e instanceof StatusException)
142 2
            && !($e instanceof ParseException)
143
        );
144
    }
145
146
    /**
147
     * @param Exception $e
148
     *
149
     * @return void
150
     */
151 2
    private function debugException(Exception $e)
152
    {
153 2
        $this->debugInfoOfPipelinesItself();
154
155 2
        for (; $e; $e = $e->getPrevious()) {
156 2
            $this->error('--------');
157 2
            $this->error(sprintf('class....: %s', get_class($e)));
158 2
            $this->error(sprintf('message..: %s', $e->getMessage()));
159 2
            $this->error(sprintf('code.....: %s', $e->getCode()));
160 2
            $this->error(sprintf('file.....: %s', $e->getFile()));
161 2
            $this->error(sprintf('line.....: %s', $e->getLine()));
162 2
            $this->error('backtrace:');
163 2
            $this->error($e->getTraceAsString());
164
        }
165 2
        $this->error('--------');
166 2
    }
167
168 2
    private function debugInfoOfPipelinesItself()
169
    {
170 2
        $this->error(sprintf(
171 2
            'pipelines: version %s w/ php %s (libyaml: %s)',
172 2
            Version::resolve(App::VERSION),
173 2
            PHP_VERSION,
174 2
            \phpversion('yaml') ?: 'n/a'
175
        ));
176 2
    }
177
178
    /**
179
     * @param string $message
180
     *
181
     * @return void
182
     */
183 2
    private function error($message)
184
    {
185 2
        $this->streams->err(
186 2
            sprintf("%s\n", $message)
187
        );
188 2
    }
189
}
190