Test Failed
Push — master ( 550bd1...54b313 )
by Php Easy Api
12:02
created

Client::validation()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 3
nc 2
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
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
        //set clientName for client
79
        $this->setClientName();
80
81
        //get http method via request http manager class
82
        $this->requestHttp = app()->resolve(ClientHttpManager::class,['client'=>$this]);
83
84
        //get request client data
85
        $this->clientData = ($clientData===null) ? $this->requestHttp->resolve() : $clientData;
86
87
        //handle request
88
        $this->handle();
89
    }
90
91
    /**
92
     * auto validate
93
     *
94
     * @param $validate
95
     */
96
    private function autoValidate($validate)
97
    {
98
        //we get the values ​​to auto-validate.
99
        foreach ($this->{$validate} as $object=>$datas){
100
101
            if(false===Utils::isNamespaceExists($object)){
102
                return;
103
            }
104
105
            // the auto-validate value must necessarily represent a class.
106
            // otherwise auto-validate is not used.
107
            $getObjectInstance = app()->resolve($object);
108
109
            // we get the index values,
110
            // which are called methods of the auto-validate value that represents the class.
111
            foreach ($datas as $dataKey=>$data){
112
113
                // if the methods of the auto-validate class resolved by the container resolve method apply,
114
                // the process of auto-validate automatic implementation will be completed.
115
                if(method_exists($getObjectInstance,$dataKey) && is_array($data)){
116
                    foreach ($data as $dataItem){
117
                        if(isset($this->origin[$dataItem])){
118
                            $getObjectInstance->{$dataKey}($this->origin[$dataItem],$this,$dataItem);
119
                        }
120
121
                        if(method_exists($this,$afterMethod = 'after'.ucfirst($dataKey))){
122
                            $this->{$afterMethod}($this,$dataItem);
123
                        }
124
                    }
125
                }
126
127
                // if the methods of the auto-validate class resolved by the container resolve method apply,
128
                // the process of auto-validate automatic implementation will be completed.
129
                if(is_numeric($dataKey) && method_exists($getObjectInstance,$data) && isset($this->origin[$data])){
130
                    if(!is_array($this->origin[$data])){
131
                        $this->origin[$data] = array($this->origin[$data]);
132
                    }
133
                    foreach ($this->origin[$data] as $originData){
134
                        $getObjectInstance->{$data}($originData,$this,$data);
135
                    }
136
                }
137
            }
138
        }
139
    }
140
141
    /**
142
     * capsule inputs
143
     *
144
     * @return void|mixed
145
     */
146
    private function capsule()
147
    {
148
        //a process can be added to the capsule array using the method.
149
        if(method_exists($this,'capsuleMethod')){
150
            $this->capsule = array_merge($this->capsule,$this->capsuleMethod());
151
        }
152
153
        // expected method is executed.
154
        // this method is a must for http method values to be found in this property.
155
        if($this->checkProperties('capsule')){
156
157
            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...
158
                $this->capsule = array_merge($this->capsule,$this->auto_capsule);
159
            }
160
161
            if($this->checkProperties('groups')){
162
                $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|null expected by parameter $array2 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

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

195
        /** @scrutinizer ignore-call */ 
196
        $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...
196
197
        // Determines which HTTP method
198
        // the request object will be exposed to.
199
        if($this->checkProperties('http')){
200
201
            // if the current http method does not exist
202
            // in the http object, the exception will be thrown.
203
            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

203
            if(!in_array($method,/** @scrutinizer ignore-type */ $this->http)){
Loading history...
204
205
                //exception batMethodCall
206
                exception()->badMethodCall(
207
                    '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

207
                    '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

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

316
            $generators = array_merge(isset($generators) ? $generators: [],/** @scrutinizer ignore-type */ $this->getGenerators());
Loading history...
317
        }
318
319
        if(isset($generators)){
320
            $this->generatorMethod($generators);
321
        }
322
    }
323
324
    /**
325
     * generator method
326
     *
327
     * @param $generators
328
     *
329
     * @throws ReflectionExceptionAlias
330
     */
331
    private function generatorMethod($generators)
332
    {
333
        //generator array object
334
        foreach ($generators as $generator){
335
336
            //generator method name
337
            $generatorMethodName = $generator.'Generator';
338
339
            // if the generator method is present,
340
            // the fake value is assigned.
341
            if(method_exists($this,$generatorMethodName)){
342
343
                //fake registration
344
                if(!isset($this->inputs[$generator])){
345
346
                    $generatorMethodNameResult = $this->{$generatorMethodName}();
347
348
                    if(!is_null($generatorMethodNameResult)){
349
                        $this->{$generator} = $this->{$generatorMethodName}();
350
                        $this->inputs[$generator] = $this->{$generatorMethodName}();
351
                        $this->generatorList[] = $generator;
352
                    }
353
                }
354
                else {
355
356
                    if($this->checkProperties('auto_generators_dont_overwrite')
357
                        && 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

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

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