Passed
Pull Request — master (#343)
by Alexander
03:36 queued 56s
created

Callback   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 85
Duplicated Lines 0 %

Test Coverage

Coverage 80%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 13
eloc 32
c 2
b 0
f 0
dl 0
loc 85
ccs 20
cts 25
cp 0.8
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 23 5
A getMethod() 0 3 1
A getName() 0 3 1
A getCallback() 0 3 1
A afterInitAttribute() 0 19 3
A getHandlerClassName() 0 3 1
A getOptions() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Attribute;
8
use Closure;
9
use InvalidArgumentException;
10
use ReflectionObject;
11
use Yiisoft\Validator\AttributeEventInterface;
12
use Yiisoft\Validator\DataSet\ObjectDataSet;
13
use Yiisoft\Validator\DataSetInterface;
14
use Yiisoft\Validator\Rule\Trait\SkipOnEmptyTrait;
15
use Yiisoft\Validator\Rule\Trait\SkipOnErrorTrait;
16
use Yiisoft\Validator\Rule\Trait\WhenTrait;
17
use Yiisoft\Validator\SerializableRuleInterface;
18
use Yiisoft\Validator\SkipOnEmptyInterface;
19
use Yiisoft\Validator\SkipOnErrorInterface;
20
use Yiisoft\Validator\ValidationContext;
21
use Yiisoft\Validator\WhenInterface;
22
23
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
24
final class Callback implements
25
    SerializableRuleInterface,
26
    SkipOnErrorInterface,
27
    WhenInterface,
28
    SkipOnEmptyInterface,
29
    AttributeEventInterface
30
{
31
    use SkipOnEmptyTrait;
32
    use SkipOnErrorTrait;
33
    use WhenTrait;
34
35 9
    public function __construct(
36
        /**
37
         * @var callable|null
38
         */
39
        private $callback = null,
40
        private ?string $method = null,
41
42
        /**
43
         * @var bool|callable|null
44
         */
45
        private $skipOnEmpty = null,
46
        private bool $skipOnError = false,
47
        /**
48
         * @var Closure(mixed, ValidationContext):bool|null
49
         */
50
        private ?Closure $when = null,
51
    ) {
52 9
        if ($this->callback === null && $this->method === null) {
53
            throw new InvalidArgumentException('Either "$callback" or "$method" must be specified.');
54
        }
55
56 9
        if ($this->callback !== null && $this->method !== null) {
57
            throw new InvalidArgumentException('"$callback" and "$method" are mutually exclusive.');
58
        }
59
    }
60
61 1
    public function getName(): string
62
    {
63 1
        return 'callback';
64
    }
65
66 14
    public function getCallback(): ?callable
67
    {
68 14
        return $this->callback;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->callback returns the type mixed which is incompatible with the type-hinted return callable|null.
Loading history...
69
    }
70
71
    public function getMethod(): ?string
72
    {
73
        return $this->method;
74
    }
75
76 3
    public function afterInitAttribute(DataSetInterface $dataSet): void
77
    {
78 3
        if (!$dataSet instanceof ObjectDataSet) {
79
            return;
80
        }
81
82 3
        $object = $dataSet->getObject();
83 3
        $method = $this->method;
84
85 3
        $reflection = new ReflectionObject($object);
86 3
        if (!$reflection->hasMethod($method)) {
87 1
            throw new InvalidArgumentException(sprintf(
88
                'Method "%s" does not exist in class "%s".',
89
                $method,
90
                $object::class,
91
            ));
92
        }
93
94 2
        $this->callback = Closure::bind(fn (...$args) => $object->{$method}(...$args), $object, $object);
95
    }
96
97 2
    public function getOptions(): array
98
    {
99
        return [
100 2
            'skipOnEmpty' => $this->getSkipOnEmptyOption(),
101 2
            'skipOnError' => $this->skipOnError,
102
        ];
103
    }
104
105 14
    public function getHandlerClassName(): string
106
    {
107 14
        return CallbackHandler::class;
108
    }
109
}
110