Passed
Pull Request — master (#675)
by Songda
02:04
created

Pay::set()   B

Complexity

Conditions 8
Paths 17

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 24
rs 8.4444
cc 8
nc 17
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yansongda\Pay;
6
7
use Closure;
8
use Illuminate\Container\Container as LaravelContainer;
0 ignored issues
show
Bug introduced by
The type Illuminate\Container\Container was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Psr\Container\ContainerInterface;
10
use Psr\Container\NotFoundExceptionInterface;
11
use think\Container as ThinkPHPContainer;
12
use Throwable;
13
use Yansongda\Pay\Contract\ServiceProviderInterface;
14
use Yansongda\Pay\Exception\ContainerException;
15
use Yansongda\Pay\Exception\ContainerNotFoundException;
16
use Yansongda\Pay\Exception\ServiceNotFoundException;
17
use Yansongda\Pay\Provider\Alipay;
18
use Yansongda\Pay\Provider\Wechat;
19
use Yansongda\Pay\Service\AlipayServiceProvider;
20
use Yansongda\Pay\Service\ConfigServiceProvider;
21
use Yansongda\Pay\Service\ContainerServiceProvider;
22
use Yansongda\Pay\Service\EventServiceProvider;
23
use Yansongda\Pay\Service\HttpServiceProvider;
24
use Yansongda\Pay\Service\LoggerServiceProvider;
25
use Yansongda\Pay\Service\WechatServiceProvider;
26
27
/**
28
 * @method static Alipay alipay(array $config = [], $container = null)
29
 * @method static Wechat wechat(array $config = [], $container = null)
30
 */
31
class Pay
32
{
33
    /**
34
     * 正常模式.
35
     */
36
    public const MODE_NORMAL = 0;
37
38
    /**
39
     * 沙箱模式.
40
     */
41
    public const MODE_SANDBOX = 1;
42
43
    /**
44
     * 服务商模式.
45
     */
46
    public const MODE_SERVICE = 2;
47
48
    /**
49
     * @var string[]
50
     */
51
    protected array $service = [
52
        AlipayServiceProvider::class,
53
        WechatServiceProvider::class,
54
    ];
55
56
    /**
57
     * @var string[]
58
     */
59
    private array $coreService = [
60
        ContainerServiceProvider::class,
61
        ConfigServiceProvider::class,
62
        LoggerServiceProvider::class,
63
        EventServiceProvider::class,
64
        HttpServiceProvider::class,
65
    ];
66
67
    /**
68
     * @var \Closure|\Psr\Container\ContainerInterface|null
69
     */
70
    private static $container = null;
71
72
    /**
73
     * @param \Closure|\Psr\Container\ContainerInterface|null $container
74
     *
75
     * @throws \Yansongda\Pay\Exception\ContainerException
76
     */
77
    private function __construct(array $config, $container = null)
78
    {
79
        $this->registerServices($config, $container);
80
    }
81
82
    /**
83
     * @return mixed
84
     *
85
     * @throws \Yansongda\Pay\Exception\ContainerException
86
     * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
87
     */
88
    public static function __callStatic(string $service, array $config)
89
    {
90
        if (!empty($config)) {
91
            self::config(...$config);
0 ignored issues
show
Bug introduced by
$config is expanded, but the parameter $config of Yansongda\Pay\Pay::config() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
            self::config(/** @scrutinizer ignore-type */ ...$config);
Loading history...
92
        }
93
94
        return self::get($service);
95
    }
96
97
    /**
98
     * @param \Closure|\Psr\Container\ContainerInterface|null $container
99
     *
100
     * @throws \Yansongda\Pay\Exception\ContainerException
101
     */
102
    public static function config(array $config = [], $container = null): bool
103
    {
104
        if (self::hasContainer() && !($config['_force'] ?? false)) {
105
            return false;
106
        }
107
108
        new self($config, $container);
109
110
        return true;
111
    }
112
113
    /**
114
     * @param mixed $value
115
     *
116
     * @throws \Yansongda\Pay\Exception\ContainerException
117
     */
118
    public static function set(string $name, $value): void
119
    {
120
        try {
121
            $container = Pay::getContainer();
122
123
            switch (true) {
124
                case $container instanceof LaravelContainer: // @phpstan-ignore-line
125
                    $container->singleton($name, $value instanceof Closure ? $value : static fn () => $value); // @phpstan-ignore-line
0 ignored issues
show
Bug introduced by
The method singleton() does not exist on Psr\Container\ContainerInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
                    $container->/** @scrutinizer ignore-call */ 
126
                                singleton($name, $value instanceof Closure ? $value : static fn () => $value); // @phpstan-ignore-line

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
126
                    break;
127
                case $container instanceof ThinkPHPContainer: // @phpstan-ignore-line
128
                    $container->delete($name);
129
                    $container->bind($name, $value instanceof Closure ? $value : static fn () => $value); // @phpstan-ignore-line
130
                    break;
131
                default:
132
                    if (!method_exists($container, 'set')) {
133
                        throw new ContainerException('Current container does NOT support `set` method');
134
                    }
135
136
                    $container->set($name, $value);
137
            }
138
        } catch (ContainerNotFoundException $e) {
139
            throw $e;
140
        } catch (Throwable $e) {
141
            throw new ContainerException($e->getMessage());
142
        }
143
    }
144
145
    /**
146
     * @return mixed
147
     *
148
     * @throws \Yansongda\Pay\Exception\ContainerException
149
     */
150
    public static function make(string $service, array $parameters = [])
151
    {
152
        try {
153
            $container = Pay::getContainer();
154
155
            if (method_exists($container, 'make')) {
156
                return $container->make(...func_get_args());
157
            }
158
        } catch (ContainerNotFoundException $e) {
159
            throw $e;
160
        } catch (Throwable $e) {
161
            throw new ContainerException($e->getMessage());
162
        }
163
164
        $parameters = array_values($parameters);
165
166
        return new $service(...$parameters);
167
    }
168
169
    /**
170
     * @return mixed
171
     *
172
     * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
173
     * @throws \Yansongda\Pay\Exception\ContainerException
174
     */
175
    public static function get(string $service)
176
    {
177
        try {
178
            $container = Pay::getContainer();
179
180
            // thinkphp 在 `get` 中必须是已经 bind 的,否则会报错,所以这里用其 make 替代
181
            switch (true) {
182
                case $container instanceof ThinkPHPContainer: // @phpstan-ignore-line
183
                    return $container->make($service);
184
                default:
185
                    return $container->get($service);
186
            }
187
        } catch (NotFoundExceptionInterface $e) {
188
            throw new ServiceNotFoundException($e->getMessage());
189
        } catch (ContainerNotFoundException $e) {
190
            throw $e;
191
        } catch (Throwable $e) {
192
            throw new ContainerException($e->getMessage());
193
        }
194
    }
195
196
    /**
197
     * @throws \Yansongda\Pay\Exception\ContainerNotFoundException
198
     */
199
    public static function has(string $service): bool
200
    {
201
        return Pay::getContainer()->has($service);
202
    }
203
204
    /**
205
     * @param \Closure|\Psr\Container\ContainerInterface|null $container
206
     */
207
    public static function setContainer($container): void
208
    {
209
        self::$container = $container;
210
    }
211
212
    /**
213
     * @throws \Yansongda\Pay\Exception\ContainerNotFoundException
214
     */
215
    public static function getContainer(): ContainerInterface
216
    {
217
        if (self::$container instanceof ContainerInterface) {
218
            return self::$container;
219
        }
220
221
        if (self::$container instanceof Closure) {
222
            return (self::$container)();
223
        }
224
225
        throw new ContainerNotFoundException('`getContainer()` failed! Maybe you should `setContainer()` first', Exception\Exception::CONTAINER_NOT_FOUND);
226
    }
227
228
    public static function hasContainer(): bool
229
    {
230
        return self::$container instanceof ContainerInterface || self::$container instanceof Closure;
231
    }
232
233
    public static function clear(): void
234
    {
235
        self::$container = null;
236
    }
237
238
    /**
239
     * @param mixed $data
240
     *
241
     * @throws \Yansongda\Pay\Exception\ContainerException
242
     */
243
    public static function registerService(string $service, $data): void
244
    {
245
        $var = new $service();
246
247
        if ($var instanceof ServiceProviderInterface) {
248
            $var->register($data);
249
        }
250
    }
251
252
    /**
253
     * @param \Closure|\Psr\Container\ContainerInterface|null $container
254
     *
255
     * @throws \Yansongda\Pay\Exception\ContainerException
256
     */
257
    private function registerServices(array $config, $container = null): void
258
    {
259
        foreach (array_merge($this->coreService, $this->service) as $service) {
260
            self::registerService($service, ContainerServiceProvider::class == $service ? $container : $config);
261
        }
262
    }
263
}
264