Skip to content

Commit dafe3a2

Browse files
committed
Add support for PHP 8 attributes
1 parent af68f44 commit dafe3a2

File tree

7 files changed

+239
-0
lines changed

7 files changed

+239
-0
lines changed

Mapping/Annotations/Address.php

Lines changed: 1 addition & 0 deletions
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 <markus.bachmann@bachi.biz>
1718
*

Mapping/Annotations/Geocodeable.php

Lines changed: 1 addition & 0 deletions
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 <markus.bachmann@bachi.biz>
1718
*

Mapping/Annotations/Latitude.php

Lines changed: 1 addition & 0 deletions
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 <markus.bachmann@bachi.biz>
1718
*

Mapping/Annotations/Longitude.php

Lines changed: 1 addition & 0 deletions
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 <markus.bachmann@bachi.biz>
1718
*

Mapping/Driver/AttributeDriver.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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\Persistence\Proxy;
19+
20+
/**
21+
* @author Pierre du Plessis <pdples@gmail.com>
22+
*/
23+
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 = new \ReflectionObject($object);
32+
33+
if ($object instanceof Proxy) {
34+
$reflection = $reflection->getParentClass();
35+
}
36+
37+
return [] !== $reflection->getAttributes(Annotations\Geocodeable::class);
38+
}
39+
40+
/**
41+
* @throws MappingException
42+
*/
43+
public function loadMetadataFromObject($object): ClassMetadata
44+
{
45+
if (PHP_VERSION_ID < 80000) {
46+
throw new MappingException(sprintf('The class %s is not geocodeable', get_class($object)));
47+
}
48+
49+
$reflection = new \ReflectionObject($object);
50+
51+
if ($object instanceof Proxy) {
52+
$reflection = $reflection->getParentClass();
53+
}
54+
55+
$attributes = $reflection->getAttributes(Annotations\Geocodeable::class);
56+
57+
if ([] === $attributes) {
58+
throw new MappingException(sprintf('The class %s is not geocodeable', get_class($object)));
59+
}
60+
61+
$metadata = new ClassMetadata();
62+
63+
foreach ($reflection->getProperties() as $property) {
64+
foreach ($property->getAttributes() as $attribute) {
65+
66+
if ($attribute->getName() === Annotations\Latitude::class) {
67+
$property->setAccessible(true);
68+
$metadata->latitudeProperty = $property;
69+
} elseif ($attribute->getName() === Annotations\Longitude::class) {
70+
$property->setAccessible(true);
71+
$metadata->longitudeProperty = $property;
72+
} elseif ($attribute->getName() === Annotations\Address::class) {
73+
$property->setAccessible(true);
74+
$metadata->addressProperty = $property;
75+
}
76+
}
77+
}
78+
79+
foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
80+
if ([] !== $method->getAttributes(Annotations\Address::class)) {
81+
if (0 !== $method->getNumberOfRequiredParameters()) {
82+
throw new MappingException('You can not use a method requiring parameters with #[Address] attribute!');
83+
}
84+
85+
$metadata->addressGetter = $method;
86+
}
87+
}
88+
89+
return $metadata;
90+
}
91+
}

Resources/doc/doctrine.md

Lines changed: 37 additions & 0 deletions
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+
```
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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\Geocodeable;
16+
use Bazinga\GeocoderBundle\Mapping\Annotations\Address;
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 <pdples@gmail.com>
27+
*/
28+
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+
protected function doSetUp(): void
43+
{
44+
$this->driver = new AttributeDriver();
45+
}
46+
47+
/**
48+
* @requires PHP 8
49+
*/
50+
public function testLoadMetadata()
51+
{
52+
if (PHP_VERSION_ID < 80000) {
53+
$this->markTestSkipped(sprintf('"%s" is only supported on PHP 8', AttributeDriver::class));
54+
}
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+
/**
65+
* @requires PHP 8
66+
*/
67+
public function testLoadMetadataFromWrongObject()
68+
{
69+
if (PHP_VERSION_ID < 80000) {
70+
$this->markTestSkipped(sprintf('"%s" is only supported on PHP 8', AttributeDriver::class));
71+
}
72+
73+
$this->expectException(MappingException::class);
74+
$this->expectExceptionMessage('The class '.Dummy4::class.' is not geocodeable');
75+
76+
$this->driver->loadMetadataFromObject(new Dummy4());
77+
}
78+
79+
/**
80+
* @requires PHP 8
81+
*/
82+
public function testIsGeocodable()
83+
{
84+
if (PHP_VERSION_ID < 80000) {
85+
$this->markTestSkipped(sprintf('"%s" is only supported on PHP 8', AttributeDriver::class));
86+
}
87+
88+
$this->assertTrue($this->driver->isGeocodeable(new Dummy3()));
89+
}
90+
}
91+
92+
#[Geocodeable()]
93+
class Dummy3
94+
{
95+
#[Latitude()]
96+
public $latitude;
97+
98+
#[Longitude()]
99+
public $longitude;
100+
101+
#[Address()]
102+
public $address;
103+
}
104+
105+
class Dummy4
106+
{
107+
}

0 commit comments

Comments
 (0)