Passed
Pull Request — master (#369)
by
unknown
03:01
created

Callback::__construct()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 23
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 23
ccs 5
cts 5
cp 1
rs 9.6111
cc 5
nc 3
nop 5
crap 5
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\Rule\Trait\SkipOnEmptyTrait;
14
use Yiisoft\Validator\Rule\Trait\SkipOnErrorTrait;
15
use Yiisoft\Validator\Rule\Trait\WhenTrait;
16
use Yiisoft\Validator\SerializableRuleInterface;
17
use Yiisoft\Validator\SkipOnEmptyInterface;
18
use Yiisoft\Validator\SkipOnErrorInterface;
19
use Yiisoft\Validator\ValidationContext;
20
use Yiisoft\Validator\WhenInterface;
21
22
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
23
final class Callback implements
24
    SerializableRuleInterface,
25
    SkipOnErrorInterface,
26
    WhenInterface,
27
    SkipOnEmptyInterface,
28
    AttributeEventInterface
29
{
30
    use SkipOnEmptyTrait;
31
    use SkipOnErrorTrait;
32
    use WhenTrait;
33
34 16
    public function __construct(
35
        /**
36
         * @var callable|null
37
         */
38
        private $callback = null,
39
        private string|null $method = null,
40
41
        /**
42
         * @var bool|callable|null
43
         */
44
        private $skipOnEmpty = null,
45
        private bool $skipOnError = false,
46
        /**
47
         * @var Closure(mixed, ValidationContext):bool|null
48
         */
49
        private ?Closure $when = null,
50
    ) {
51 16
        if ($this->callback === null && $this->method === null) {
52 1
            throw new InvalidArgumentException('Either "$callback" or "$method" must be specified.');
53
        }
54
55 15
        if ($this->callback !== null && $this->method !== null) {
56 1
            throw new InvalidArgumentException('"$callback" and "$method" are mutually exclusive.');
57
        }
58
    }
59
60 1
    public function getName(): string
61
    {
62 1
        return 'callback';
63
    }
64
65 16
    public function getCallback(): callable|null
66
    {
67 16
        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...
68
    }
69
70 2
    public function getMethod(): string|null
71
    {
72 2
        return $this->method;
73
    }
74
75 4
    public function afterInitAttribute(ObjectDataSet $dataSet): void
76
    {
77 4
        if ($this->method === null) {
78 1
            return;
79
        }
80
81 3
        $object = $dataSet->getObject();
82 3
        $method = $this->method;
83
84 3
        $reflection = new ReflectionObject($object);
85 3
        if (!$reflection->hasMethod($method)) {
86 1
            throw new InvalidArgumentException(sprintf(
87
                'Method "%s" does not exist in class "%s".',
88
                $method,
89
                $object::class,
90
            ));
91
        }
92
93
        /** @psalm-suppress MixedMethodCall */
94 2
        $this->callback = Closure::bind(fn (mixed ...$args): mixed => $object->{$method}(...$args), $object, $object);
95
    }
96
97 3
    public function getOptions(): array
98
    {
99
        return [
100 3
            'method' => $this->method,
101 3
            'skipOnEmpty' => $this->getSkipOnEmptyOption(),
102 3
            'skipOnError' => $this->skipOnError,
103
        ];
104
    }
105
106 15
    public function getHandlerClassName(): string
107
    {
108 15
        return CallbackHandler::class;
109
    }
110
}
111