Completed
Push — master ( ba7f7c...c7ee1c )
by Eugene
10:02
created

CustomErrorMiddleware::fromMapping()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 1
nop 1
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
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
    private function __construct($factory)
31
    {
32
        $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(), $ex)
47
     *             : $ex;
48
     *     }
49
     * );
50
     * ```
51
     */
52
    public static function fromFactory(\Closure $factory) : self
53
    {
54
        return new self(
55
            static function (Error $err, RequestFailed $ex) use ($factory) : \Exception {
56
                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
77
     */
78
    public static function fromMapping(array $mapping) : self
79
    {
80
        return new self(
81
            static function (Error $err, RequestFailed $ex) use ($mapping) : \Exception {
82
                do {
83
                    $customType = $err->tryGetField('custom_type');
84
                    if ($customType && isset($mapping[$customType])) {
85
                        return new $mapping[$customType]($err->getMessage(), $err->getCode(), $ex);
86
                    }
87
                } while ($err = $err->getPrevious());
88
89
                return $ex;
90
            }
91
        );
92
    }
93
94
    /**
95
     * Creates the middleware from the base namespace for the custom exception classes.
96
     * The exception class name then will be in the format of "<namespace>/<lua_error.custom_type>".
97
     *
98
     * Example:
99
     *
100
     * ```php
101
     * $middleware = CustomErrorMiddleware::fromNamespace('Foo\Bar');
102
     * ```
103
     */
104
    public static function fromNamespace(string $namespace) : self
105
    {
106
        $namespace = \rtrim($namespace, '\\').'\\';
107
108
        return new self(
109
            static function (Error $err, RequestFailed $ex) use ($namespace) : \Exception {
110
                if (!$customType = $err->tryGetField('custom_type')) {
111
                    return $ex;
112
                }
113
114
                /** @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...
115
                $className = $namespace.$customType;
116
                if (!\class_exists($className)) {
117
                    return $ex;
118
                }
119
120
                return new $className($err->getMessage(), $err->getCode(), $ex);
121
            }
122
        );
123
    }
124
125
    public function process(Request $request, Handler $handler) : Response
126
    {
127
        try {
128
            return $handler->handle($request);
129
        } catch (RequestFailed $e) {
130
            if ($error = $e->getError()) {
131
                throw ($this->factory)($error, $e);
132
            }
133
134
            throw $e;
135
        }
136
    }
137
}
138