Skip to content

Commit 32c3004

Browse files
committed
Add PHP8 attributes support
1 parent 63c73a8 commit 32c3004

12 files changed

Lines changed: 149 additions & 157 deletions

.travis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ env:
55
- XDEBUG_MODE=coverage
66
language: php
77
php:
8-
- '7.2'
9-
- '7.3'
108
- '7.4'
119
- '8.0'
1210

README.md

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ Squirrel Validator Cascade
33

44
[![Build Status](https://img.shields.io/travis/com/squirrelphp/validator-cascade.svg)](https://travis-ci.com/squirrelphp/validator-cascade) [![Test Coverage](https://api.codeclimate.com/v1/badges/e056be025c6db0eb31f1/test_coverage)](https://codeclimate.com/github/squirrelphp/validator-cascade/test_coverage) ![PHPStan](https://img.shields.io/badge/style-level%208-success.svg?style=flat-round&label=phpstan) [![Packagist Version](https://img.shields.io/packagist/v/squirrelphp/validator-cascade.svg?style=flat-round)](https://packagist.org/packages/squirrelphp/validator-cascade) [![PHP Version](https://img.shields.io/packagist/php-v/squirrelphp/validator-cascade.svg)](https://packagist.org/packages/squirrelphp/validator-cascade) [![Software License](https://img.shields.io/badge/license-MIT-success.svg?style=flat-round)](LICENSE)
55

6-
Reimplements the `Valid` constraint in the Symfony validator component as `Cascade` annotation which is more straightforward to use than `Valid` and has no surprising behavior.
6+
Reimplements the `Valid` constraint in the Symfony validator component as `Cascade` annotation/attribute which is more straightforward to use than `Valid` and has no surprising behavior.
77

8-
This component is compatible with the Symfony validator component in versions 4.0+ and 5.0+ and will be adapted to support future versions of Symfony (if any changes are necessary for that).
8+
This component is compatible with the Symfony validator component in version 5.0+ and will be adapted to support future versions of Symfony (if any changes are necessary for that).
99

1010
Installation
1111
------------
@@ -53,62 +53,106 @@ class Order
5353
*
5454
* Validates "Default" and "phoneNumberMandatory" validation groups in $shippingAddress
5555
*
56-
* @Assert\NotNull()
57-
* @Assert\Type(type="Address")
5856
* @Cascade(trigger={"Default", "phoneNumberMandatory"})
5957
*/
60-
public $shippingAddress;
58+
public Address $shippingAddress;
6159

6260
/**
6361
* Validate $invoiceAddress only if validation group
6462
* "alternateInvoiceAddress" is passed to validator
6563
*
6664
* Validates only "Default" validation group in $invoiceAddress, so phone number is optional
6765
*
68-
* @Assert\NotNull()
69-
* @Assert\Type(type="Address")
66+
* @Assert\NotNull(groups={"alternateInvoiceAddress"})
7067
* @Cascade(groups={"alternateInvoiceAddress"})
7168
*/
72-
public $invoiceAddress;
69+
public ?Address $invoiceAddress = null;
7370
}
7471

7572
class Address
7673
{
7774
/**
78-
* @Assert\NotNull()
79-
* @Assert\NotBlank()
8075
* @Assert\Length(
8176
* min = 1,
8277
* max = 50
8378
* )
84-
*
85-
* @var string
8679
*/
87-
public $street = '';
80+
public string $street = '';
8881

8982
/**
90-
* @Assert\NotNull()
91-
* @Assert\NotBlank()
9283
* @Assert\Length(
9384
* min = 1,
9485
* max = 50
9586
* )
96-
*
97-
* @var string
9887
*/
99-
public $city = '';
88+
public string $city = '';
10089

10190
/**
102-
* @Assert\NotNull(groups={"phoneNumberMandatory"})
103-
* @Assert\NotBlank(groups={"phoneNumberMandatory"})
10491
* @Assert\Length(
10592
* min = 1,
106-
* max = 50
93+
* max = 50,
94+
* groups = {"phoneNumberMandatory"}
10795
* )
10896
*
10997
* @var string
11098
*/
111-
public $phoneNumber = '';
99+
public string $phoneNumber = '';
100+
}
101+
102+
$order = new Order();
103+
$order->shippingAddress = new Address();
104+
$order->invoiceAddress = new Address();
105+
106+
// This validates with the "Default" validation group,
107+
// so only shippingAddress must be specified
108+
$symfonyValidator->validate($order);
109+
110+
// This also validates the invoice address in addition
111+
// to the shipping address
112+
$symfonyValidator->validate($order, null, [
113+
"Default",
114+
"alternateInvoiceAddress",
115+
]);
116+
```
117+
118+
If you are using PHP8+, you should use attributes instead (although annotations will still work):
119+
120+
```php
121+
use Squirrel\ValidatorCascade\Cascade;
122+
use Symfony\Component\Validator\Constraints as Assert;
123+
124+
class Order
125+
{
126+
/**
127+
* Validate $shippingAddress if validation with no validation
128+
* group or the "Default" validation group is triggered
129+
*
130+
* Validates "Default" and "phoneNumberMandatory" validation groups in $shippingAddress
131+
*/
132+
#[Cascade(trigger: ['Default', 'phoneNumberMandatory'])]
133+
public Address $shippingAddress;
134+
135+
/**
136+
* Validate $invoiceAddress only if validation group
137+
* "alternateInvoiceAddress" is passed to validator
138+
*
139+
* Validates only "Default" validation group in $invoiceAddress, so phone number is optional
140+
*/
141+
#[Assert\NotNull(groups: ['alternateInvoiceAddress'])]
142+
#[Cascade(groups: ['alternateInvoiceAddress'])]
143+
public ?Address $invoiceAddress = null;
144+
}
145+
146+
class Address
147+
{
148+
#[Assert\Length(min: 1, max: 50)]
149+
public string $street = '';
150+
151+
#[Assert\Length(min: 1, max: 50)]
152+
public string $city = '';
153+
154+
#[Assert\Length(min: 1, max: 50, groups: ['phoneNumberMandatory'])]
155+
public string $phoneNumber = '';
112156
}
113157

114158
$order = new Order();

composer.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
}
2020
],
2121
"require": {
22-
"php": ">=7.2",
23-
"symfony/validator": "^4.0|^5.0",
24-
"doctrine/annotations": "^1.6"
22+
"php": ">=7.4",
23+
"symfony/validator": "^5.0"
2524
},
2625
"require-dev": {
26+
"doctrine/annotations": "^1.6",
2727
"bamarni/composer-bin-plugin": "^1.3",
2828
"captainhook/plugin-composer": "^5.0",
29-
"phpunit/phpunit": "^8.0|^9.0",
29+
"phpunit/phpunit": "^9.0",
3030
"mockery/mockery": "^1.0"
3131
},
3232
"config": {
@@ -52,9 +52,9 @@
5252
"psalm_base": "vendor/bin/psalm --set-baseline=psalm-baseline.xml",
5353
"phpunit": "vendor/bin/phpunit --colors=always",
5454
"phpunit_clover": "vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml",
55-
"coverage": "vendor/bin/phpunit --coverage-html tests/_reports",
56-
"phpcs": "vendor/bin/phpcs --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --colors src tests",
57-
"phpcsfix": "vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --cache=.phpcs-cache src tests",
55+
"coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html tests/_reports",
56+
"phpcs": "vendor/bin/phpcs --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=src/Cascade.php --colors src tests",
57+
"phpcsfix": "vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=src/Cascade.php src tests",
5858
"binupdate": "@composer bin all update --ansi",
5959
"bininstall": "@composer bin all install --ansi"
6060
}

examples/Address.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,8 @@ class Address
4040
*
4141
* @var string
4242
*/
43+
#[Assert\NotNull(groups: ['phoneNumberMandatory'])]
44+
#[Assert\NotBlank(groups: ['phoneNumberMandatory'])]
45+
#[Assert\Length(min: 1, max: 50)]
4346
public $phoneNumber;
4447
}

examples/Order.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class Order
2020
*
2121
* @var Address
2222
*/
23+
#[Assert\NotNull()]
24+
#[Assert\Type(type: 'Squirrel\ValidatorCascade\Examples\Address')]
25+
#[Cascade(trigger: ['Default', 'phoneNumberMandatory'])]
2326
public $shippingAddress;
2427

2528
/**
@@ -31,7 +34,7 @@ class Order
3134
*
3235
* @Assert\NotNull()
3336
* @Assert\Type(type="Squirrel\ValidatorCascade\Examples\Address")
34-
* @Cascade(groups="alternateInvoiceAddress")
37+
* @Cascade(groups={"alternateInvoiceAddress"})
3538
*
3639
* @var Address
3740
*/
@@ -43,7 +46,7 @@ class Order
4346
*
4447
* @Assert\NotNull()
4548
* @Assert\Type(type="Squirrel\ValidatorCascade\Examples\Address")
46-
* @Cascade(trigger="phoneNumberMandatory")
49+
* @Cascade(trigger={"phoneNumberMandatory"})
4750
*
4851
* @var Address
4952
*/

phpunit.xml.dist

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
3-
<phpunit
4-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5-
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/9.1/phpunit.xsd"
6-
backupGlobals="false"
7-
colors="true"
8-
bootstrap="vendor/autoload.php"
9-
>
10-
<testsuites>
11-
<testsuite name="Unit Tests">
12-
<directory>tests</directory>
13-
</testsuite>
14-
</testsuites>
15-
16-
<filter>
17-
<whitelist>
18-
<directory suffix=".php">src</directory>
19-
</whitelist>
20-
</filter>
21-
</phpunit>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="vendor/autoload.php">
3+
<coverage>
4+
<include>
5+
<directory suffix=".php">src</directory>
6+
</include>
7+
</coverage>
8+
<testsuites>
9+
<testsuite name="Unit Tests">
10+
<directory>tests</directory>
11+
</testsuite>
12+
</testsuites>
13+
</phpunit>

psalm-baseline.xml

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,2 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="4.3.1@2feba22a005a18bf31d4c7b9bdb9252c73897476">
3-
<file src="examples/Address.php">
4-
<MissingConstructor occurrences="3">
5-
<code>$city</code>
6-
<code>$phoneNumber</code>
7-
<code>$street</code>
8-
</MissingConstructor>
9-
</file>
10-
<file src="examples/Order.php">
11-
<MissingConstructor occurrences="3">
12-
<code>$invoiceAddress</code>
13-
<code>$phoneNumberOnlyAddress</code>
14-
<code>$shippingAddress</code>
15-
</MissingConstructor>
16-
</file>
17-
</files>
2+
<files psalm-version="4.3.1@2feba22a005a18bf31d4c7b9bdb9252c73897476"/>

psalm.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
>
99
<projectFiles>
1010
<directory name="src" />
11-
<directory name="examples" />
1211
<ignoreFiles>
1312
<directory name="vendor" />
1413
</ignoreFiles>

src/Cascade.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,40 @@
66

77
/**
88
* @Annotation
9-
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
9+
* @Target({"PROPERTY", "METHOD"})
1010
*/
11+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
1112
class Cascade extends Constraint
1213
{
1314
/**
14-
* @var mixed Which validation groups to trigger in any child objects, can be array or string
15+
* @var array Which validation groups to trigger in any child objects
1516
*/
16-
public $trigger = [Constraint::DEFAULT_GROUP];
17+
private array $trigger = [Constraint::DEFAULT_GROUP];
18+
19+
/**
20+
* @param array|null $options
21+
* @param string[] $groups
22+
* @param string[] $trigger
23+
*/
24+
public function __construct(
25+
$options = null,
26+
array $groups = [Constraint::DEFAULT_GROUP],
27+
array $trigger = [Constraint::DEFAULT_GROUP]
28+
) {
29+
if (isset($options['groups'])) {
30+
$groups = $options['groups'];
31+
}
32+
33+
if (isset($options['trigger'])) {
34+
$trigger = $options['trigger'];
35+
}
36+
37+
$this->groups = $groups;
38+
$this->trigger = $trigger;
39+
}
40+
41+
public function getTrigger(): array
42+
{
43+
return $this->trigger;
44+
}
1745
}

src/CascadeValidator.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,9 @@ public function validate($value, Constraint $constraint): void
1919
return;
2020
}
2121

22-
// Convert string to array if a string was given for trigger validation groups
23-
if (\is_string($constraint->trigger)) {
24-
$constraint->trigger = [$constraint->trigger];
25-
}
26-
27-
// At this point we require an array, otherwise something went wrong
28-
if (!\is_array($constraint->trigger)) {
29-
throw new UnexpectedTypeException($constraint->trigger, 'array|string');
30-
}
31-
3222
$this->context
3323
->getValidator()
3424
->inContext($this->context)
35-
->validate($value, null, $constraint->trigger);
25+
->validate($value, null, $constraint->getTrigger());
3626
}
3727
}

0 commit comments

Comments
 (0)