Passed
Push — master ( 793cce...b0cf56 )
by Eugene
07:19
created

CustomErrorMiddleware::process()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
nc 3
nop 2
dl 0
loc 10
rs 10
c 1
b 0
f 0
ccs 5
cts 6
cp 0.8333
crap 3.0416
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
    }
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
            }
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
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 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
            }
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 */
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
            }
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