Container::resolve()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 2
dl 0
loc 25
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Resta\Container;
4
5
use DI\NotFoundException;
6
use DI\DependencyException;
7
use Resta\Console\ConsoleBindings;
8
use Resta\Contracts\ContainerContracts;
9
use Resta\Contracts\ApplicationContracts;
10
use Resta\Exception\FileNotFoundException;
11
12
class Container implements ContainerContracts,\ArrayAccess
13
{
14
    /**
15
     * @var bool
16
     */
17
    public $singleton = false;
18
19
    /**
20
     * @var
21
     */
22
    public $kernel;
23
24
    /**
25
     * @var array  $instance
26
     */
27
    private static $instance = [];
28
29
    /**
30
     * @var array $instances
31
     */
32
    private $instances = [];
33
34
    /**
35
     * @var array $bindParams
36
     */
37
    private static $bindParams = [];
38
39
    /**
40
     * @var
41
     */
42
    protected $unregister;
43
44
    /**
45
     * @var array
46
     */
47
    protected $values = [];
48
49
    /**
50
     * @param $make
51
     * @param array $bind
52
     * @return array
53
     */
54
    public function applicationProviderBinding($make,$bind=array())
55
    {
56
        //service container is an automatic application provider
57
        //that we can bind to the special class di in the dependency condition.
58
        //This method is automatically added to the classes resolved by the entire make bind method.
59
        return array_merge($bind,['app'=>$make]);
60
    }
61
62
    /**
63
     * @param null $object
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $object is correct as it would always require null to be passed?
Loading history...
64
     * @param null $callback
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $callback is correct as it would always require null to be passed?
Loading history...
65
     * @param null|string $alias
66
     * @return mixed
67
     *
68
     * @throws DependencyException
69
     * @throws NotFoundException
70
     */
71
    public function bind($object=null,$callback=null,$alias=null)
72
    {
73
        if(!is_null($alias)){
74
            $object = $alias;
75
        }
76
77
        //If the bind method does not have parameters object and callback, the value is directly assigned to the kernel object.
78
        //Otherwise, when the bind object and callback are sent, the closure class inherits
79
        //the applicationProvider object and the resolve method is called
80
        return $this->make($object,$callback,'container');
0 ignored issues
show
Bug introduced by
It seems like $object can also be of type string; however, parameter $object of Resta\Container\Container::make() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

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

80
        return $this->make(/** @scrutinizer ignore-type */ $object,$callback,'container');
Loading history...
Bug introduced by
'container' of type string is incompatible with the type boolean expected by parameter $container of Resta\Container\Container::make(). ( Ignorable by Annotation )

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

80
        return $this->make($object,$callback,/** @scrutinizer ignore-type */ 'container');
Loading history...
81
    }
82
83
    /**
84
     * @param $object
85
     * @param $callback
86
     * @param bool $sync
87
     * @return mixed
88
     *
89
     * @throws DependencyException
90
     * @throws NotFoundException
91
     */
92
    private function build($object,$callback,$sync=false)
93
    {
94
        //If the console object returns true,
95
        //we do not cancel binding operations
96
        //We are getting what applies to console with consoleKernelObject.
97
        if($sync===false) return $this->consoleKernelObjectChecker($object,$callback);
98
99
        //the value corresponding to the bind value for the global object is assigned and
100
        //the resolve method is called for the dependency injection.
101
        $this->kernelAssigner()->setKernelObject($object,$callback);
102
103
        //return kernel object
104
        return $this->kernel();
105
    }
106
107
    /**
108
     * @param $object
109
     * @param bool $container
110
     * @param $callback
111
     * @return mixed
112
     *
113
     * @throws DependencyException
114
     * @throws NotFoundException
115
     */
116
    private function consoleKernelObject($object,$callback,$container=false)
117
    {
118
        //we use the console bindings class to specify the classes to be preloaded in the console application.
119
        //Thus, classes that can not be bound with http are called without closure in global loaders directory.
120
        $this->resolve(ConsoleBindings::class)->console($object,$callback,$container);
121
122
        //The console application must always return the kernel method.
123
        return $this->kernel();
124
    }
125
126
    /**
127
     * @param $object
128
     * @param $callback
129
     * @param bool $container
130
     * @return mixed
131
     *
132
     * @throws DependencyException
133
     * @throws NotFoundException
134
     */
135
    private function consoleKernelObjectChecker($object,$callback,$container=false)
136
    {
137
        //we check whether the callback value is a callable function.
138
        $isCallableForCallback = is_callable($callback);
139
140
        //If the console object returns true,
141
        //we do not cancel binding operations
142
        //We are getting what applies to console with consoleKernelObject.
143
        if($this->console() AND $isCallableForCallback) return $this->consoleKernelObject($object,$callback,$container);
0 ignored issues
show
Bug introduced by
The method console() does not exist on Resta\Container\Container. Did you maybe mean consoleKernelObject()? ( Ignorable by Annotation )

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

143
        if($this->/** @scrutinizer ignore-call */ console() AND $isCallableForCallback) return $this->consoleKernelObject($object,$callback,$container);

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...
144
145
        //If the application is not a console operation, we re-bind to existing methods synchronously.
146
        return ($container) ? $this->containerBuild($object,$callback,true) : $this->build($object,$callback,true);
147
    }
148
149
    /**
150
     * @param $object
151
     * @param $callback
152
     *
153
     * @throws DependencyException
154
     * @throws NotFoundException
155
     */
156
    private function consoleShared($object,$callback)
157
    {
158
        //The console share is evaluated as a true variable to be assigned as the 3rd parameter in the classes to be bound.
159
        //The work to be done here is to bind the classes to be included in the console share privately.
160
        $this->kernelAssigner()->consoleShared($object,$callback);
161
    }
162
163
    /**
164
     * @param $object
165
     * @param $callback
166
     * @param bool $sync
167
     * @return mixed
168
     *
169
     * @throws DependencyException
170
     * @throws NotFoundException
171
     */
172
    public function containerBuild($object,$callback,$sync=false)
173
    {
174
        //If the console object returns true,
175
        //we do not cancel binding operations
176
        //We are getting what applies to console with consoleKernelObject.
177
        if($sync===false) return $this->consoleKernelObjectChecker($object,$callback,true);
178
179
        //Since the objects that come to the build method are objects from the container method,
180
        //we need to automatically create a kernel object named serviceContainer in this method.
181
        $this->serviceContainerObject();
182
183
        //the value corresponding to the bind value for the global object is assigned and
184
        //the resolve method is called for the dependency method.
185
        $this->kernelAssigner()->setKernelObject($object,$callback,'serviceContainer');
186
187
        //return kernel object
188
        return $this->kernel();
189
    }
190
191
    /**
192
     * @param $class
193
     * @param $bind
194
     */
195
    private function contextualBindCleaner($class,$bind)
196
    {
197
        //the context bind objects are checked again and the bind sequence submitted by
198
        //the user is checked and forced to re-instantiate the object.
199
        if(isset(self::$instance[$class]) && self::$bindParams[$class]!==$bind){
200
            unset(self::$instance[$class]);
201
            unset(self::$bindParams[$class]);
202
        }
203
    }
204
205
    /**
206
     * get abstract data in container
207
     *
208
     * @param $abstract
209
     * @return mixed|ApplicationContracts|null
210
     *
211
     * @throws FileNotFoundException
212
     */
213
    public function get($abstract)
214
    {
215
        //get instance container
216
        $container = app();
217
218
        $containerClosureResolver = [];
219
220
        // the has method can have a dotted string value so
221
        // we need to be able to control the string or array within the container.
222
        foreach (explode(".",$abstract) as $item){
223
            if(isset($container[$item])){
224
                $container = $container[$item];
225
            }
226
            else{
227
                $containerClosureResolver[] = $item;
228
            }
229
        }
230
231
        if(count($containerClosureResolver)>0){
232
            $container = ContainerClosureResolver::get(implode('.',$containerClosureResolver));
233
        }
234
235
        return $container;
236
    }
237
238
    /**
239
     * checks if the container entity is available
240
     *
241
     * @param $abstract
242
     * @return bool|mixed
243
     *
244
     * @throws FileNotFoundException
245
     */
246
    public function has($abstract)
247
    {
248
        $bools = [];
249
250
        //get instance container
251
        $container = $this;
252
253
        $containerClosureResolver = [];
254
255
        // the has method can have a dotted string value so
256
        // we need to be able to control the string or array within the container.
257
        foreach (explode(".",$abstract) as $item){
258
259
            // this blog will work
260
            // if the data in the container loop points to an array.
261
            if(!is_array($container)){
262
263
                // we are querying the value of
264
                // the items corresponding to the dotted value in the container.
265
                // the control result is transferred to the bools array.
266
                $container = $container[$item];
267
268
                if(is_null($container)){
269
                    $containerClosureResolver[] = $item;
270
                    $container = ContainerClosureResolver::get(implode('.',$containerClosureResolver));
271
272
                    if(is_callable($container)){
273
                        $bools = [];
274
                        $container = true;
275
                    }
276
                }
277
278
                $bools[] = is_null($container) ? false : true;
279
            }
280
            else{
281
                // if the container array corresponds to a string,
282
                // the bools array is filled with the isset control directly.
283
                $bools[] = isset($container[$item]) ? true : false;
284
            }
285
        }
286
287
        // the method returns false if the bools sequence is false,
288
        // otherwise it will return true.
289
        return in_array(false,$bools) ? false : true;
290
    }
291
292
    /**
293
     * Register an existing instance as shared in the container.
294
     *
295
     * @param  string  $abstract
296
     * @param  mixed   $instance
297
     * @return mixed
298
     */
299
    public function instance($abstract, $instance)
300
    {
301
        // we'll check to determine if this type has been bound before, and if it has
302
        // we will fire the rebound callbacks registered with the container and it
303
        // can be updated with consuming classes that have gotten resolved here.
304
        $this->instances[$abstract] = $instance;
305
    }
306
307
    /**
308
     * @return mixed
309
     */
310
    public function kernel()
311
    {
312
        //The kernel object system is the container backbone.
313
        //Binding binding and container loads are done with
314
        //the help of this object and distributed to the system.
315
        return $this->kernel;
316
    }
317
318
    /**
319
     * @return mixed
320
     *
321
     * @throws DependencyException
322
     * @throws NotFoundException
323
     */
324
    public function kernelAssigner()
325
    {
326
        //We will use the kernelAssigner class to resolve the singleton object state.
327
        return $this->resolve(ContainerKernelAssigner::class);
328
    }
329
330
    /**
331
     * @param null $object
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $object is correct as it would always require null to be passed?
Loading history...
332
     * @param null $callback
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $callback is correct as it would always require null to be passed?
Loading history...
333
     * @param bool $container
334
     * @return mixed
335
     *
336
     * @throws DependencyException
337
     * @throws NotFoundException
338
     */
339
    public function make($object=null,$callback=null,$container=false)
340
    {
341
        //we check whether the boolean value of the singleton variable used
342
        //for booting does not reset every time the object variable to be assigned to the kernel variable is true
343
        $this->singleton();
344
345
        //The console share is evaluated as a true variable to be assigned as the 3rd parameter in the classes to be bound.
346
        //The work to be done here is to bind the classes to be included in the console share privately.
347
        if($container){
348
            $this->consoleShared($object,$callback);
349
        }
350
351
        //If the third parameter passed to the bind method carries a container value,
352
        //then you will not be able to fire the build method instead of the make method.
353
        $makeBuild = ($container==="container") ? 'containerBuild' : 'build';
0 ignored issues
show
introduced by
The condition $container === 'container' is always false.
Loading history...
354
355
        //If the bind method does not have parameters object and callback, the value is directly assigned to the kernel object.
356
        //Otherwise, when the bind object and callback are sent, the closure class inherits
357
        //the applicationProvider object and the resolve method is called
358
        return ($object===null) ? $this->kernel() : $this->{$makeBuild}($object,$callback);
0 ignored issues
show
introduced by
The condition $object === null is always true.
Loading history...
359
    }
360
361
    /**
362
     * @param $offset
363
     * @return bool
364
     */
365
    public function offsetExists($offset) {
366
        return isset($this->container[$offset]);
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist on Resta\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
367
    }
368
369
    /**
370
     * @param mixed $offset
371
     * @return mixed
372
     *
373
     * @throws DependencyException
374
     * @throws NotFoundException
375
     */
376
    public function offsetGet($offset) {
377
378
        return $this->resolve($this->instances['containerInstanceResolve'],[
379
            'instances' => $this->instances
380
        ])->{$offset}();
381
    }
382
383
    /**
384
     * @param $offset
385
     * @param $value
386
     */
387
    public function offsetSet($offset, $value) {}
388
389
    /**
390
     * @param $offset
391
     */
392
    public function offsetUnset($offset) {
393
        unset($this->container[$offset]);
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist on Resta\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
394
    }
395
396
    /**
397
     * @param $key
398
     * @param $object
399
     * @param null $concrete
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $concrete is correct as it would always require null to be passed?
Loading history...
400
     * @return bool|mixed
401
     */
402
    public function register($key,$object,$concrete=null)
403
    {
404
        // we assign the values ​​required
405
        // for register to the global value variable.
406
        $this->values['key']        = $key;
407
        $this->values['object']     = $object;
408
        $this->values['concrete']   = $concrete;
409
410
        // If there is an instance of the application class,
411
        // the register method is saved both in this example and in the global.
412
        if(defined('appInstance')){
413
414
            // where we will assign both the global instance
415
            // and the registered application object.
416
            $this->setAppInstance($this->singleton());
417
            $this->setAppInstance(core());
418
419
            return false;
420
        }
421
422
        // we are just doing global instance here.
423
        $this->setAppInstance($this->singleton());
424
    }
425
426
    /**
427
     * @param $instance
428
     * @param bool $withConcrete
429
     * @return bool
430
     */
431
    private function registerProcess($instance,$withConcrete=false)
432
    {
433
        // values recorded without concrete.
434
        // or values deleted
435
        if(false===$withConcrete){
436
437
            //values registered without concrete
438
            $instance->{$this->values['key']}=$this->values['object'];
439
            return false;
440
        }
441
442
        //values registered with concrete
443
        $instance->{$this->values['key']}[$this->values['object']]=$this->values['concrete'];
444
    }
445
446
    /**
447
     * @param $class
448
     * @param array $bind
449
     * @return mixed
450
     *
451
     * @throws DependencyException
452
     * @throws NotFoundException
453
     */
454
    public function resolve($class,$bind=array())
455
    {
456
        //the context bind objects are checked again and the bind sequence submitted by
457
        //the user is checked and forced to re-instantiate the object.
458
        $this->contextualBindCleaner($class,$bind);
459
460
        //We do an instance check to get the static instance values of
461
        //the classes to be resolved with the make bind method.
462
        if(!isset(self::$instance[$class])){
463
464
            //bind params object
465
            self::$bindParams[$class] = $bind;
466
467
            //By singleton checking, we solve the dependency injection of the given class.
468
            //Thus, each class can be called together with its dependency.
469
            self::$instance[$class] = DIContainerManager::make($class,$this->applicationProviderBinding($this,self::$bindParams[$class]));
470
            $this->singleton()->resolved[$class] = self::$instance[$class];
471
472
            //return resolve class
473
            return self::$instance[$class];
474
        }
475
476
        //if the class to be resolved has already been loaded,
477
        //we get the instance value that was saved to get the recurring instance.
478
        return self::$instance[$class];
479
    }
480
481
    /**
482
     * get resolved class
483
     *
484
     * @param $class
485
     * @return mixed
486
     */
487
    public function resolved($class){
488
489
        if(isset($this['resolved'][$class])){
490
            return $this['resolved'][$class];
491
        }
492
493
        exception()->runtime('The class named '.$class.' was not solved before.');
494
    }
495
496
    /**
497
     * @param null $object
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $object is correct as it would always require null to be passed?
Loading history...
498
     * @param null $callback
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $callback is correct as it would always require null to be passed?
Loading history...
499
     * @return mixed
500
     *
501
     * @throws DependencyException
502
     * @throws NotFoundException
503
     */
504
    public function share($object=null,$callback=null)
505
    {
506
        //we check whether the boolean value of the singleton variable used
507
        //for booting does not reset every time the object variable to be assigned to the kernel variable is true
508
        $this->singleton();
509
510
        //The console share is evaluated as a true variable to be assigned as the 3rd parameter in the classes to be bound.
511
        //The work to be done here is to bind the classes to be included in the console share privately.
512
        $this->consoleShared($object,$callback);
513
514
        //If the bind method does not have parameters object and callback, the value is directly assigned to the kernel object.
515
        //Otherwise, when the bind object and callback are sent, the closure class inherits
516
        //the applicationProvider object and the resolve method is called
517
        return ($object===null) ? $this->kernel() : $this->build($object,$callback);
0 ignored issues
show
introduced by
The condition $object === null is always true.
Loading history...
518
519
    }
520
521
    /**
522
     * @throws DependencyException
523
     * @throws NotFoundException
524
     */
525
    private function serviceContainerObject()
526
    {
527
        //Since the objects that come to the build method are objects from the container method,
528
        //we need to automatically create a kernel object named serviceContainer in this method.
529
        $this->kernelAssigner()->container();
530
    }
531
532
    /**
533
     * @method singleton
534
     */
535
    public function singleton()
536
    {
537
        if($this->singleton===false){
538
539
            //after first initializing, the singleton variable is set to true,
540
            //and subsequent incoming classes can inherit the loaded object.
541
            $this->singleton = true;
542
            $this->kernel = \application::kernelBindObject();
0 ignored issues
show
Bug introduced by
The type application 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...
543
        }
544
545
        //kernel object taken over
546
        return $this->kernel();
547
    }
548
549
    /**
550
     * @param $instance
551
     * @return bool
552
     */
553
    private function setAppInstance($instance)
554
    {
555
        // for application instance
556
        // if the values ​​to be saved are to be saved without the concrete,
557
        // if it is an array.
558
        if($this->values['concrete']===null) {
559
560
            // Without concrete,
561
            // the saved value will be saved
562
            // if the it does not exist in application instance.
563
            if(!isset($instance->{$this->values['key']})) {
564
                $this->registerProcess($instance);
565
            }
566
            return false;
567
        }
568
569
        // We send concrete values to be recorded with concrete as true.
570
        // these values will be recorded as a array.
571
        $this->registerProcess($instance,true);
572
    }
573
574
    /**
575
     * @param string $key
576
     * @param null|string $object
577
     * @return mixed
578
     */
579
    public function terminate($key,$object=null)
580
    {
581
        // object null is
582
        // sent to just terminate a key.
583
        if($object===null){
584
            unset(core()->{$key});
585
            unset($this->singleton()->{$key});
586
            return false;
587
        }
588
589
        // It is used to delete
590
        // both key and sequence members.
591
        unset($this->singleton()->{$key}[$object]);
592
        unset(core()->{$key}[$object]);
593
    }
594
595
    /**
596
     * Dynamically access container services.
597
     *
598
     * @param  string  $key
599
     * @return mixed
600
     */
601
    public function __get($key)
602
    {
603
        return $this[$key];
604
    }
605
    /**
606
     * Dynamically set container services.
607
     *
608
     * @param  string  $key
609
     * @param  mixed   $value
610
     * @return void
611
     */
612
    public function __set($key, $value)
613
    {
614
        $this[$key] = $value;
615
    }
616
}