CustomErrorMiddleware   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Test Coverage

Coverage 91.67%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 29
c 3
b 0
f 0
dl 0
loc 114
ccs 33
cts 36
cp 0.9167
rs 10
wmc 12

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A fromNamespace() 0 17 3
A fromFactory() 0 5 1
A fromMapping() 0 13 4
A process() 0 10 3
1
<?php
2
3
/**
4
 * This file is part of the tarantool/client package.
5
 *
6
 * (c) Eugene Leonovich <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Tarantool\Client\Middleware;
15
16
use Tarantool\Client\Error;
17
use Tarantool\Client\Exception\RequestFailed;
18
use Tarantool\Client\Handler\Handler;
19
use Tarantool\Client\Request\Request;
20
use Tarantool\Client\Response;
21
22
final class CustomErrorMiddleware implements Middleware
23
{
24
    /** @var \Closure(Error, RequestFailed) : \Exception */
25
    private $factory;
26
27
    /**
28
     * @param \Closure(Error, RequestFailed) : \Exception $factory
29
     */
30 16
    private function __construct($factory)
31
    {
32 16
        $this->factory = $factory;
33
    }
34
35
    /**
36
     * Creates the middleware from a provided closure which receives
37
     * `Tarantool\Client\Error` and `Tarantool\Client\Exception\RequestFailed`
38
     * objects as arguments and should return a custom exception.
39
     *
40
     * Example:
41
     *
42
     * ```php
43
     * $middleware = CustomErrorMiddleware::fromFactory(
44
     *     static function (Error $err, RequestFailed $ex) : \Exception {
45
     *         return 'UserNotFound' === $err->tryGetField('custom_type')
46
     *             ? new UserNotFound($err->getMessage(), $err->getCode())
47
     *             : $ex;
48
     *     }
49
     * );
50
     * ```
51
     */
52 4
    public static function fromFactory(\Closure $factory) : self
53
    {
54 4
        return new self(
55 4
            static function (Error $err, RequestFailed $ex) use ($factory) : \Exception {
56 4
                return $factory($err, $ex);
57 4
            }
58 4
        );
59
    }
60
61
    /**
62
     * Creates the middleware from an array in which the keys are custom types
63
     * of box.error objects and the corresponding values are fully qualified names
64
     * of custom exception classes.
65
     *
66
     * Example:
67
     *
68
     * ```php
69
     * $middleware = CustomErrorMiddleware::fromMapping([
70
     *     'UserNotFound' => UserNotFound::class,
71
     *     'MyCustomType' => MyCustomException::class,
72
     *     ...
73
     * ]);
74
     * ```
75
     *
76
     * @param array<string, class-string<\Exception>> $mapping
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, class-string<\Exception>> at position 4 could not be parsed: Unknown type name 'class-string' at position 4 in array<string, class-string<\Exception>>.
Loading history...
77
     */
78 8
    public static function fromMapping(array $mapping) : self
79
    {
80 8
        return new self(
81 8
            static function (Error $err, RequestFailed $ex) use ($mapping) : \Exception {
82
                do {
83 8
                    $customType = $err->tryGetField('custom_type');
84 8
                    if ($customType && isset($mapping[$customType])) {
85
                        /** @psalm-suppress UnsafeInstantiation */
86 8
                        return new $mapping[$customType]($err->getMessage(), $err->getCode());
87
                    }
88 4
                } while ($err = $err->getPrevious());
89
90
                return $ex;
91 8
            }
92 8
        );
93
    }
94
95
    /**
96
     * Creates the middleware from the base namespace for the custom exception classes.
97
     * The exception class name then will be in the format of "<namespace>\<lua_error.custom_type>".
98
     *
99
     * Example:
100
     *
101
     * ```php
102
     * $middleware = CustomErrorMiddleware::fromNamespace('Foo\Bar');
103
     * ```
104
     */
105 4
    public static function fromNamespace(string $namespace) : self
106
    {
107 4
        $namespace = \rtrim($namespace, '\\').'\\';
108
109 4
        return new self(
110 4
            static function (Error $err, RequestFailed $ex) use ($namespace) : \Exception {
111 4
                if (!$customType = $err->tryGetField('custom_type')) {
112
                    return $ex;
113
                }
114
115
                /** @var class-string<\Exception> $className */
116 4
                $className = $namespace.$customType;
117
118
                /** @psalm-suppress UnsafeInstantiation */
119 4
                return \class_exists($className)
120 4
                    ? new $className($err->getMessage(), $err->getCode())
121 4
                    : $ex;
122 4
            }
123 4
        );
124
    }
125
126 16
    public function process(Request $request, Handler $handler) : Response
127
    {
128
        try {
129 16
            return $handler->handle($request);
130 16
        } catch (RequestFailed $e) {
131 16
            if ($error = $e->getError()) {
132 16
                throw ($this->factory)($error, $e);
133
            }
134
135
            throw $e;
136
        }
137
    }
138
}
139