Skip to content

Commit 3460bbf

Browse files
authored
Merge pull request #302 from pierredup/php8-attributes
Add support for PHP 8 attributes
2 parents e0ae2b2 + f007051 commit 3460bbf

File tree

7 files changed

+216
-0
lines changed

7 files changed

+216
-0
lines changed

Mapping/Annotations/Address.php

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace Bazinga\GeocoderBundle\Mapping\Annotations;
1414

15+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
1516
/**
1617
* @author Markus Bachmann <[email protected]>
1718
*

Mapping/Annotations/Geocodeable.php

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace Bazinga\GeocoderBundle\Mapping\Annotations;
1414

15+
#[\Attribute(\Attribute::TARGET_CLASS)]
1516
/**
1617
* @author Markus Bachmann <[email protected]>
1718
*

Mapping/Annotations/Latitude.php

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace Bazinga\GeocoderBundle\Mapping\Annotations;
1414

15+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
1516
/**
1617
* @author Markus Bachmann <[email protected]>
1718
*

Mapping/Annotations/Longitude.php

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace Bazinga\GeocoderBundle\Mapping\Annotations;
1414

15+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
1516
/**
1617
* @author Markus Bachmann <[email protected]>
1718
*

Mapping/Driver/AttributeDriver.php

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the BazingaGeocoderBundle package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Bazinga\GeocoderBundle\Mapping\Driver;
14+
15+
use Bazinga\GeocoderBundle\Mapping\Annotations;
16+
use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
17+
use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;
18+
use Doctrine\Common\Util\ClassUtils;
19+
20+
/**
21+
* @author Pierre du Plessis <[email protected]>
22+
*/
23+
final class AttributeDriver implements DriverInterface
24+
{
25+
public function isGeocodeable($object): bool
26+
{
27+
if (PHP_VERSION_ID < 80000) {
28+
return false;
29+
}
30+
31+
$reflection = ClassUtils::newReflectionObject($object);
32+
33+
return count($reflection->getAttributes(Annotations\Geocodeable::class)) > 0;
34+
}
35+
36+
/**
37+
* @throws MappingException
38+
*/
39+
public function loadMetadataFromObject($object): ClassMetadata
40+
{
41+
if (PHP_VERSION_ID < 80000) {
42+
throw new MappingException(sprintf('The class %s is not geocodeable', get_class($object)));
43+
}
44+
45+
$reflection = ClassUtils::newReflectionObject($object);
46+
47+
$attributes = $reflection->getAttributes(Annotations\Geocodeable::class);
48+
49+
if (0 === count($attributes)) {
50+
throw new MappingException(sprintf('The class %s is not geocodeable', get_class($object)));
51+
}
52+
53+
$metadata = new ClassMetadata();
54+
55+
foreach ($reflection->getProperties() as $property) {
56+
foreach ($property->getAttributes() as $attribute) {
57+
if (Annotations\Latitude::class === $attribute->getName()) {
58+
$property->setAccessible(true);
59+
$metadata->latitudeProperty = $property;
60+
} elseif (Annotations\Longitude::class === $attribute->getName()) {
61+
$property->setAccessible(true);
62+
$metadata->longitudeProperty = $property;
63+
} elseif (Annotations\Address::class === $attribute->getName()) {
64+
$property->setAccessible(true);
65+
$metadata->addressProperty = $property;
66+
}
67+
}
68+
}
69+
70+
foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
71+
if (count($method->getAttributes(Annotations\Address::class)) > 0) {
72+
if (0 !== $method->getNumberOfRequiredParameters()) {
73+
throw new MappingException('You can not use a method requiring parameters with #[Address] attribute!');
74+
}
75+
76+
$metadata->addressGetter = $method;
77+
}
78+
}
79+
80+
return $metadata;
81+
}
82+
}

Resources/doc/doctrine.md

+37
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,40 @@ $em->flush();
9696
echo $user->getLatitude(); // will output 52.516325
9797
echo $user->getLongitude(); // will output 13.377264
9898
```
99+
100+
## PHP 8
101+
102+
If you are using PHP 8, you can use [Attributes](https://www.php.net/manual/en/language.attributes.overview.php) in your entity:
103+
104+
```php
105+
106+
use Bazinga\GeocoderBundle\Mapping\Annotations as Geocoder;
107+
108+
#[Geocoder\Geocodeable()]
109+
class User
110+
{
111+
#[Geocoder\Address()]
112+
private $address;
113+
114+
#[Geocoder\Latitude()]
115+
private $latitude;
116+
117+
#[Geocoder\Longitude()]
118+
private $longitude;
119+
}
120+
```
121+
122+
Then update your service configuration to register the `AttributeDriver`:
123+
124+
```yaml
125+
Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver:
126+
class: Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver
127+
128+
Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener:
129+
class: Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener
130+
arguments:
131+
- '@bazinga_geocoder.provider.acme'
132+
- '@Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver'
133+
tags:
134+
- doctrine.event_subscriber
135+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the BazingaGeocoderBundle package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Bazinga\GeocoderBundle\Tests\Mapping\Driver;
14+
15+
use Bazinga\GeocoderBundle\Mapping\Annotations\Address;
16+
use Bazinga\GeocoderBundle\Mapping\Annotations\Geocodeable;
17+
use Bazinga\GeocoderBundle\Mapping\Annotations\Latitude;
18+
use Bazinga\GeocoderBundle\Mapping\Annotations\Longitude;
19+
use Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver;
20+
use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;
21+
use Doctrine\Common\Annotations\Reader;
22+
use PHPUnit\Framework\TestCase;
23+
use Symfony\Bridge\PhpUnit\SetUpTearDownTrait;
24+
25+
/**
26+
* @author Pierre du Plessis <[email protected]>
27+
*/
28+
final class AttributeDriverTest extends TestCase
29+
{
30+
use SetUpTearDownTrait;
31+
32+
/**
33+
* @var AttributeDriver
34+
*/
35+
private $driver;
36+
37+
/**
38+
* @var Reader
39+
*/
40+
private $reader;
41+
42+
public static function doSetUpBeforeClass(): void
43+
{
44+
if (PHP_VERSION_ID < 80000) {
45+
self::markTestSkipped(sprintf('"%s" is only supported on PHP 8', AttributeDriver::class));
46+
}
47+
}
48+
49+
protected function doSetUp(): void
50+
{
51+
$this->driver = new AttributeDriver();
52+
}
53+
54+
public function testLoadMetadata()
55+
{
56+
$obj = new Dummy3();
57+
$metadata = $this->driver->loadMetadataFromObject($obj);
58+
59+
$this->assertInstanceOf('ReflectionProperty', $metadata->addressProperty);
60+
$this->assertInstanceOf('ReflectionProperty', $metadata->latitudeProperty);
61+
$this->assertInstanceOf('ReflectionProperty', $metadata->longitudeProperty);
62+
}
63+
64+
public function testLoadMetadataFromWrongObject()
65+
{
66+
$this->expectException(MappingException::class);
67+
$this->expectExceptionMessage('The class '.Dummy4::class.' is not geocodeable');
68+
69+
$this->driver->loadMetadataFromObject(new Dummy4());
70+
}
71+
72+
public function testIsGeocodable()
73+
{
74+
$this->assertTrue($this->driver->isGeocodeable(new Dummy3()));
75+
}
76+
}
77+
78+
#[Geocodeable()]
79+
class Dummy3
80+
{
81+
#[Latitude()]
82+
public $latitude;
83+
84+
#[Longitude()]
85+
public $longitude;
86+
87+
#[Address()]
88+
public $address;
89+
}
90+
91+
class Dummy4
92+
{
93+
}

0 commit comments

Comments
 (0)