• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

emgiezet / errbitPHP / #89

pending completion
#89

push

php-coveralls

web-flow
Merge pull request #29 from emgiezet/feature/php8.0-support

php8.0 - support removed some fancy stuff like readonly arguments

1 of 1 new or added line in 1 file covered. (100.0%)

337 of 421 relevant lines covered (80.05%)

2.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/src/Errbit/Errbit.php
1
<?php
2
declare(strict_types=1);
3
namespace Errbit;
4

5
use Errbit\Exception\Exception;
6

7
use Errbit\Errors\Notice;
8
use Errbit\Errors\Error;
9
use Errbit\Errors\Fatal;
10
use Errbit\Handlers\ErrorHandlers;
11
use Errbit\Writer\WriterInterface;
12

13
/**
14
 * The Errbit client.
15
 *
16
 * @example Configuring the client
17
 *    Errbit::instance()->configure(array( ... ))->start();
18
 *
19
 * @example Notify an Exception manually
20
 *    Errbit::instance()->notify($exception);
21
 *
22
 */
23
class Errbit
24
{
25
    private static ?\Errbit\Errbit $instance = null;
26

27
    /**
28
     * @var WriterInterface
29
     */
30
    protected WriterInterface $writer;
31

32
    /**
33
     * Get a singleton instance of the client.
34
     *
35
     * This is the intended way to access the Errbit client.
36
     *
37
     * @return Errbit a singleton
38
     */
39
    public static function instance()
40
    {
×
41
        if (!isset(self::$instance)) {
×
42
            self::$instance = new self();
×
43
        }
44

×
45
        return self::$instance;
46
    }
47

48
    public const VERSION       = '1.0.5';
49
    public const API_VERSION   = '2.2';
50
    public const PROJECT_NAME  = 'errbit-php';
51
    public const PROJECT_URL   = 'https://github.com/emgiezet/errbit-php';
52
    private array $observers = [];
53

54
    /**
55
     * Initialize a new client with the given config.
56
     *
57
     * This is made public for flexibility, though it is not expected you
58
     * should use it.
59
     *
60
     * @param array $config the configuration for the API
61
     */
62
    public function __construct(private $config = [])
63
    {
64
    }
65

3✔
66
    public function setWriter(WriterInterface $writer): void
3✔
67
    {
68
        $this->writer = $writer;
69
    }
70

71
    /**
72
     * Add a handler to be invoked after a notification occurs.
73
     *
2✔
74
     * @param [Callback] $callback any callable function
2✔
75
     *
76
     * @throws [Exception]
77
     *
78
     * @return static the current instance
79
     */
80
    public function onNotify($callback): static
81
    {
82
        if (!is_callable($callback)) {
83
            throw new Exception('Notify callback must be callable');
84
        }
85

86
        $this->observers[] = $callback;
×
87

×
88
        return $this;
89
    }
90

×
91
    /**
92
     * Set the full configuration for the client.
×
93
     *
94
     * The only required keys are `api_key' and `host', but other supported
95
     * options are:
96
     *
97
     *   - api_key
98
     *   - host
99
     *   - port
100
     *   - secure
101
     *   - project_root
102
     *   - environment_name
103
     *   - url
104
     *   - controller
105
     *   - action
106
     *   - session_data
107
     *   - parameters
108
     *   - cgi_data
109
     *   - params_filters
110
     *   - backtrace_filters
111
     *
112
     * @param [Array] $config the full configuration
113
     *
114
     * @return static the current instance of the client
115
     */
116
    public function configure($config = []): static
117
    {
118
        $this->config = array_merge($this->config, $config);
119
        $this->checkConfig();
120

121
        return $this;
122
    }
×
123

×
124
    /**
125
     * Register all error handlers around this instance.
×
126
     *
127
     * @param [Array] $handlers an array of handler names (one or all of 'exception', 'error', 'fatal')
128
     *
129
     * @return static the current instance
130
     */
131
    public function start($handlers = ['exception', 'error', 'fatal']): static
132
    {
133
        $this->checkConfig();
134
        ErrorHandlers::register($this, $handlers);
135

136
        return $this;
137
    }
138
    
×
139
    /**
×
140
     * Notify an individual exception manually.
141
     *
×
142
     * @param array $options
143
     *
144
     * @return static [Errbit] the current instance
145
     * @throws \Errbit\Exception\Exception
146
     */
147
    public function notify(Fatal|Errors\Warning|Notice|Error $exception, $options = []): static
148
    {
149
        $this->checkConfig();
150
        $config = array_merge($this->config, $options);
151

152
        if ($this->shouldNotify($exception, $config['skipped_exceptions'])) {
153
            $this->getWriter()->write($exception, $config);
154
            $this->notifyObservers($exception, $config);
3✔
155
        }
3✔
156

157
        return $this;
3✔
158
    }
2✔
159

2✔
160
    protected function shouldNotify($exception, array $skippedExceptions): bool
2✔
161
    {
162
        foreach ($skippedExceptions as $skippedException) {
3✔
163
            if ($exception instanceof $skippedException) {
164
                return false;
165
            }
166
        }
167
        foreach ($this->config['ignore_user_agent'] as $ua) {
3✔
168
            if (str_contains((string) $_SERVER['HTTP_USER_AGENT'],$ua) ) {
2✔
169
                return false;
1✔
170
            }
171
        }
2✔
172

173
        return true;
2✔
174
    }
175

176
    protected function notifyObservers($exception, array $config): void
177
    {
178
        foreach ($this->observers as $observer) {
2✔
179
            $observer($exception, $config);
×
180
        }
2✔
181
    }
2✔
182

183
    protected function getWriter(): object
184
    {
185
        if (empty($this->writer)) {
2✔
186
            $defaultWriter = new $this->config['default_writer'];
1✔
187
            $this->writer = $defaultWriter;
1✔
188
        }
1✔
189

190
        return $this->writer;
2✔
191
    }
192

193
    // -- Private Methods
194
    /**
195
     * Config checker
196
     *
197
     * @throws Exception
198
     */
199
    private function checkConfig(): void
200
    {
201
        if (empty($this->config['api_key'])) {
202
            throw new Exception("`api_key' must be configured");
3✔
203
        }
×
204

205
        if (empty($this->config['host'])) {
206
            throw new Exception("`host' must be configured");
3✔
207
        }
×
208

209
        if (empty($this->config['port'])) {
210
            $this->config['port'] = !empty($this->config['secure']) ? 443 : 80;
3✔
211
        }
2✔
212

2✔
213
        if (!isset($this->config['secure'])) {
214
            $this->config['secure'] = ($this->config['port'] == 443);
3✔
215
        }
2✔
216

2✔
217
        if (empty($this->config['hostname'])) {
218
            $this->config['hostname'] = gethostname() ?: '<unknown>';
3✔
219
        }
3✔
220

3✔
221
        if (empty($this->config['project_root'])) {
222
            $this->config['project_root'] = __DIR__;
3✔
223
        }
3✔
224

3✔
225
        if (empty($this->config['environment_name'])) {
226
            $this->config['environment_name'] = 'development';
3✔
227
        }
3✔
228

3✔
229
        if (!isset($this->config['params_filters'])) {
230
            $this->config['params_filters'] = ['/password/'];
3✔
231
        }
3✔
232

3✔
233
        if (!isset($this->config['connect_timeout'])) {
234
            $this->config['connect_timeout'] = 3;
3✔
235
        }
3✔
236

3✔
237
        if (!isset($this->config['write_timeout'])) {
238
            $this->config['write_timeout'] = 3;
3✔
239
        }
3✔
240

3✔
241
        if (!isset($this->config['backtrace_filters'])) {
242
            $this->config['backtrace_filters'] = [sprintf('/^%s/', preg_quote((string) $this->config['project_root'], '/')) => '[PROJECT_ROOT]'];
3✔
243
        }
3✔
244

3✔
245
        if (!isset($this->config['skipped_exceptions'])) {
3✔
246
            $this->config['skipped_exceptions'] = [];
3✔
247
        }
248

3✔
249
        if (!isset($this->config['default_writer'])) {
1✔
250
            $this->config['default_writer'] = \Errbit\Writer\SocketWriter::class;
1✔
251
        }
252

3✔
253
        if (!isset($this->config['agent'])) {
3✔
254
            $this->config['agent'] = 'errbitPHP';
3✔
255
        }
256
        if (!isset($this->config['async'])) {
3✔
257
            $this->config['async'] = false;
3✔
258
        }
3✔
259
        if (!isset($this->config['ignore_user_agent'])) {
3✔
260
            $this->config['ignore_user_agent'] = [];
3✔
261
        }
3✔
262
    }
3✔
263
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc