Passed
Pull Request — master (#468)
by Sergei
03:08
created

Callback::getMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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