Completed
Push — master ( 26a01e...b7bf72 )
by Eugene
07:05
created

CustomErrorMiddleware   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 117
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 91.18%

Importance

Changes 0
Metric Value
dl 0
loc 117
ccs 31
cts 34
cp 0.9118
c 0
b 0
f 0
rs 10
wmc 12
lcom 2
cbo 3

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A fromFactory() 0 8 1
A fromMapping() 0 16 4
A fromNamespace() 0 20 3
A process() 0 12 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 12
    private function __construct($factory)
31
    {
32 12
        $this->factory = $factory;
33 12
    }
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 3
    public static function fromFactory(\Closure $factory) : self
53
    {
54 3
        return new self(
55 3
            static function (Error $err, RequestFailed $ex) use ($factory) : \Exception {
56 3
                return $factory($err, $ex);
57 3
            }
58
        );
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
77
     */
78 6
    public static function fromMapping(array $mapping) : self
79
    {
80 6
        return new self(
81 6
            static function (Error $err, RequestFailed $ex) use ($mapping) : \Exception {
82
                do {
83 6
                    $customType = $err->tryGetField('custom_type');
84 6
                    if ($customType && isset($mapping[$customType])) {
85
                        /** @psalm-suppress UnsafeInstantiation */
86 6
                        return new $mapping[$customType]($err->getMessage(), $err->getCode());
87
                    }
88 3
                } while ($err = $err->getPrevious());
89
90
                return $ex;
91 6
            }
92
        );
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 3
    public static function fromNamespace(string $namespace) : self
106
    {
107 3
        $namespace = \rtrim($namespace, '\\').'\\';
108
109 3
        return new self(
110 3
            static function (Error $err, RequestFailed $ex) use ($namespace) : \Exception {
111 3
                if (!$customType = $err->tryGetField('custom_type')) {
112
                    return $ex;
113
                }
114
115
                /** @var class-string<\Exception> $className */
0 ignored issues
show
Documentation introduced by
The doc-type class-string<\Exception> could not be parsed: Unknown type name "class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
116 3
                $className = $namespace.$customType;
117
118
                /** @psalm-suppress UnsafeInstantiation */
119 3
                return \class_exists($className)
120 3
                    ? new $className($err->getMessage(), $err->getCode())
121 3
                    : $ex;
122 3
            }
123
        );
124
    }
125
126 12
    public function process(Request $request, Handler $handler) : Response
127
    {
128
        try {
129 12
            return $handler->handle($request);
130 12
        } catch (RequestFailed $e) {
131 12
            if ($error = $e->getError()) {
132 12
                throw ($this->factory)($error, $e);
133
            }
134
135
            throw $e;
136
        }
137
    }
138
}
139