Client   F
last analyzed

Complexity

Total Complexity 105

Size/Duplication

Total Lines 652
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 179
dl 0
loc 652
rs 2
c 0
b 0
f 0
wmc 105

21 Methods

Rating   Name   Duplication   Size   Complexity  
B generatorMethod() 0 42 9
A setClientName() 0 11 6
A handle() 0 50 3
A __construct() 0 17 2
A containerRegister() 0 5 1
A expectedInputs() 0 30 6
A except() 0 14 2
A registerRequestInputs() 0 13 1
C autoValidate() 0 39 14
B groupsProcess() 0 28 8
A setClientObjects() 0 23 4
A initClient() 0 9 2
A requestExcept() 0 6 5
A checkProperties() 0 6 4
C capsule() 0 37 12
A generatorManager() 0 16 5
A unity() 0 16 5
A checkHttpMethod() 0 16 3
A getClientName() 0 3 1
B setRequestInputs() 0 24 8
A validation() 0 8 4

How to fix   Complexity   

Complex Class

Complex classes like Client often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Resta\Client;
4
5
use Resta\Support\Utils;
6
use Resta\Contracts\HandleContracts;
7
use Resta\Support\ReflectionProcess;
8
use ReflectionException as ReflectionExceptionAlias;
9
10
/**
11
 * @property $this auto_capsule
12
 * @property $this http
13
 * @property $this autoObjectValidate
14
 * @property $this requestExcept
15
 * @property $this expected
16
 * @property  $this groups
17
 * @property  $this unity
18
 */
19
class Client extends ClientAbstract implements HandleContracts
20
{
21
    /**
22
     * @var array
23
     */
24
    protected $capsule = [];
25
26
    /**
27
     * @var null|string
28
     */
29
    protected $clientName;
30
31
    /**
32
     * @var array
33
     */
34
    protected $except = [];
35
36
    /**
37
     * @var null|string
38
     */
39
    protected $method;
40
41
    /**
42
     * @var ReflectionProcess
43
     */
44
    protected $reflection;
45
46
    /**
47
     * @var null|ClientHttpManager
48
     */
49
    protected $requestHttp;
50
51
    /**
52
     * @var null|array
53
     */
54
    protected $clientData;
55
56
    /**
57
     * @var array
58
     */
59
    protected $requestData = [];
60
61
    /**
62
     * @var array
63
     */
64
    protected $generatorList = [];
65
66
    /**
67
     * Request constructor.
68
     *
69
     * @param null|array $clientData
70
     *
71
     * @throws ReflectionExceptionAlias
72
     */
73
    public function __construct($clientData=null)
74
    {
75
        //reflection process
76
        $this->reflection = app()['reflection']($this);
77
78
79
        //set clientName for client
80
        $this->setClientName();
81
82
        //get http method via request http manager class
83
        $this->requestHttp = app()->resolve(ClientHttpManager::class,['client'=>$this]);
84
85
        //get request client data
86
        $this->clientData = ($clientData===null) ? $this->requestHttp->resolve() : $clientData;
87
88
        //handle request
89
        $this->handle();
90
    }
91
92
    /**
93
     * auto validate
94
     *
95
     * @param $validate
96
     */
97
    private function autoValidate($validate)
98
    {
99
        //we get the values ​​to auto-validate.
100
        foreach ($this->{$validate} as $object=>$datas){
101
102
            if(false===Utils::isNamespaceExists($object)){
103
                return;
104
            }
105
106
            // the auto-validate value must necessarily represent a class.
107
            // otherwise auto-validate is not used.
108
            $getObjectInstance = app()->resolve($object);
109
110
            // we get the index values,
111
            // which are called methods of the auto-validate value that represents the class.
112
            foreach ($datas as $dataKey=>$data){
113
114
                // if the methods of the auto-validate class resolved by the container resolve method apply,
115
                // the process of auto-validate automatic implementation will be completed.
116
                if(method_exists($getObjectInstance,$dataKey) && is_array($data)){
117
                    foreach ($data as $dataItem){
118
                        if(isset($this->origin[$dataItem])){
119
                            $getObjectInstance->{$dataKey}($this->origin[$dataItem],$this,$dataItem);
120
                        }
121
122
                        if(method_exists($this,$afterMethod = 'after'.ucfirst($dataKey))){
123
                            $this->{$afterMethod}($this,$dataItem);
124
                        }
125
                    }
126
                }
127
128
                // if the methods of the auto-validate class resolved by the container resolve method apply,
129
                // the process of auto-validate automatic implementation will be completed.
130
                if(is_numeric($dataKey) && method_exists($getObjectInstance,$data) && isset($this->origin[$data])){
131
                    if(!is_array($this->origin[$data])){
132
                        $this->origin[$data] = array($this->origin[$data]);
133
                    }
134
                    foreach ($this->origin[$data] as $originData){
135
                        $getObjectInstance->{$data}($originData,$this,$data);
136
                    }
137
                }
138
            }
139
        }
140
    }
141
142
    /**
143
     * capsule inputs
144
     *
145
     * @return void|mixed
146
     */
147
    private function capsule()
148
    {
149
        //a process can be added to the capsule array using the method.
150
        if(method_exists($this,'capsuleMethod')){
151
            $this->capsule = array_merge($this->capsule,$this->capsuleMethod());
152
        }
153
154
        // expected method is executed.
155
        // this method is a must for http method values to be found in this property.
156
        if($this->checkProperties('capsule')){
157
158
            if(property_exists($this,'auto_capsule') && is_array($this->auto_capsule)){
0 ignored issues
show
introduced by
The condition is_array($this->auto_capsule) is always false.
Loading history...
159
                $this->capsule = array_merge($this->capsule,$this->auto_capsule);
160
            }
161
162
            if($this->checkProperties('groups')){
163
                $this->capsule = array_merge($this->capsule,$this->groups);
0 ignored issues
show
Bug introduced by
$this->groups of type Resta\Client\Client is incompatible with the type array expected by parameter $arrays of array_merge(). ( Ignorable by Annotation )

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

163
                $this->capsule = array_merge($this->capsule,/** @scrutinizer ignore-type */ $this->groups);
Loading history...
164
165
                foreach ($this->capsule as $item) {
166
                    $groupProcess = $this->groupsProcess($item,true);
167
                    if(is_array($groupProcess)){
168
                        $defaultInputs = $this->inputs;
169
                        $this->inputs = array_merge($this->inputs,$groupProcess);
170
                    }
171
                }
172
            }
173
174
            foreach($this->inputs as $input=>$value){
175
176
                if($this->checkProperties('capsule') && !in_array($input,$this->capsule)){
177
                    exception('clientCapsule',['key'=>$input])
178
                        ->overflow('The '.$input.' value cannot be sent.');
179
                }
180
            }
181
182
            if(isset($defaultInputs)){
183
                $this->inputs = $defaultInputs;
184
            }
185
        }
186
    }
187
188
    /**
189
     * check http method
190
     *
191
     * @return void|mixed
192
     */
193
    private function checkHttpMethod()
194
    {
195
        //get http method
196
        $method = $this->requestHttp->getMethod();
0 ignored issues
show
Bug introduced by
The method getMethod() does not exist on null. ( Ignorable by Annotation )

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

196
        /** @scrutinizer ignore-call */ $method = $this->requestHttp->getMethod();

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...
197
198
        // Determines which HTTP method
199
        // the request object will be exposed to.
200
        if($this->checkProperties('http')){
201
202
            // if the current http method does not exist
203
            // in the http object, the exception will be thrown.
204
            if(!in_array($method,$this->http)){
0 ignored issues
show
Bug introduced by
$this->http of type Resta\Client\Client is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

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

204
            if(!in_array($method,/** @scrutinizer ignore-type */ $this->http)){
Loading history...
205
206
                //exception batMethodCall
207
                exception()->badMethodCall(
208
                    'Invalid http method process for '.basename($this).'.That is accepted http methods ['.implode(",",$this->http).'] ');
0 ignored issues
show
Bug introduced by
$this of type Resta\Client\Client is incompatible with the type string expected by parameter $path of basename(). ( Ignorable by Annotation )

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

208
                    'Invalid http method process for '.basename(/** @scrutinizer ignore-type */ $this).'.That is accepted http methods ['.implode(",",$this->http).'] ');
Loading history...
Bug introduced by
$this->http of type Resta\Client\Client is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

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

208
                    'Invalid http method process for '.basename($this).'.That is accepted http methods ['.implode(",",/** @scrutinizer ignore-type */ $this->http).'] ');
Loading history...
209
            }
210
        }
211
    }
212
213
    /**
214
     * check properties
215
     *
216
     * @param $properties
217
     * @return bool
218
     */
219
    private function checkProperties($properties)
220
    {
221
        // from the properties of the object properties to
222
        // the existing variables, control the array and at least one element.
223
        return (property_exists($this,$properties)
224
            && is_array($this->{$properties}) && count($this->{$properties})) ? true : false;
225
    }
226
227
    /**
228
     * register container for request
229
     *
230
     * @return mixed|void
231
     */
232
    private function containerRegister()
233
    {
234
        // we are saving the expected values ​​for the request in container.
235
        // this record can be returned in exception information.
236
        app()->register('requestExpected',$this->expected);
237
    }
238
239
    /**
240
     * get request except
241
     *
242
     * @param $except
243
     * @return $this
244
     */
245
    public function except($except)
246
    {
247
        // the except parameter is a callable value.
248
        if(is_callable($except)){
249
            $call = call_user_func_array($except,[$this]);
250
            $except = $call;
251
        }
252
253
        // except with the except exceptions property
254
        // and then assigning them to the inputs property.
255
        $this->except = array_merge($this->except,$except);
256
        $this->inputs = array_diff_key($this->inputs,array_flip($this->except));
257
258
        return $this;
259
    }
260
261
    /**
262
     * expected inputs
263
     *
264
     * @return void|mixed
265
     */
266
    private function expectedInputs()
267
    {
268
        // expected method is executed.
269
        // this method is a must for http method values to be found in this property.
270
        if($this->checkProperties('expected')){
271
272
            // if the expected values are not found in the inputs array,
273
            // the exception will be thrown.
274
            foreach ($this->expected as $expected){
275
276
                $expectedValues = [];
277
278
                // mandatory expected data for each key can be separated by | operator.
279
                // this is evaluated as "or".
280
                foreach($expectedData = explode("|",$expected) as $inputs){
281
282
                    // we should do key control for group format.
283
                    // this process will allow us to perform key control for 2D array correctly.
284
                    $this->groupsProcess($inputs);
285
286
                    if(!isset($this->inputs[$inputs])){
287
                        $expectedValues[$inputs] = $inputs;
288
                    }
289
                }
290
291
                // if the expectedData and expectedValues ​​
292
                // array are numerically equal to the expected key, the exception is thrown.
293
                if(count($expectedData)===count($expectedValues)){
294
                    exception('clientExpected',['key'=>$expected])
295
                        ->unexpectedValue('You absolutely have to send the value '.implode(" or ",$expectedValues).' for request object');
296
                }
297
            }
298
        }
299
    }
300
301
    /**
302
     * generator manager
303
     *
304
     * @throws ReflectionExceptionAlias
305
     */
306
    private function generatorManager()
307
    {
308
        // check the presence of the generator object
309
        // and operate the generator over this object.
310
        if($this->checkProperties('auto_generators')){
311
            $generators = $this->getAutoGenerators();
312
        }
313
314
        // check the presence of the generator object
315
        // and operate the generator over this object.
316
        if($this->checkProperties('generators')){
317
            $generators = array_merge(isset($generators) ? $generators: [],$this->getGenerators());
0 ignored issues
show
Bug introduced by
It seems like $this->getGenerators() can also be of type Resta\Client\ClientAbstract; however, parameter $arrays of array_merge() does only seem to accept array, 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

317
            $generators = array_merge(isset($generators) ? $generators: [],/** @scrutinizer ignore-type */ $this->getGenerators());
Loading history...
318
        }
319
320
        if(isset($generators)){
321
            $this->generatorMethod($generators);
322
        }
323
    }
324
325
    /**
326
     * generator method
327
     *
328
     * @param $generators
329
     *
330
     * @throws ReflectionExceptionAlias
331
     */
332
    private function generatorMethod($generators)
333
    {
334
        //generator array object
335
        foreach ($generators as $generator){
336
337
            //generator method name
338
            $generatorMethodName = $generator.'Generator';
339
340
            // if the generator method is present,
341
            // the fake value is assigned.
342
            if(method_exists($this,$generatorMethodName)){
343
344
                //fake registration
345
                if(!isset($this->inputs[$generator])){
346
347
                    $generatorMethodNameResult = $this->{$generatorMethodName}();
348
349
                    if(!is_null($generatorMethodNameResult)){
350
                        $this->{$generator} = $calledGenerator = $generatorMethodNameResult;
351
                        $this->inputs[$generator] = $calledGenerator;
352
                        $this->generatorList[] = $generator;
353
                    }
354
                }
355
                else {
356
357
                    if($this->checkProperties('auto_generators_dont_overwrite')
358
                        && in_array($generator,$this->getAutoGeneratorsDontOverwrite())){
0 ignored issues
show
Bug introduced by
It seems like $this->getAutoGeneratorsDontOverwrite() can also be of type Resta\Client\ClientAbstract; however, parameter $haystack of in_array() does only seem to accept array, 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

358
                        && in_array($generator,/** @scrutinizer ignore-type */ $this->getAutoGeneratorsDontOverwrite())){
Loading history...
359
                        $this->{$generator} = $calledGenerator = $this->{$generatorMethodName}();
360
                        $this->inputs[$generator] = $calledGenerator;
361
                        $this->generatorList[] = $generator;
362
                    }
363
364
                    if($this->checkProperties('generators_dont_overwrite')
365
                        && in_array($generator,$this->getGeneratorsDontOverwrite())){
366
                        $this->{$generator} = $calledGenerator = $this->{$generatorMethodName}();
367
                        $this->inputs[$generator] = $calledGenerator;
368
                        $this->generatorList[] = $generator;
369
                    }
370
371
                }
372
373
                $this->registerRequestInputs($generator);
374
            }
375
        }
376
    }
377
378
    /**
379
     * get client name for request
380
     *
381
     * @return string
382
     */
383
    public function getClientName()
384
    {
385
        return $this->clientName;
386
    }
387
388
    /**
389
     * we should do key control for group format.
390
     * this process will allow us to perform key control for 2D array correctly.
391
     *
392
     * @param null $key
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $key is correct as it would always require null to be passed?
Loading history...
393
     * @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...
394
     * @return mixed|void
395
     */
396
    public function groupsProcess($key=null,$callback=null)
397
    {
398
        if(property_exists($this,'groups') && is_array($this->groups)){
0 ignored issues
show
introduced by
The condition is_array($this->groups) is always false.
Loading history...
399
400
            $clientObjects = $this->getClientObjects();
401
402
            foreach ($this->groups as $group){
403
404
                if(true === $callback){
405
                    if(isset($clientObjects['origin'][$key])){
406
                        return $clientObjects['origin'][$key];
407
                    }
408
                    return [];
409
                }
410
411
                if(isset($clientObjects['origin'][$group][$key])){
412
413
                    $this->{$key} = $clientObjects['origin'][$group][$key];
414
                    $this->inputs[$key] = $this->{$key};
415
416
                    if(is_callable($callback)){
417
                        call_user_func_array($callback,[$key]);
418
                    }
419
                }
420
            }
421
        }
422
423
        return [];
424
    }
425
426
    /**
427
     * request handle
428
     *
429
     * @return mixed|void
430
     *
431
     * @throws ReflectionExceptionAlias
432
     */
433
    public function handle()
434
    {
435
        //set container for request
436
        $this->containerRegister();
437
438
        //we record the values ​​
439
        //that coming with the post.
440
        $this->initClient();
441
442
        if(method_exists($this,'eventBefore')){
443
            $this->eventBefore();
444
        }
445
446
        // this method determines
447
        // how the request object will be requested,
448
        $this->checkHttpMethod();
449
450
        // get capsule as mandatory values
451
        $this->capsule();
452
453
        // if a fake method is defined and it is not in
454
        // the context of any key method when access is granted,
455
        // it can be filled with fake method.
456
        $this->generatorManager();
457
458
        // contrary to capsule method,
459
        // expected values must be in the key being sent.
460
        $this->expectedInputs();
461
462
        // contrary to unity method,
463
        // accepts only one of the elements in the array in the client data sent.
464
        $this->unity();
465
466
        // we update the input values ​​after
467
        // we receive and check the saved objects.
468
        $this->setClientObjects();
469
470
        // it passes all keys that are sent through
471
        // a validation method on the user side.
472
        $this->validation();
473
474
        // the values ​​specified in request except property
475
        // are subtracted from all input values.
476
        $this->requestExcept();
477
478
        if(app()->has('clientRequestInputs')){
479
            app()->terminate('clientRequestInputs');
480
        }
481
482
        app()->register('clientRequestInputs',$this->inputs);
483
    }
484
485
    /**
486
     * accepts only one of the elements in the array in the client data sent.
487
     *
488
     * @return void
489
     */
490
    private function unity()
491
    {
492
        // unity method is executed.
493
        if($this->checkProperties('unity')){
494
495
            $list = [];
496
497
            foreach ($this->unity as $unity){
498
                if(isset($this->inputs[$unity])){
499
                    $list[] = $unity;
500
                }
501
            }
502
503
            if(count($list)>1){
504
                exception('clientUnityExpected',['key'=>implode(',',$this->unity)])
0 ignored issues
show
Bug introduced by
$this->unity of type Resta\Client\Client is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

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

504
                exception('clientUnityExpected',['key'=>implode(',',/** @scrutinizer ignore-type */ $this->unity)])
Loading history...
505
                    ->invalidArgument('clientUnityExpected');
506
            }
507
        }
508
    }
509
510
    /**
511
     * get init client
512
     *
513
     * @return void
514
     */
515
    private function initClient()
516
    {
517
        // we use the http method to write
518
        // the values to the inputs and origin properties.
519
        foreach($this->clientData as $key=>$value){
520
521
            //inputs and origin properties
522
            $this->inputs[$key] = $value;
523
            $this->origin[$key] = $value;
524
        }
525
    }
526
527
    /**
528
     * the values ​​specified in request except property
529
     * are subtracted from all input values.
530
     *
531
     * @return mixed|void
532
     */
533
    private function requestExcept()
534
    {
535
        if(property_exists($this,'requestExcept') && is_array($this->requestExcept)){
0 ignored issues
show
introduced by
The condition is_array($this->requestExcept) is always false.
Loading history...
536
            foreach ($this->requestExcept as $item){
537
                if(isset($this->inputs[$item])){
538
                    unset($this->inputs[$item]);
539
                }
540
            }
541
        }
542
    }
543
544
    /**
545
     * set client name for client resolver
546
     *
547
     * @param null|string $clientName
548
     * @return void|mixed
549
     */
550
    public function setClientName($clientName=null)
551
    {
552
        if(!is_null($clientName) && is_string($clientName)){
553
            return $this->clientName = $clientName;
554
        }
555
556
        if(!is_null(Utils::trace(0)) && isset(Utils::trace(0)['object'])){
557
            $backTrace = Utils::trace(0)['object'];
558
559
            if(property_exists($backTrace,'clientName')){
560
                $this->clientName = $backTrace->clientName;
561
            }
562
        }
563
    }
564
565
    /**
566
     * set client objects
567
     *
568
     * @throws ReflectionExceptionAlias
569
     */
570
    private function setClientObjects()
571
    {
572
        $clientObjects = $this->getClientObjects();
573
574
        // we update the input values ​​after
575
        // we receive and check the saved objects.
576
        foreach ($clientObjects as $key=>$value){
577
578
            // we should do key control for group format.
579
            // this process will allow us to perform key control for 2D array correctly.
580
            $this->groupsProcess($key,function($key){
581
                $this->registerRequestInputs($key);
582
                unset($this->inputs[$key]);
583
            });
584
585
            if(!in_array($key,$this->generatorList) && isset($clientObjects['origin'][$key])){
586
587
                $this->{$key} = $clientObjects['origin'][$key];
588
                $this->inputs[$key] = $this->{$key};
589
590
                // the request update to be performed using
591
                // the method name to be used with the http method.
592
                $this->registerRequestInputs($key);
593
            }
594
        }
595
596
    }
597
598
    /**
599
     * register request inputs
600
     *
601
     * @param $key
602
     *
603
     * @throws ReflectionExceptionAlias
604
     */
605
    private function registerRequestInputs($key)
606
    {
607
        // the method name to be used with
608
        // the http method.
609
        $requestMethod = $this->requestHttp->getMethod().''.ucfirst($key);
610
611
        // the request update to be performed using
612
        // the method name to be used with the http method.
613
        $this->setRequestInputs($requestMethod,$key);
614
615
        // the request update to be performed using
616
        // the method name to be used without the http method.
617
        $this->setRequestInputs($key,$key);
618
    }
619
620
    /**
621
     * set request inputs
622
     *
623
     * @param $method
624
     * @param $key
625
     *
626
     * @throws ReflectionExceptionAlias
627
     */
628
    private function setRequestInputs($method,$key)
629
    {
630
        if(!in_array($key,$this->generatorList) && method_exists($this,$method) && $this->reflection->reflectionMethodParams($method)->isProtected){
631
632
            //check annotations for method
633
            $annotation = app()->resolve(ClientAnnotationManager::class,['request'=>$this]);
634
            $annotation->annotation($method,$key);
635
636
            if(isset($this->inputs[$key]) && is_array($this->inputs[$key])){
637
638
                $inputKeys = $this->inputs[$key];
639
640
                $this->inputs[$key] = [];
641
                foreach ($inputKeys as $ikey=>$input){
642
643
                    $this->{$key}[$ikey]        = $input;
644
                    $keyMethod                  = $this->{$method}();
645
                    $this->inputs[$key]         = $keyMethod;
646
                }
647
            }
648
            else{
649
                if(isset($this->inputs[$key])){
650
                    $keyMethod = $this->{$method}();
651
                    $this->inputs[$key] = $keyMethod;
652
                }
653
654
            }
655
        }
656
    }
657
658
    /**
659
     * validation for request
660
     *
661
     * @return void
662
     */
663
    private function validation()
664
    {
665
        // the auto object validate property is the property
666
        // where all of your request values ​​are automatically validated.
667
        /** @noinspection PhpParamsInspection */
668
        if(property_exists($this,'autoObjectValidate')
669
            && is_array($this->autoObjectValidate) && count($this->autoObjectValidate)){
0 ignored issues
show
introduced by
The condition is_array($this->autoObjectValidate) is always false.
Loading history...
670
            $this->autoValidate('autoObjectValidate');
671
        }
672
    }
673
}