Test Failed
Push — drivers ( 4bb5a4...c650c9 )
by Joe
08:01
created

Config::getCacheDriver()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
namespace PhpWinTools\WmiScripting\Configuration;
4
5
use Illuminate\Support\Arr;
6
use PhpWinTools\Support\COM\ComWrapper;
7
use PhpWinTools\Support\COM\VariantWrapper;
8
use PhpWinTools\Support\COM\ComVariantWrapper;
9
use PhpWinTools\WmiScripting\Containers\Connections;
10
use PhpWinTools\WmiScripting\Containers\Container;
11
use PhpWinTools\WmiScripting\Support\Bus\CommandBus;
12
use PhpWinTools\WmiScripting\Connections\ComConnection;
13
use PhpWinTools\WmiScripting\Support\Events\EventProvider;
14
use PhpWinTools\WmiScripting\Support\Events\EventHistoryProvider;
15
use PhpWinTools\WmiScripting\Exceptions\InvalidConnectionException;
16
use PhpWinTools\WmiScripting\Exceptions\UnresolvableClassException;
17
18
class Config extends Container
19
{
20
    /** @var Config|null */
21
    protected static $instance = null;
22
23
    protected static $test_mode = false;
24
25
    protected $resolver;
26
27
    protected $eventProvider;
28
29
    protected $resolve_stack = [];
30
31
    public function __construct(array $config = [], Resolver $resolver = null)
32
    {
33
        $this->resolver = $resolver ?? new Resolver($this);
34
        $this->boot($config);
35
36
        static::$instance = $this;
37
    }
38
39
    /**
40
     * Returns current instance if available and merges any available configuration.
41
     * This is the method used throughout the library to retrieve configuration.
42
     *
43
     * @param array $items
44
     * @param Resolver|null $resolver
45
     *
46
     * @return Config
47
     */
48
    public static function instance(array $items = [], Resolver $resolver = null)
49
    {
50
        if (static::$instance && !empty($items)) {
51
            return static::newInstance(array_merge(static::$instance->container, $items), $resolver);
52
        }
53
54
        if (static::$instance && $resolver) {
55
            static::$instance->resolver = $resolver;
56
        }
57
58
        return static::$instance ?? static::newInstance($items, $resolver);
59
    }
60
61
    /**
62
     * Always returns a new instance. Used primary for testing. If used be aware that any previous changes
63
     * to the instance will be lost. If it's in test mode it will remain until you explicitly remove it.
64
     * You should never need to reference this directly except inside of tests.
65
     *
66
     * @param array $items
67
     * @param Resolver|null $resolver
68
     *
69
     * @return Config
70
     */
71
    public static function newInstance(array $items = [], Resolver $resolver = null)
72
    {
73
        return new static($items, $resolver);
74
    }
75
76
    /**
77
     * This merges in a testing configuration. Any instance from this point will use that configuration.
78
     *
79
     * @param array $items
80
     * @param Resolver|null $resolver
81
     *
82
     * @return Config
83
     */
84
    public static function testInstance(array $items = [], Resolver $resolver = null)
85
    {
86
        if (static::$test_mode === false && !is_null(static::$instance)) {
87
            static::$instance = null;
88
        }
89
90
        static::$test_mode = true;
91
92
        return static::instance($items, $resolver);
93
    }
94
95
    /**
96
     * Same as endTest, but also returns a fresh instance.
97
     *
98
     * @param array $items
99
     * @param Resolver|null $resolver
100
     *
101
     * @return Config
102
     */
103
    public static function killTestInstance(array $items = [], Resolver $resolver = null)
104
    {
105
        (new static())->endTest();
106
107
        return static::instance($items, $resolver);
108
    }
109
110
    /**
111
     * This removes the testing flag and allow the normal configuration to return. This must be called to have
112
     * the library behave normally when testing.
113
     *
114
     * @return Config
115
     */
116
    public function endTest()
117
    {
118
        static::$test_mode = false;
119
        static::$instance = null;
120
121
        return $this;
122
    }
123
124
    /**
125
     * Returns the Resolver if no class is specified otherwise it attempts to resolve the given class.
126
     *
127
     * @param string|null $class
128
     * @param mixed ...$parameters
129
     *
130
     * @return Resolver|mixed|null
131
     */
132
    public function __invoke(string $class = null, ...$parameters)
133
    {
134
        if (is_null($class)) {
135
            return $this->resolve();
136
        }
137
138
        if ($this->hasResolvable($class)) {
139
            return $this->resolveFromStack($class);
140
        }
141
142
        if (array_key_exists($class, $this->apiObjects())) {
143
            return $this->resolve()->make($this->getApiObject($class), ...$parameters);
144
        }
145
146
        if (array_key_exists($class, $this->com())) {
147
            return $this->resolve()->make($this->com()[$class], ...$parameters);
148
        }
149
150
        throw UnresolvableClassException::default($class);
151
    }
152
153
    /**
154
     * @return Resolver
155
     */
156
    public function resolve()
157
    {
158
        return $this->resolver;
159
    }
160
161
    /**
162
     * Returns the current resolve stack.
163
     *
164
     * @return array
165
     */
166
    public function resolveStack()
167
    {
168
        return $this->resolve_stack;
169
    }
170
171
    /**
172
     * This attempts to get a resolvable item from the stack. Items on the stack are FIFO (First In First Out).
173
     * This is only ever utilized if using the Config classes' __invoke capability.
174
     *
175
     * @param $abstract
176
     *
177
     * @return mixed|null
178
     */
179
    public function resolveFromStack($abstract)
180
    {
181
        foreach ($this->resolve_stack as $key => $resolvable) {
182
            if (array_key_exists($abstract, $resolvable)) {
183
                $result = $resolvable[$abstract];
184
                unset($this->resolve_stack[$key]);
185
186
                return $result;
187
            }
188
        }
189
190
        return null;
191
    }
192
193
    /**
194
     * Add new resolvable to the end of the stack.
195
     *
196
     * @param $abstract
197
     * @param $concrete
198
     *
199
     * @return Config
200
     */
201
    public function addResolvable($abstract, $concrete)
202
    {
203
        $this->resolve_stack[] = [$abstract => $concrete];
204
205
        return $this;
206
    }
207
208
    /**
209
     * Check stack for resolvable. There may be a chance for caching pointers for resolvable abstracts.
210
     *
211
     * @param $abstract
212
     *
213
     * @return bool
214
     */
215
    public function hasResolvable($abstract): bool
216
    {
217
        foreach ($this->resolve_stack as $key => $resolvable) {
218
            if (array_key_exists($abstract, $resolvable)) {
219
                return true;
220
            }
221
        }
222
223
        return false;
224
    }
225
226
    /**
227
     * @return array
228
     */
229
    public function apiObjects()
230
    {
231
        return $this->get('wmi.api_objects', []);
232
    }
233
234
    /**
235
     * @param string $abstract_class
236
     * @param string|callable|object $concrete_class
237
     *
238
     * @return Config
239
     */
240
    public function addApiObject(string $abstract_class, $concrete_class)
241
    {
242
        $this->set("wmi.api_objects.{$abstract_class}", $concrete_class);
243
244
        return $this;
245
    }
246
247
    /**
248
     * @param string $class
249
     *
250
     * @return string
251
     */
252
    public function getApiObject(string $class)
253
    {
254
        return $this->get("wmi.api_objects.{$class}");
255
    }
256
257
    /**
258
     * @return array
259
     */
260
    public function com()
261
    {
262
        return $this->get('com', []);
263
    }
264
265
    /**
266
     * @param string $abstract_class
267
     * @param string|callable|object $concrete_class
268
     *
269
     * @return Config
270
     */
271
    public function addComObject(string $abstract_class, $concrete_class)
272
    {
273
        $this->set("com.{$abstract_class}", $concrete_class);
274
275
        return $this;
276
    }
277
278
    /**
279
     * @return EventProvider
280
     */
281
    public function eventProvider()
282
    {
283
        return $this->getProvider('event');
284
    }
285
286
    /**
287
     * @return EventHistoryProvider
288
     */
289
    public function eventHistoryProvider()
290
    {
291
        return $this->getProvider('event_history');
292
    }
293
294
    /**
295
     * @return CommandBus
296
     */
297
    public function commandBus()
298
    {
299
        return $this->getProvider('bus');
300
    }
301
302
    /**
303
     * Return an already registered provider or instantiate it from configuration when $default is null.
304
     *
305
     * @param string $alias
306
     * @param null|mixed $default
307
     *
308
     * @return mixed
309
     */
310
    public function getProvider($alias, $default = null)
311
    {
312
        $default = $default ?? function () use ($alias) {
313
            return $this->registerProvider($alias);
314
        };
315
316
        return $this->get("providers.registered.{$alias}", $default);
317
    }
318
319
    public function getCacheDriver($provider = null)
320
    {
321
        $provider = is_null($provider) ? $provider : "{$provider}.";
322
323
        $driver = $this->get("{$provider}cache.driver");
324
325
        return new $driver($this);
326
    }
327
328
    public function shouldTrackEvents()
329
    {
330
        return $this->get('event.track', false);
331
    }
332
333
    public function trackEvents()
334
    {
335
        $this->set('event.track', true);
336
337
        return $this;
338
    }
339
340
    public function doNotTrackEvents()
341
    {
342
        $this->set('event.track', false);
343
344
        return $this;
345
    }
346
347
    /**
348
     * @return string
349
     */
350
    public function getComClass()
351
    {
352
        return $this->get('com.com_class');
353
    }
354
355
    /**
356
     * @return string
357
     */
358
    public function getVariantClass()
359
    {
360
        return $this->get('com.variant_class');
361
    }
362
363
    /**
364
     * @return string
365
     */
366
    public function getComVariantWrapper()
367
    {
368
        return $this->get('com.' . ComVariantWrapper::class);
369
    }
370
371
    /**
372
     * @return string
373
     */
374
    public function getComWrapper()
375
    {
376
        return $this->get('com.' . ComWrapper::class);
377
    }
378
379
    /**
380
     * @return string
381
     */
382
    public function getVariantWrapper()
383
    {
384
        return $this->get('com.' . VariantWrapper::class);
385
    }
386
387
    /**
388
     * @return Connections
389
     */
390
    public function connections()
391
    {
392
        return $this->get('wmi.connections.servers');
393
    }
394
395
    /**
396
     * @param string $name
397
     *
398
     * @return ComConnection|null
399
     */
400
    public function getConnection(string $name = null)
401
    {
402
        if ($name === 'default' || is_null($name)) {
403
            $name = $this->get('wmi.connections.default');
404
        }
405
406
        return $this->connections()->get($name);
407
    }
408
409
    /**
410
     * @param string        $name
411
     * @param ComConnection $connection
412
     *
413
     * @return Config
414
     */
415
    public function addConnection(string $name, ComConnection $connection): self
416
    {
417
        $this->connections()->set($name, $connection);
418
419
        return $this;
420
    }
421
422
    public function getDefaultConnection()
423
    {
424
        return $this->getConnection($this->getDefaultConnectionName());
425
    }
426
427
    public function getDefaultConnectionName()
428
    {
429
        return $this->get('wmi.connections.default');
430
    }
431
432
    public function setDefaultConnection(string $name)
433
    {
434
        if (!$this->getConnection($name)) {
435
            throw InvalidConnectionException::new($name);
436
        }
437
438
        return $this->set('wmi.connections.default', $name);
439
    }
440
441
    public function registerProvider($provider_alias, $instance = null)
442
    {
443
        if (!is_object($instance) && ($instance = $this->getProvider($provider_alias, false)) === false) {
444
            $instance = $this->get("providers.bootstrap.{$provider_alias}");
445
            $instance = new $instance($this);
446
        }
447
448
        $this->set("providers.registered.{$provider_alias}", $instance);
449
450
        return $instance;
451
    }
452
453
    protected function boot(array $config)
454
    {
455
        if (static::$test_mode) {
456
            $this->merge(include(__DIR__ . '/../config/testing.php'));
457
        }
458
459
        if (!empty($config)) {
460
            $this->merge($config);
461
        }
462
463
        $this->merge(include(__DIR__ . '/../config/bootstrap.php'))
464
            ->registerProviders()
465
            ->bootConnections();
466
    }
467
468
    protected function registerProviders()
469
    {
470
        array_map(function ($alias) {
471
            $this->registerProvider($alias);
472
        }, array_keys($this->get('providers.bootstrap', [])));
473
474
        return $this;
475
    }
476
477
    protected function bootConnections()
478
    {
479
        Arr::set($this->container, 'wmi.connections.servers', new Connections($this));
480
481
        return $this;
482
    }
483
}
484