diff --git a/.gitattributes b/.gitattributes
index 30da17f8..c47225aa 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,4 +12,4 @@
/phpspec.yml.dist export-ignore
/phpunit.xml.dist export-ignore
/spec/ export-ignore
-/Tests/ export-ignore
+/tests/ export-ignore
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 65dd06ff..bfb1cf15 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,10 +7,10 @@ concurrency:
jobs:
phpstan:
- name: "PHPStan"
+ name: PHPStan
runs-on: ubuntu-latest
env:
- php-version: 8.1
+ php-version: 8.2
steps:
- name: "Setup PHP"
uses: shivammathur/setup-php@v2
@@ -19,11 +19,12 @@ jobs:
tools: flex
- name: "Checkout code"
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: "Install Composer dependencies"
- run: |
- composer install --prefer-dist --no-progress --no-interaction --optimize-autoloader
+ uses: "ramsey/composer-install@v2"
+ with:
+ composer-options: "--optimize-autoloader"
- name: "Run PHPStan"
run: |
@@ -34,7 +35,7 @@ jobs:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
env:
- php-version: 8.1
+ php-version: 8.2
steps:
- name: "Setup PHP"
uses: shivammathur/setup-php@v2
@@ -43,79 +44,34 @@ jobs:
tools: flex, cs2pr
- name: "Checkout code"
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: "Install Composer dependencies"
- run: |
- composer install --prefer-dist --no-progress --no-interaction --optimize-autoloader
+ uses: "ramsey/composer-install@v2"
+ with:
+ composer-options: "--optimize-autoloader"
- name: "Run PHP-CS-Fixer"
run: vendor/bin/php-cs-fixer fix -v --dry-run --using-cache=no --format=checkstyle | cs2pr
phpunit:
- name: "PHPUnit"
+ name: PHPUnit (PHP ${{ matrix.php }}) (Symfony ${{ matrix.sf_version }}) (${{ matrix.dependencies }})
runs-on: ubuntu-latest
strategy:
max-parallel: 10
fail-fast: true
matrix:
- strategy: ['default']
- php: ['7.4', '8.0', '8.1']
+ dependencies: ['highest']
+ php: ['7.4', '8.0', '8.1', '8.2']
include:
- php: '7.4'
- sf_version: '5.4.*'
- strategy: 'lowest'
- - php: '8.0'
- sf_version: '5.4.*'
- - php: '8.1'
- sf_version: '5.4.*'
- - php: '8.0'
- sf_version: '6.0.*'
- - php: '8.1'
- sf_version: '6.0.*'
- - php: '8.1'
- sf_version: '6.1.*'
- steps:
- - name: "Setup PHP"
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php }}
- tools: flex
- coverage: none
-
- - name: "Checkout code"
- uses: actions/checkout@v3
-
- - name: "Install Composer dependencies (default)"
- if: matrix.strategy != 'lowest'
- env:
- SYMFONY_REQUIRE: ${{ matrix.sf_version }}
- run: composer update --no-interaction --optimize-autoloader
-
- - name: "Install Composer dependencies (lowest)"
- if: matrix.strategy == 'lowest'
- env:
- SYMFONY_REQUIRE: ${{ matrix.sf_version }}
- run: composer update --no-interaction --prefer-stable --prefer-lowest --optimize-autoloader
-
- - name: "Run tests"
- env:
- SYMFONY_DEPRECATIONS_HELPER: 'max[self]=2'
- run: ./vendor/bin/simple-phpunit -v --testsuite main
-
- doctrine:
- name: "PHPUnit (Doctrine)"
- runs-on: ubuntu-latest
- strategy:
- max-parallel: 10
- fail-fast: true
- matrix:
- php: ['7.4', '8.0', '8.1']
- include:
+ sf_version: '4.4.*'
+ dependencies: 'lowest'
- php: '7.4'
sf_version: '4.4.*'
- php: '7.4'
sf_version: '5.4.*'
+ dependencies: 'lowest'
- php: '8.0'
sf_version: '5.4.*'
- php: '8.1'
@@ -124,8 +80,12 @@ jobs:
sf_version: '6.0.*'
- php: '8.1'
sf_version: '6.0.*'
- - php: '8.1'
- sf_version: '6.1.*'
+ - php: '8.2'
+ sf_version: '6.2.*'
+ - php: '8.2'
+ sf_version: '6.4.*'
+ - php: '8.2'
+ sf_version: '7.0.*'
steps:
- name: "Setup PHP"
uses: shivammathur/setup-php@v2
@@ -135,14 +95,15 @@ jobs:
coverage: none
- name: "Checkout code"
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: "Install Composer dependencies"
- env:
- SYMFONY_REQUIRE: ${{ matrix.sf_version }}
- run: composer update --prefer-source --no-interaction --optimize-autoloader
+ uses: "ramsey/composer-install@v2"
+ with:
+ composer-options: "--optimize-autoloader"
+ dependency-versions: "${{ matrix.dependencies }}"
- name: "Run tests"
env:
- SYMFONY_DEPRECATIONS_HELPER: 'max[self]=2'
- run: ./vendor/bin/simple-phpunit -v --testsuite doctrine
+ SYMFONY_DEPRECATIONS_HELPER: 'max[self]=3&max[indirect]=1'
+ run: ./vendor/bin/simple-phpunit -v
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index c78dec05..7236c5b8 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -8,6 +8,7 @@
return (new PhpCsFixer\Config())
->setRules([
'@Symfony' => true,
+ 'no_superfluous_phpdoc_tags' => false,
])
->setFinder($finder)
;
diff --git a/composer.json b/composer.json
index 144c4103..2cba9b9b 100644
--- a/composer.json
+++ b/composer.json
@@ -13,13 +13,16 @@
"require": {
"php": "^7.4 || ^8.0",
"geocoder-php/plugin": "^1.5",
+ "php-http/curl-client": "^2.3",
"php-http/discovery": "^1.14",
- "symfony/console": "^4.4 || ^5.0 || ^6.0",
- "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0",
- "symfony/options-resolver": "^4.4 || ^5.0 || ^6.0",
+ "symfony/console": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/options-resolver": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"willdurand/geocoder": "^4.6"
},
"require-dev": {
+ "doctrine/annotations": "^1.11.1 || ^2.0",
+ "doctrine/doctrine-bundle": "^2.3",
"doctrine/orm": "~2.8",
"fakerphp/faker": "^1.20",
"friendsofphp/php-cs-fixer": "^3.13",
@@ -56,13 +59,15 @@
"nyholm/nsa": "^1.3",
"nyholm/psr7": "^1.5",
"nyholm/symfony-bundle-test": "dev-master",
- "php-http/curl-client": "^2.2",
+ "php-http/mock-client": "^1.6",
"php-http/message": "^1.13",
"phpstan/phpstan": "^1.9.2",
- "symfony/config": "^4.4 || ^5.0 || ^6.0",
- "symfony/phpunit-bridge": "^5.2 || ^6.0",
- "symfony/validator": "^4.4 || ^5.0 || ^6.0",
- "symfony/yaml": "^4.4 || ^5.0 || ^6.0"
+ "psr/http-client": "^1.0",
+ "symfony/cache": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/phpunit-bridge": "^5.2 || ^6.0 || ^7.0",
+ "symfony/validator": "^4.4 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/yaml": "^4.4 || ^5.0 || ^6.0 || ^7.0"
},
"conflict": {
"geocoder-php/nominatim-provider": "<5.0"
@@ -82,7 +87,8 @@
},
"config": {
"allow-plugins": {
- "symfony/flex": true
+ "symfony/flex": true,
+ "php-http/discovery": false
},
"sort-packages": true
},
diff --git a/doc/doctrine.md b/doc/doctrine.md
index f89894d6..83c5afa1 100644
--- a/doc/doctrine.md
+++ b/doc/doctrine.md
@@ -80,7 +80,7 @@ You have to indicate which provider to use to reverse geocode the address. Here
- '@bazinga_geocoder.provider.acme'
- '@Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver'
tags:
- - doctrine.event_subscriber
+ - { name: doctrine.event_listener, event: onFlush }
```
It is done!
@@ -131,5 +131,5 @@ Then update your service configuration to register the `AttributeDriver`:
- '@bazinga_geocoder.provider.acme'
- '@Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver'
tags:
- - doctrine.event_subscriber
+ - { name: doctrine.event_listener, event: onFlush }
```
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 0d12c3f3..ac33fcc6 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -42,7 +42,7 @@ parameters:
-
message: "#^Cannot access offset 'options' on mixed\\.$#"
- count: 6
+ count: 4
path: src/DependencyInjection/BazingaGeocoderExtension.php
-
@@ -140,6 +140,11 @@ parameters:
count: 1
path: src/Doctrine/ORM/GeocoderListener.php
+ -
+ message: "#^Method Bazinga\\\\GeocoderBundle\\\\Plugin\\\\FakeIpPlugin\\:\\:handleQuery\\(\\) return type with generic interface Http\\\\Promise\\\\Promise does not specify its types\\: T$#"
+ count: 1
+ path: src/Plugin/FakeIpPlugin.php
+
-
message: "#^Parameter \\#1 \\$text of method Geocoder\\\\Query\\\\GeocodeQuery\\:\\:withText\\(\\) expects string, string\\|null given\\.$#"
count: 1
@@ -150,6 +155,11 @@ parameters:
count: 1
path: src/Plugin/FakeIpPlugin.php
+ -
+ message: "#^Method Bazinga\\\\GeocoderBundle\\\\Plugin\\\\ProfilingPlugin\\:\\:handleQuery\\(\\) return type with generic interface Http\\\\Promise\\\\Promise does not specify its types\\: T$#"
+ count: 1
+ path: src/Plugin/ProfilingPlugin.php
+
-
message: "#^Parameter \\#1 \\$accountId of class GeoIp2\\\\WebService\\\\Client constructor expects int, int\\|null given\\.$#"
count: 1
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 970cd03e..85c1a78e 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,11 +13,6 @@
./tests
- ./tests/Doctrine
-
-
-
- ./tests/Doctrine
diff --git a/src/BazingaGeocoderBundle.php b/src/BazingaGeocoderBundle.php
index 01070055..e9aba853 100644
--- a/src/BazingaGeocoderBundle.php
+++ b/src/BazingaGeocoderBundle.php
@@ -24,8 +24,6 @@
class BazingaGeocoderBundle extends Bundle
{
/**
- * {@inheritdoc}
- *
* @return void
*/
public function build(ContainerBuilder $container)
@@ -37,9 +35,6 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new FactoryValidatorPass());
}
- /**
- * {@inheritdoc}
- */
public function getPath(): string
{
return \dirname(__DIR__);
diff --git a/src/Command/GeocodeCommand.php b/src/Command/GeocodeCommand.php
index 28e01562..12b0c50c 100644
--- a/src/Command/GeocodeCommand.php
+++ b/src/Command/GeocodeCommand.php
@@ -35,8 +35,6 @@ public function __construct(ProviderAggregator $geocoder)
}
/**
- * {@inheritdoc}
- *
* @return void
*/
protected function configure()
@@ -57,12 +55,7 @@ protected function configure()
);
}
- /**
- * {@inheritdoc}
- *
- * @return int
- */
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($input->getOption('provider')) {
$this->geocoder->using($input->getOption('provider'));
diff --git a/src/DataCollector/GeocoderDataCollector.php b/src/DataCollector/GeocoderDataCollector.php
index a98b19be..688932f0 100644
--- a/src/DataCollector/GeocoderDataCollector.php
+++ b/src/DataCollector/GeocoderDataCollector.php
@@ -45,8 +45,6 @@ public function reset()
}
/**
- * {@inheritdoc}
- *
* @return void
*/
public function collect(Request $request, Response $response, \Throwable $exception = null)
@@ -117,9 +115,6 @@ public function addInstance(ProfilingPlugin $instance)
$this->data['providers'][] = $instance->getName();
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'geocoder';
diff --git a/src/DependencyInjection/Compiler/FactoryValidatorPass.php b/src/DependencyInjection/Compiler/FactoryValidatorPass.php
index ed202d13..6f54fba5 100644
--- a/src/DependencyInjection/Compiler/FactoryValidatorPass.php
+++ b/src/DependencyInjection/Compiler/FactoryValidatorPass.php
@@ -29,8 +29,6 @@ class FactoryValidatorPass implements CompilerPassInterface
private static $factoryServiceIds = [];
/**
- * {@inheritdoc}
- *
* @return void
*/
public function process(ContainerBuilder $container)
diff --git a/src/DependencyInjection/Compiler/ProfilerPass.php b/src/DependencyInjection/Compiler/ProfilerPass.php
index 982bcd35..3ba985db 100644
--- a/src/DependencyInjection/Compiler/ProfilerPass.php
+++ b/src/DependencyInjection/Compiler/ProfilerPass.php
@@ -25,8 +25,6 @@
class ProfilerPass implements CompilerPassInterface
{
/**
- * {@inheritdoc}
- *
* @return void
*/
public function process(ContainerBuilder $container)
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 817ec084..ca4d3da1 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -30,10 +30,8 @@ public function __construct(bool $debug)
/**
* Generates the configuration tree builder.
- *
- * @return TreeBuilder The tree builder
*/
- public function getConfigTreeBuilder()
+ public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('bazinga_geocoder');
$rootNode = $treeBuilder->getRootNode();
diff --git a/src/Doctrine/ORM/GeocoderListener.php b/src/Doctrine/ORM/GeocoderListener.php
index 99bc52ce..070d3a82 100644
--- a/src/Doctrine/ORM/GeocoderListener.php
+++ b/src/Doctrine/ORM/GeocoderListener.php
@@ -36,8 +36,6 @@ public function __construct(Provider $geocoder, DriverInterface $driver)
}
/**
- * {@inheritdoc}
- *
* @return list
*/
public function getSubscribedEvents(): array
@@ -52,7 +50,7 @@ public function getSubscribedEvents(): array
*/
public function onFlush(OnFlushEventArgs $args)
{
- $em = $args->getEntityManager();
+ $em = method_exists($args, 'getObjectManager') ? $args->getObjectManager() : $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
diff --git a/src/Mapping/Driver/AnnotationDriver.php b/src/Mapping/Driver/AnnotationDriver.php
index eb675f0e..e2ae8498 100644
--- a/src/Mapping/Driver/AnnotationDriver.php
+++ b/src/Mapping/Driver/AnnotationDriver.php
@@ -30,9 +30,6 @@ public function __construct(Reader $reader)
$this->reader = $reader;
}
- /**
- * {@inheritdoc}
- */
public function isGeocodeable($object): bool
{
$reflection = ClassUtils::newReflectionObject($object);
@@ -40,9 +37,6 @@ public function isGeocodeable($object): bool
return (bool) $this->reader->getClassAnnotation($reflection, Annotations\Geocodeable::class);
}
- /**
- * {@inheritdoc}
- */
public function loadMetadataFromObject($object)
{
$reflection = ClassUtils::newReflectionObject($object);
diff --git a/src/Mapping/Driver/AttributeDriver.php b/src/Mapping/Driver/AttributeDriver.php
index 21c68612..2b1cbaaf 100644
--- a/src/Mapping/Driver/AttributeDriver.php
+++ b/src/Mapping/Driver/AttributeDriver.php
@@ -22,9 +22,6 @@
*/
final class AttributeDriver implements DriverInterface
{
- /**
- * {@inheritdoc}
- */
public function isGeocodeable($object): bool
{
if (PHP_VERSION_ID < 80000) {
@@ -37,8 +34,6 @@ public function isGeocodeable($object): bool
}
/**
- * {@inheritdoc}
- *
* @throws MappingException
*/
public function loadMetadataFromObject($object): ClassMetadata
diff --git a/src/ProviderFactory/AbstractFactory.php b/src/ProviderFactory/AbstractFactory.php
index 2fcf9d54..069ca660 100644
--- a/src/ProviderFactory/AbstractFactory.php
+++ b/src/ProviderFactory/AbstractFactory.php
@@ -32,7 +32,7 @@ abstract class AbstractFactory implements ProviderFactoryInterface
protected ?ClientInterface $httpClient;
- public function __construct(?ClientInterface $httpClient = null)
+ public function __construct(ClientInterface $httpClient = null)
{
$this->httpClient = $httpClient;
}
@@ -42,9 +42,6 @@ public function __construct(?ClientInterface $httpClient = null)
*/
abstract protected function getProvider(array $config): Provider;
- /**
- * {@inheritdoc}
- */
public function createProvider(array $options = []): Provider
{
$this->verifyDependencies();
@@ -56,9 +53,6 @@ public function createProvider(array $options = []): Provider
return $this->getProvider($config);
}
- /**
- * {@inheritdoc}
- */
public static function validate(array $options, $providerName)
{
static::verifyDependencies();
diff --git a/src/ProviderFactory/ChainFactory.php b/src/ProviderFactory/ChainFactory.php
index a01b81f1..77eb4404 100644
--- a/src/ProviderFactory/ChainFactory.php
+++ b/src/ProviderFactory/ChainFactory.php
@@ -42,9 +42,6 @@ protected function getProvider(array $config): Provider
return $provider;
}
- /**
- * {@inheritdoc}
- */
protected static function configureOptionResolver(OptionsResolver $resolver)
{
parent::configureOptionResolver($resolver);
diff --git a/src/Validator/Constraint/Address.php b/src/Validator/Constraint/Address.php
index c2e8fa21..36cfe125 100644
--- a/src/Validator/Constraint/Address.php
+++ b/src/Validator/Constraint/Address.php
@@ -26,15 +26,12 @@ class Address extends Constraint
{
public const INVALID_ADDRESS_ERROR = '2243aa07-2ea7-4eb7-962c-6a9586593f2c';
- /**
- * @var string[]
- */
protected const ERROR_NAMES = [
self::INVALID_ADDRESS_ERROR => 'INVALID_ADDRESS_ERROR',
];
/**
- * @var string[]
+ * @var array
*
* @deprecated since BazingaGeocoderBundle 5.17, use const ERROR_NAMES instead
*/
diff --git a/tests/DependencyInjection/Compiler/AddProvidersPassTest.php b/tests/DependencyInjection/Compiler/AddProvidersPassTest.php
index 9c95ef24..7144744d 100644
--- a/tests/DependencyInjection/Compiler/AddProvidersPassTest.php
+++ b/tests/DependencyInjection/Compiler/AddProvidersPassTest.php
@@ -15,7 +15,7 @@
use Bazinga\GeocoderBundle\DependencyInjection\Compiler\AddProvidersPass;
use Geocoder\Provider\BingMaps\BingMaps;
use Geocoder\ProviderAggregator;
-use Http\Client\Curl\Client;
+use Http\Mock\Client;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
diff --git a/tests/Doctrine/ORM/GeocoderListenerTest.php b/tests/Doctrine/ORM/GeocoderListenerTest.php
deleted file mode 100644
index 09b08130..00000000
--- a/tests/Doctrine/ORM/GeocoderListenerTest.php
+++ /dev/null
@@ -1,411 +0,0 @@
-
- */
-final class GeocoderListenerTest extends OrmTestCase
-{
- private EntityManager $em;
- private GeocoderListener $listener;
-
- public static function setUpBeforeClass(): void
- {
- parent::setUpBeforeClass();
-
- if (!class_exists(OrmTestCase::class)) {
- /*
- * We check for DoctrineTestCase because it is in the same package as OrmTestCase and we want to be able to
- * fake OrmTestCase
- */
- static::fail('Doctrine\Tests\OrmTestCase was not found.');
- }
- }
-
- protected function setUp(): void
- {
- parent::setUp();
-
- AnnotationRegistry::registerLoader('class_exists');
-
- $conn = DriverManager::getConnection([
- 'driver' => 'pdo_sqlite',
- 'memory' => true,
- ]);
-
- $this->em = $this->getTestEntityManager($conn);
-
- $reader = new SimpleAnnotationReader();
- $reader->addNamespace('Bazinga\GeocoderBundle\Mapping\Annotations');
- $reader->addNamespace('Doctrine\ORM\Mapping');
-
- $driver = new AnnotationDriver($reader);
- $geocoder = Nominatim::withOpenStreetMapServer(new Client(), 'BazingaGeocoderBundle/Test');
- $this->listener = new GeocoderListener($geocoder, $driver);
-
- $this->em->getEventManager()->addEventSubscriber($this->listener);
-
- $sm = new SchemaTool($this->em);
- $sm->createSchema([
- $this->em->getClassMetadata('Bazinga\GeocoderBundle\Tests\Doctrine\ORM\DummyWithProperty'),
- $this->em->getClassMetadata('Bazinga\GeocoderBundle\Tests\Doctrine\ORM\DummyWithEmptyProperty'),
- $this->em->getClassMetadata('Bazinga\GeocoderBundle\Tests\Doctrine\ORM\DummyWithGetter'),
- $this->em->getClassMetadata('Bazinga\GeocoderBundle\Tests\Doctrine\ORM\DummyWithInvalidGetter'),
- ]);
- }
-
- public function testPersistForProperty(): void
- {
- $dummy = new DummyWithProperty();
- $dummy->address = 'Berlin, Germany';
-
- $this->em->persist($dummy);
- $this->em->flush();
-
- self::assertNotNull($dummy->latitude);
- self::assertNotNull($dummy->longitude);
-
- $clone = clone $dummy;
- $dummy->address = 'Paris, France';
-
- $this->em->persist($dummy);
- $this->em->flush();
-
- self::assertNotEquals($clone->latitude, $dummy->latitude);
- self::assertNotEquals($clone->longitude, $dummy->longitude);
- }
-
- public function testPersistForGetter(): void
- {
- $dummy = new DummyWithGetter();
- $dummy->setAddress('Berlin, Germany');
-
- $this->em->persist($dummy);
- $this->em->flush();
-
- self::assertNotNull($dummy->getLatitude());
- self::assertNotNull($dummy->getLongitude());
-
- $clone = clone $dummy;
- $dummy->setAddress('Paris, France');
-
- $this->em->persist($dummy);
- $this->em->flush();
-
- self::assertNotEquals($clone->getLatitude(), $dummy->getLatitude());
- self::assertNotEquals($clone->getLongitude(), $dummy->getLongitude());
- }
-
- public function testPersistForInvalidGetter(): void
- {
- $dummy = new DummyWithInvalidGetter();
- $dummy->setAddress('Berlin, Germany');
-
- $this->em->persist($dummy);
-
- $this->expectException(\Exception::class);
-
- $this->em->flush();
- }
-
- public function testPersistForEmptyProperty(): void
- {
- $dummy = new DummyWithEmptyProperty();
- $dummy->address = '';
-
- $this->em->persist($dummy);
- $this->em->flush();
-
- self::assertNull($dummy->latitude);
- self::assertNull($dummy->longitude);
- }
-
- public function testDoesNotGeocodeIfAddressNotChanged(): void
- {
- $this->em->getEventManager()->removeEventListener(Events::onFlush, $this->listener);
-
- $reader = new SimpleAnnotationReader();
- $reader->addNamespace('Bazinga\GeocoderBundle\Mapping\Annotations');
- $reader->addNamespace('Doctrine\ORM\Mapping');
-
- $driver = new AnnotationDriver($reader);
-
- $client = new TrackedCurlClient();
- $geocoder = Nominatim::withOpenStreetMapServer($client, 'BazingaGeocoderBundle/Test');
- $listener = new GeocoderListener($geocoder, $driver);
-
- $this->em->getEventManager()->addEventSubscriber($listener);
-
- $dummy = new DummyWithProperty();
- $dummy->address = 'Frankfurt, Germany';
-
- $this->em->persist($dummy);
- $this->em->flush();
-
- $dummy->latitude = 0;
- $dummy->longitude = 0;
-
- $this->em->flush();
-
- self::assertSame('Frankfurt, Germany', $dummy->address);
- self::assertSame(0, $dummy->latitude);
- self::assertSame(0, $dummy->longitude);
- self::assertCount(1, $client->getResponses());
- }
-}
-
-/**
- * @Geocodeable
- *
- * @Entity
- */
-class DummyWithProperty
-{
- /**
- * @Id
- *
- * @GeneratedValue
- *
- * @Column(type="integer")
- */
- public $id;
-
- /**
- * @Latitude
- *
- * @Column()
- */
- public $latitude;
-
- /**
- * @Longitude
- *
- * @Column
- */
- public $longitude;
-
- /**
- * @Address
- *
- * @Column
- */
- public $address;
-}
-
-/**
- * @Geocodeable
- *
- * @Entity
- */
-class DummyWithGetter
-{
- /**
- * @Id @GeneratedValue
- *
- * @Column(type="integer")
- */
- private $id;
-
- /**
- * @Latitude
- *
- * @Column
- */
- private $latitude;
-
- /**
- * @Longitude
- *
- * @Column
- */
- private $longitude;
-
- /**
- * @Column
- */
- private $_address;
-
- public function setAddress($address)
- {
- $this->_address = $address;
- }
-
- /**
- * @Address
- */
- public function getAddress()
- {
- return $this->_address;
- }
-
- public function getLatitude()
- {
- return $this->latitude;
- }
-
- public function setLatitude($latitude)
- {
- $this->latitude = $latitude;
- }
-
- public function getLongitude()
- {
- return $this->longitude;
- }
-
- public function setLongitude($longitude)
- {
- $this->longitude = $longitude;
- }
-}
-
-/**
- * @Geocodeable
- *
- * @Entity
- */
-class DummyWithInvalidGetter
-{
- /**
- * @Id @GeneratedValue
- *
- * @Column(type="integer")
- */
- private $id;
-
- /**
- * @Latitude
- *
- * @Column
- */
- private $latitude;
-
- /**
- * @Longitude
- *
- * @Column
- */
- private $longitude;
-
- /**
- * @Column
- */
- private $_address;
-
- public function setAddress($address)
- {
- $this->_address = $address;
- }
-
- /**
- * @Address
- */
- public function getAddress($requiredParameter)
- {
- return $this->_address;
- }
-
- public function getLatitude()
- {
- return $this->latitude;
- }
-
- public function setLatitude($latitude)
- {
- $this->latitude = $latitude;
- }
-
- public function getLongitude()
- {
- return $this->longitude;
- }
-
- public function setLongitude($longitude)
- {
- $this->longitude = $longitude;
- }
-}
-
-/**
- * @Geocodeable
- *
- * @Entity
- */
-class DummyWithEmptyProperty
-{
- /**
- * @Id @GeneratedValue
- *
- * @Column(type="integer")
- */
- public $id;
-
- /**
- * @Latitude
- *
- * @Column(nullable=true)
- */
- public $latitude;
-
- /**
- * @Longitude
- *
- * @Column(nullable=true)
- */
- public $longitude;
-
- /**
- * @Address
- *
- * @Column
- */
- public $address;
-}
-
-class TrackedCurlClient extends Client
-{
- private $responses = [];
-
- public function sendRequest(RequestInterface $request): ResponseInterface
- {
- return $this->responses[] = parent::sendRequest($request);
- }
-
- public function getResponses(): array
- {
- return $this->responses;
- }
-}
diff --git a/tests/Functional/CustomTestKernel.php b/tests/Functional/CustomTestKernel.php
new file mode 100644
index 00000000..393172c9
--- /dev/null
+++ b/tests/Functional/CustomTestKernel.php
@@ -0,0 +1,244 @@
+shutdown();
+ $this->warmupDir = $warmupDir;
+ $this->boot();
+ }
+
+ /*
+ * Needed, otherwise the used cache is different on each kernel boot, which is a big issue in PluginInteractionTest
+ */
+ public function getCacheDir(): string
+ {
+ return realpath(sys_get_temp_dir()).'/NyholmBundleTest/cachePluginInteractionTest';
+ }
+
+ /**
+ * Returns the kernel parameters.
+ */
+ protected function getKernelParameters(): array
+ {
+ $bundles = [];
+ $bundlesMetadata = [];
+
+ foreach ($this->bundles as $name => $bundle) {
+ $bundles[$name] = \get_class($bundle);
+ $bundlesMetadata[$name] = [
+ 'path' => $bundle->getPath(),
+ 'namespace' => $bundle->getNamespace(),
+ ];
+ }
+
+ return [
+ 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(),
+ 'kernel.environment' => $this->environment,
+ 'kernel.runtime_environment' => '%env(default:kernel.environment:APP_RUNTIME_ENV)%',
+ 'kernel.debug' => $this->debug,
+ 'kernel.build_dir' => realpath($buildDir = $this->warmupDir ?: $this->getBuildDir()) ?: $buildDir,
+ 'kernel.cache_dir' => realpath($cacheDir = ($this->getCacheDir() === $this->getBuildDir() ? ($this->warmupDir ?: $this->getCacheDir()) : $this->getCacheDir())) ?: $cacheDir,
+ 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(),
+ 'kernel.bundles' => $bundles,
+ 'kernel.bundles_metadata' => $bundlesMetadata,
+ 'kernel.charset' => $this->getCharset(),
+ 'kernel.container_class' => $this->getContainerClass(),
+ ];
+ }
+
+ /**
+ * @internal
+ */
+ public function setAnnotatedClassCache(array $annotatedClasses)
+ {
+ file_put_contents(($this->warmupDir ?: $this->getBuildDir()).'/annotations.map', sprintf('getContainerClass();
+ $buildDir = $this->warmupDir ?: $this->getBuildDir();
+ $cache = new ConfigCache($buildDir.'/'.$class.'.php', $this->debug);
+ $cachePath = $cache->getPath();
+
+ // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
+ $errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
+
+ try {
+ if (false && \is_object($this->container = include $cachePath)
+ && (!$this->debug || (self::$freshCache[$cachePath] ?? $cache->isFresh()))
+ ) {
+ self::$freshCache[$cachePath] = true;
+ $this->container->set('kernel', $this);
+ error_reporting($errorLevel);
+
+ return;
+ }
+ } catch (\Throwable $e) {
+ }
+
+ $oldContainer = \is_object($this->container) ? new \ReflectionClass($this->container) : $this->container = null;
+
+ try {
+ is_dir($buildDir) ?: mkdir($buildDir, 0777, true);
+
+ if ($lock = fopen($cachePath.'.lock', 'w')) {
+ if (!flock($lock, \LOCK_EX | \LOCK_NB, $wouldBlock) && !flock($lock, $wouldBlock ? \LOCK_SH : \LOCK_EX)) {
+ fclose($lock);
+ $lock = null;
+ } elseif (true || !\is_object($this->container = include $cachePath)) {
+ $this->container = null;
+ } elseif (!$oldContainer || \get_class($this->container) !== $oldContainer->name) {
+ flock($lock, \LOCK_UN);
+ fclose($lock);
+ $this->container->set('kernel', $this);
+
+ return;
+ }
+ }
+ } catch (\Throwable $e) {
+ } finally {
+ error_reporting($errorLevel);
+ }
+
+ if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
+ $collectedLogs = [];
+ $previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
+ if (\E_USER_DEPRECATED !== $type && \E_DEPRECATED !== $type) {
+ return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
+ }
+
+ if (isset($collectedLogs[$message])) {
+ ++$collectedLogs[$message]['count'];
+
+ return null;
+ }
+
+ $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 5);
+ // Clean the trace by removing first frames added by the error handler itself.
+ for ($i = 0; isset($backtrace[$i]); ++$i) {
+ if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
+ $backtrace = \array_slice($backtrace, 1 + $i);
+ break;
+ }
+ }
+ for ($i = 0; isset($backtrace[$i]); ++$i) {
+ if (!isset($backtrace[$i]['file'], $backtrace[$i]['line'], $backtrace[$i]['function'])) {
+ continue;
+ }
+ if (!isset($backtrace[$i]['class']) && 'trigger_deprecation' === $backtrace[$i]['function']) {
+ $file = $backtrace[$i]['file'];
+ $line = $backtrace[$i]['line'];
+ $backtrace = \array_slice($backtrace, 1 + $i);
+ break;
+ }
+ }
+
+ // Remove frames added by DebugClassLoader.
+ for ($i = \count($backtrace) - 2; 0 < $i; --$i) {
+ if (\in_array($backtrace[$i]['class'] ?? null, [DebugClassLoader::class, LegacyDebugClassLoader::class], true)) {
+ $backtrace = [$backtrace[$i + 1]];
+ break;
+ }
+ }
+
+ $collectedLogs[$message] = [
+ 'type' => $type,
+ 'message' => $message,
+ 'file' => $file,
+ 'line' => $line,
+ 'trace' => [$backtrace[0]],
+ 'count' => 1,
+ ];
+
+ return null;
+ });
+ }
+
+ try {
+ $container = null;
+ $container = $this->buildContainer();
+ $container->compile();
+ } finally {
+ if ($collectDeprecations) {
+ restore_error_handler();
+
+ @file_put_contents($buildDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
+ @file_put_contents($buildDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
+ }
+ }
+
+ $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
+
+ if ($lock) {
+ flock($lock, \LOCK_UN);
+ fclose($lock);
+ }
+
+ $this->container = require $cachePath;
+ $this->container->set('kernel', $this);
+
+ if ($oldContainer && \get_class($this->container) !== $oldContainer->name) {
+ // Because concurrent requests might still be using them,
+ // old container files are not removed immediately,
+ // but on a next dump of the container.
+ static $legacyContainers = [];
+ $oldContainerDir = \dirname($oldContainer->getFileName());
+ $legacyContainers[$oldContainerDir.'.legacy'] = true;
+ foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy', \GLOB_NOSORT) as $legacyContainer) {
+ if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) {
+ (new Filesystem())->remove(substr($legacyContainer, 0, -7));
+ }
+ }
+
+ touch($oldContainerDir.'.legacy');
+ }
+
+ $preload = $this instanceof WarmableInterface ? (array) $this->warmUp($this->container->getParameter('kernel.cache_dir')) : [];
+
+ if ($this->container->has('cache_warmer')) {
+ $preload = array_merge($preload, (array) $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')));
+ }
+
+ if ($preload && method_exists(Preloader::class, 'append') && file_exists($preloadFile = $buildDir.'/'.$class.'.preload.php')) {
+ Preloader::append($preloadFile, $preload);
+ }
+ }
+}
diff --git a/tests/Functional/Fixtures/Entity/DummyWithEmptyProperty.php b/tests/Functional/Fixtures/Entity/DummyWithEmptyProperty.php
new file mode 100644
index 00000000..5431a311
--- /dev/null
+++ b/tests/Functional/Fixtures/Entity/DummyWithEmptyProperty.php
@@ -0,0 +1,70 @@
+_address = $address;
+ }
+
+ /**
+ * @Address
+ */
+ #[Address]
+ public function getAddress()
+ {
+ return $this->_address;
+ }
+
+ public function getLatitude()
+ {
+ return $this->latitude;
+ }
+
+ public function setLatitude($latitude)
+ {
+ $this->latitude = $latitude;
+ }
+
+ public function getLongitude()
+ {
+ return $this->longitude;
+ }
+
+ public function setLongitude($longitude)
+ {
+ $this->longitude = $longitude;
+ }
+}
diff --git a/tests/Functional/Fixtures/Entity/DummyWithInvalidGetter.php b/tests/Functional/Fixtures/Entity/DummyWithInvalidGetter.php
new file mode 100644
index 00000000..63d3e8ce
--- /dev/null
+++ b/tests/Functional/Fixtures/Entity/DummyWithInvalidGetter.php
@@ -0,0 +1,101 @@
+_address = $address;
+ }
+
+ /**
+ * @Address
+ */
+ #[Address]
+ public function getAddress($requiredParameter)
+ {
+ return $this->_address;
+ }
+
+ public function getLatitude()
+ {
+ return $this->latitude;
+ }
+
+ public function setLatitude($latitude)
+ {
+ $this->latitude = $latitude;
+ }
+
+ public function getLongitude()
+ {
+ return $this->longitude;
+ }
+
+ public function setLongitude($longitude)
+ {
+ $this->longitude = $longitude;
+ }
+}
diff --git a/tests/Functional/Fixtures/Entity/DummyWithProperty.php b/tests/Functional/Fixtures/Entity/DummyWithProperty.php
new file mode 100644
index 00000000..eb88e3ef
--- /dev/null
+++ b/tests/Functional/Fixtures/Entity/DummyWithProperty.php
@@ -0,0 +1,72 @@
+
+ */
+final class GeocoderListenerTest extends KernelTestCase
+{
+ protected function tearDown(): void
+ {
+ $em = self::getContainer()->get('doctrine.orm.entity_manager');
+
+ $tool = new SchemaTool($em);
+ $tool->dropSchema($em->getMetadataFactory()->getAllMetadata());
+ }
+
+ protected static function getKernelClass(): string
+ {
+ return TestKernel::class;
+ }
+
+ protected static function createKernel(array $options = []): KernelInterface
+ {
+ /**
+ * @var TestKernel $kernel
+ */
+ $kernel = parent::createKernel($options);
+ $kernel->addTestBundle(DoctrineBundle::class);
+ $kernel->addTestBundle(BazingaGeocoderBundle::class);
+ $kernel->addTestCompilerPass(new PublicServicePass('|[Bb]azinga:*|'));
+ $kernel->addTestCompilerPass(new PublicServicePass('|[gG]eocoder:*|'));
+ if (defined(ConnectionFactory::class.'::DEFAULT_SCHEME_MAP')) {
+ $kernel->addTestConfig(static function (ContainerBuilder $container) {
+ $container->prependExtensionConfig('doctrine', [
+ 'orm' => [
+ 'report_fields_where_declared' => true,
+ ],
+ ]);
+ });
+ }
+ $kernel->handleOptions($options);
+
+ return $kernel;
+ }
+
+ public function testPersistForProperty(): void
+ {
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener_'.(PHP_VERSION_ID >= 80000 ? 'php8' : 'php7').'.yml');
+ }]);
+
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $httpClient = $container->get(Client::class);
+ $httpClient->on(new RequestMatcher(), function (RequestInterface $request) {
+ if ('https://nominatim.openstreetmap.org/search?format=jsonv2&q=Berlin%2C%20Germany&addressdetails=1&extratags=1&limit=5' === (string) $request->getUri()) {
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::once())
+ ->method('getStatusCode')
+ ->willReturn(200);
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[{"place_id":159647018,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":62422,"lat":"52.5170365","lon":"13.3888599","category":"boundary","type":"administrative","place_rank":8,"importance":0.7875390282491362,"addresstype":"city","name":"Berlin","display_name":"Berlin, Deutschland","address":{"city":"Berlin","ISO3166-2-lvl4":"DE-BE","country":"Deutschland","country_code":"de"},"extratags":{"ele": "35", "email": "info@berlin.de", "place": "city", "capital": "yes", "website": "http://www.berlin.de", "de:place": "city", "ref:nuts": "DE3;DE30;DE300", "wikidata": "Q64", "wikipedia": "de:Berlin", "population": "3769962", "ref:LOCODE": "DEBER", "ref:nuts:1": "DE3", "ref:nuts:2": "DE30", "ref:nuts:3": "DE300", "state_code": "BE", "name:prefix": "Land und Kreisfreie Stadt", "linked_place": "city", "official_status": "Land", "contact:facebook": "http://www.facebook.com/Berlin", "name:prefix:city": "Kreisfreie Stadt", "openGeoDB:loc_id": "14356", "capital_ISO3166-1": "yes", "name:prefix:state": "Land", "source:population": "https://download.statistik-berlin-brandenburg.de/fa93e3bd19a2e885/a5ecfb2fff6a/SB_A01-05-00_2020h02_BE.pdf", "license_plate_code": "B", "official_status:de": "Land", "official_status:en": "State", "official_status:ru": "земля", "geographical_region": "Barnim;Berliner Urstromtal;Teltow;Nauener Platte", "blind:description:de": "Auf www.berlinfuerblinde.de gibt es einen kostenlosen Audioguide und weitere Informationen.", "de:regionalschluessel": "110000000000", "openGeoDB:postal_codes": "10178,10115,10117,10119,10179,10243,10245,10247,10249,10315,10317,10318,10319,10365,10367,10369,10405,10407,10409,10435,10437,10439,10551,10553,10555,10557,10559,10585,10587,10589,10623,10625,10627,10629,10707,10709,10711,10713,10715,10717,10719,10777,10", "report_problems:website": "https://ordnungsamt.berlin.de/", "TMC:cid_58:tabcd_1:Class": "Area", "openGeoDB:license_plate_code": "B", "TMC:cid_58:tabcd_1:LCLversion": "12.0", "openGeoDB:telephone_area_code": "030", "TMC:cid_58:tabcd_1:LocationCode": "266", "de:amtlicher_gemeindeschluessel": "11000000", "openGeoDB:community_identification_number": "11000000"},"boundingbox":["52.3382448","52.6755087","13.0883450","13.7611609"]}]');
+
+ return $response;
+ }
+
+ if ('https://nominatim.openstreetmap.org/search?format=jsonv2&q=Paris%2C%20France&addressdetails=1&extratags=1&limit=5' === (string) $request->getUri()) {
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::once())
+ ->method('getStatusCode')
+ ->willReturn(200);
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[{"place_id":115350921,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":7444,"lat":"48.8588897","lon":"2.3200410217200766","category":"boundary","type":"administrative","place_rank":15,"importance":0.8317101715588673,"addresstype":"suburb","name":"Paris","display_name":"Paris, Île-de-France, France métropolitaine, France","address":{"suburb":"Paris","city_district":"Paris","city":"Paris","ISO3166-2-lvl6":"FR-75","state":"Île-de-France","ISO3166-2-lvl4":"FR-IDF","region":"France métropolitaine","country":"France","country_code":"fr"},"extratags":{"capital": "yes", "wikidata": "Q90", "ref:INSEE": "75056", "wikipedia": "fr:Paris", "population": "2187526", "ref:FR:MGP": "T1", "source:population": "INSEE 2020"},"boundingbox":["48.8155755","48.9021560","2.2241220","2.4697602"]},{"place_id":114827617,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":71525,"lat":"48.8534951","lon":"2.3483915","category":"boundary","type":"administrative","place_rank":12,"importance":0.8317101715588673,"addresstype":"city","name":"Paris","display_name":"Paris, Île-de-France, France métropolitaine, France","address":{"city":"Paris","ISO3166-2-lvl6":"FR-75","state":"Île-de-France","ISO3166-2-lvl4":"FR-IDF","region":"France métropolitaine","country":"France","country_code":"fr"},"extratags":{"rank": "0", "capital": "yes", "ref:nuts": "FR101", "wikidata": "Q90", "ref:INSEE": "75", "wikipedia": "fr:Paris", "is_capital": "country", "population": "2165423", "ref:nuts:3": "FR101", "linked_place": "city", "source:name:oc": "ieo-bdtopoc", "contact:website": "http://www.paris.fr", "population:date": "2019", "capital_ISO3166-1": "yes", "source:population": "INSEE 2022"},"boundingbox":["48.8155755","48.9021560","2.2241220","2.4697602"]},{"place_id":114994164,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":1641193,"lat":"48.8588897","lon":"2.3200410217200766","category":"boundary","type":"administrative","place_rank":14,"importance":0.4283953917728152,"addresstype":"city_district","name":"Paris","display_name":"Paris, Île-de-France, France métropolitaine, France","address":{"city_district":"Paris","city":"Paris","ISO3166-2-lvl6":"FR-75","state":"Île-de-France","ISO3166-2-lvl4":"FR-IDF","region":"France métropolitaine","country":"France","country_code":"fr"},"extratags":{"wikidata": "Q2863958", "ref:INSEE": "751", "wikipedia": "fr:Arrondissement de Paris"},"boundingbox":["48.8155755","48.9021560","2.2241220","2.4697602"]}]');
+
+ return $response;
+ }
+
+ self::fail(sprintf('Unexpected http call "%s %s".', $request->getMethod(), (string) $request->getUri()));
+ });
+
+ $dummy = new DummyWithProperty();
+ $dummy->address = 'Berlin, Germany';
+
+ $em = $container->get('doctrine.orm.entity_manager');
+
+ $tool = new SchemaTool($em);
+ $tool->createSchema($em->getMetadataFactory()->getAllMetadata());
+
+ $em->persist($dummy);
+ $em->flush();
+
+ self::assertNotNull($dummy->latitude);
+ self::assertNotNull($dummy->longitude);
+
+ $clone = clone $dummy;
+ $dummy->address = 'Paris, France';
+
+ $em->persist($dummy);
+ $em->flush();
+
+ self::assertNotEquals($clone->latitude, $dummy->latitude);
+ self::assertNotEquals($clone->longitude, $dummy->longitude);
+ }
+
+ public function testPersistForGetter(): void
+ {
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener_'.(PHP_VERSION_ID >= 80000 ? 'php8' : 'php7').'.yml');
+ }]);
+
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $httpClient = $container->get(Client::class);
+ $httpClient->on(new RequestMatcher(), function (RequestInterface $request) {
+ if ('https://nominatim.openstreetmap.org/search?format=jsonv2&q=Berlin%2C%20Germany&addressdetails=1&extratags=1&limit=5' === (string) $request->getUri()) {
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::once())
+ ->method('getStatusCode')
+ ->willReturn(200);
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[{"place_id":159647018,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":62422,"lat":"52.5170365","lon":"13.3888599","category":"boundary","type":"administrative","place_rank":8,"importance":0.7875390282491362,"addresstype":"city","name":"Berlin","display_name":"Berlin, Deutschland","address":{"city":"Berlin","ISO3166-2-lvl4":"DE-BE","country":"Deutschland","country_code":"de"},"extratags":{"ele": "35", "email": "info@berlin.de", "place": "city", "capital": "yes", "website": "http://www.berlin.de", "de:place": "city", "ref:nuts": "DE3;DE30;DE300", "wikidata": "Q64", "wikipedia": "de:Berlin", "population": "3769962", "ref:LOCODE": "DEBER", "ref:nuts:1": "DE3", "ref:nuts:2": "DE30", "ref:nuts:3": "DE300", "state_code": "BE", "name:prefix": "Land und Kreisfreie Stadt", "linked_place": "city", "official_status": "Land", "contact:facebook": "http://www.facebook.com/Berlin", "name:prefix:city": "Kreisfreie Stadt", "openGeoDB:loc_id": "14356", "capital_ISO3166-1": "yes", "name:prefix:state": "Land", "source:population": "https://download.statistik-berlin-brandenburg.de/fa93e3bd19a2e885/a5ecfb2fff6a/SB_A01-05-00_2020h02_BE.pdf", "license_plate_code": "B", "official_status:de": "Land", "official_status:en": "State", "official_status:ru": "земля", "geographical_region": "Barnim;Berliner Urstromtal;Teltow;Nauener Platte", "blind:description:de": "Auf www.berlinfuerblinde.de gibt es einen kostenlosen Audioguide und weitere Informationen.", "de:regionalschluessel": "110000000000", "openGeoDB:postal_codes": "10178,10115,10117,10119,10179,10243,10245,10247,10249,10315,10317,10318,10319,10365,10367,10369,10405,10407,10409,10435,10437,10439,10551,10553,10555,10557,10559,10585,10587,10589,10623,10625,10627,10629,10707,10709,10711,10713,10715,10717,10719,10777,10", "report_problems:website": "https://ordnungsamt.berlin.de/", "TMC:cid_58:tabcd_1:Class": "Area", "openGeoDB:license_plate_code": "B", "TMC:cid_58:tabcd_1:LCLversion": "12.0", "openGeoDB:telephone_area_code": "030", "TMC:cid_58:tabcd_1:LocationCode": "266", "de:amtlicher_gemeindeschluessel": "11000000", "openGeoDB:community_identification_number": "11000000"},"boundingbox":["52.3382448","52.6755087","13.0883450","13.7611609"]}]');
+
+ return $response;
+ }
+
+ if ('https://nominatim.openstreetmap.org/search?format=jsonv2&q=Paris%2C%20France&addressdetails=1&extratags=1&limit=5' === (string) $request->getUri()) {
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::once())
+ ->method('getStatusCode')
+ ->willReturn(200);
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[{"place_id":115350921,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":7444,"lat":"48.8588897","lon":"2.3200410217200766","category":"boundary","type":"administrative","place_rank":15,"importance":0.8317101715588673,"addresstype":"suburb","name":"Paris","display_name":"Paris, Île-de-France, France métropolitaine, France","address":{"suburb":"Paris","city_district":"Paris","city":"Paris","ISO3166-2-lvl6":"FR-75","state":"Île-de-France","ISO3166-2-lvl4":"FR-IDF","region":"France métropolitaine","country":"France","country_code":"fr"},"extratags":{"capital": "yes", "wikidata": "Q90", "ref:INSEE": "75056", "wikipedia": "fr:Paris", "population": "2187526", "ref:FR:MGP": "T1", "source:population": "INSEE 2020"},"boundingbox":["48.8155755","48.9021560","2.2241220","2.4697602"]},{"place_id":114827617,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":71525,"lat":"48.8534951","lon":"2.3483915","category":"boundary","type":"administrative","place_rank":12,"importance":0.8317101715588673,"addresstype":"city","name":"Paris","display_name":"Paris, Île-de-France, France métropolitaine, France","address":{"city":"Paris","ISO3166-2-lvl6":"FR-75","state":"Île-de-France","ISO3166-2-lvl4":"FR-IDF","region":"France métropolitaine","country":"France","country_code":"fr"},"extratags":{"rank": "0", "capital": "yes", "ref:nuts": "FR101", "wikidata": "Q90", "ref:INSEE": "75", "wikipedia": "fr:Paris", "is_capital": "country", "population": "2165423", "ref:nuts:3": "FR101", "linked_place": "city", "source:name:oc": "ieo-bdtopoc", "contact:website": "http://www.paris.fr", "population:date": "2019", "capital_ISO3166-1": "yes", "source:population": "INSEE 2022"},"boundingbox":["48.8155755","48.9021560","2.2241220","2.4697602"]},{"place_id":114994164,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":1641193,"lat":"48.8588897","lon":"2.3200410217200766","category":"boundary","type":"administrative","place_rank":14,"importance":0.4283953917728152,"addresstype":"city_district","name":"Paris","display_name":"Paris, Île-de-France, France métropolitaine, France","address":{"city_district":"Paris","city":"Paris","ISO3166-2-lvl6":"FR-75","state":"Île-de-France","ISO3166-2-lvl4":"FR-IDF","region":"France métropolitaine","country":"France","country_code":"fr"},"extratags":{"wikidata": "Q2863958", "ref:INSEE": "751", "wikipedia": "fr:Arrondissement de Paris"},"boundingbox":["48.8155755","48.9021560","2.2241220","2.4697602"]}]');
+
+ return $response;
+ }
+
+ self::fail(sprintf('Unexpected http call "%s %s".', $request->getMethod(), (string) $request->getUri()));
+ });
+
+ $em = $container->get('doctrine.orm.entity_manager');
+
+ $tool = new SchemaTool($em);
+ $tool->createSchema($em->getMetadataFactory()->getAllMetadata());
+
+ $dummy = new DummyWithGetter();
+ $dummy->setAddress('Berlin, Germany');
+
+ $em->persist($dummy);
+ $em->flush();
+
+ self::assertNotNull($dummy->getLatitude());
+ self::assertNotNull($dummy->getLongitude());
+
+ $clone = clone $dummy;
+ $dummy->setAddress('Paris, France');
+
+ $em->persist($dummy);
+ $em->flush();
+
+ self::assertNotEquals($clone->getLatitude(), $dummy->getLatitude());
+ self::assertNotEquals($clone->getLongitude(), $dummy->getLongitude());
+ }
+
+ public function testPersistForInvalidGetter(): void
+ {
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener_'.(PHP_VERSION_ID >= 80000 ? 'php8' : 'php7').'.yml');
+ }]);
+
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $em = $container->get('doctrine.orm.entity_manager');
+
+ $tool = new SchemaTool($em);
+ $tool->createSchema($em->getMetadataFactory()->getAllMetadata());
+
+ $dummy = new DummyWithInvalidGetter();
+ $dummy->setAddress('Berlin, Germany');
+
+ $em->persist($dummy);
+
+ $this->expectException(\Exception::class);
+
+ $em->flush();
+ }
+
+ public function testPersistForEmptyProperty(): void
+ {
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener_'.(PHP_VERSION_ID >= 80000 ? 'php8' : 'php7').'.yml');
+ }]);
+
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $em = $container->get('doctrine.orm.entity_manager');
+
+ $tool = new SchemaTool($em);
+ $tool->createSchema($em->getMetadataFactory()->getAllMetadata());
+
+ $dummy = new DummyWithEmptyProperty();
+ $dummy->address = '';
+
+ $em->persist($dummy);
+ $em->flush();
+
+ self::assertNull($dummy->latitude);
+ self::assertNull($dummy->longitude);
+ }
+
+ public function testDoesNotGeocodeIfAddressNotChanged(): void
+ {
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener.yml');
+ $kernel->addTestConfig(__DIR__.'/config/listener_'.(PHP_VERSION_ID >= 80000 ? 'php8' : 'php7').'.yml');
+ }]);
+
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $httpRequests = 0;
+ $httpClient = $container->get(Client::class);
+ $httpClient->on(new RequestMatcher(), function (RequestInterface $request) use (&$httpRequests) {
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::any())
+ ->method('getStatusCode')
+ ->willReturn(200);
+
+ if ('https://nominatim.openstreetmap.org/search?format=jsonv2&q=Frankfurt%2C%20Germany&addressdetails=1&extratags=1&limit=5' === (string) $request->getUri() && 0 === $httpRequests) {
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[{"place_id":152571305,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":62400,"lat":"50.1106444","lon":"8.6820917","category":"boundary","type":"administrative","place_rank":12,"importance":0.6941325622496303,"addresstype":"city","name":"Frankfurt am Main","display_name":"Frankfurt am Main, Hessen, Deutschland","address":{"city":"Frankfurt am Main","state":"Hessen","ISO3166-2-lvl4":"DE-HE","country":"Deutschland","country_code":"de"},"extratags":{"ele": "112", "flag": "File:Flag of Frankfurt am Main.svg", "logo": "File:Frankfurt am Main logo.svg", "de:place": "city", "nickname": "Europastadt", "wikidata": "Q1794", "wikipedia": "de:Frankfurt am Main", "population": "701350", "ref:LOCODE": "DEFRA", "ref:nuts:3": "DE712", "border_type": "county", "name:prefix": "Stadt", "nickname:de": "Europastadt", "nickname:la": "Urbem Europaeam", "nickname:nl": "Bankfurt", "coat_of_arms": "File:Wappen Frankfurt am Main.svg", "linked_place": "city", "wikimedia_commons": "Category:Frankfurt am Main", "license_plate_code": "F", "de:regionalschluessel": "064120000000", "TMC:cid_58:tabcd_1:Class": "Area", "TMC:cid_58:tabcd_1:LCLversion": "9.00", "TMC:cid_58:tabcd_1:LocationCode": "414", "de:amtlicher_gemeindeschluessel": "06412000"},"boundingbox":["50.0153529","50.2271424","8.4727605","8.8004049"]},{"place_id":160849350,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":62523,"lat":"52.3412273","lon":"14.549452","category":"boundary","type":"administrative","place_rank":12,"importance":0.5626903004005709,"addresstype":"city","name":"Frankfurt (Oder)","display_name":"Frankfurt (Oder), Brandenburg, Deutschland","address":{"city":"Frankfurt (Oder)","state":"Brandenburg","ISO3166-2-lvl4":"DE-BB","country":"Deutschland","country_code":"de"},"extratags":{"ele": "28", "place": "city", "website": "https://www.frankfurt-oder.de/", "de:place": "city", "wikidata": "Q4024", "wikipedia": "de:Frankfurt (Oder)", "population": "61969", "ref:LOCODE": "DEFFO", "ref:nuts:3": "DE403", "name:prefix": "Kreisfreie Stadt", "linked_place": "town", "license_plate_code": "FF", "telephone_area_code": "0335", "de:regionalschluessel": "120530000000", "TMC:cid_58:tabcd_1:Class": "Area", "TMC:cid_58:tabcd_1:LCLversion": "8.00", "TMC:cid_58:tabcd_1:LocationCode": "415", "de:amtlicher_gemeindeschluessel": "12053000"},"boundingbox":["52.2528709","52.3980721","14.3948254","14.6013644"]}]');
+ } else {
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[]');
+ }
+
+ ++$httpRequests;
+
+ return $response;
+ });
+
+ $em = $container->get('doctrine.orm.entity_manager');
+
+ $tool = new SchemaTool($em);
+ $tool->createSchema($em->getMetadataFactory()->getAllMetadata());
+
+ $dummy = new DummyWithProperty();
+ $dummy->address = 'Frankfurt, Germany';
+
+ $em->persist($dummy);
+ $em->flush();
+
+ $dummy->latitude = 0;
+ $dummy->longitude = 0;
+
+ $em->flush();
+
+ self::assertSame('Frankfurt, Germany', $dummy->address);
+ self::assertSame(0, $dummy->latitude);
+ self::assertSame(0, $dummy->longitude);
+ self::assertSame(1, $httpRequests);
+ }
+}
diff --git a/tests/Functional/Helper/CacheHelperV8.php b/tests/Functional/Helper/CacheHelperV8.php
index 5c3a40e0..a8fd141d 100644
--- a/tests/Functional/Helper/CacheHelperV8.php
+++ b/tests/Functional/Helper/CacheHelperV8.php
@@ -23,7 +23,7 @@ public function get(string $key, mixed $default = null): mixed
{
}
- public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool
+ public function set(string $key, mixed $value, int|\DateInterval $ttl = null): bool
{
return true;
}
@@ -43,7 +43,7 @@ public function getMultiple(iterable $keys, mixed $default = null): iterable
return [];
}
- public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool
+ public function setMultiple(iterable $values, int|\DateInterval $ttl = null): bool
{
return true;
}
diff --git a/tests/Functional/PluginInteractionTest.php b/tests/Functional/PluginInteractionTest.php
new file mode 100644
index 00000000..2164cd6b
--- /dev/null
+++ b/tests/Functional/PluginInteractionTest.php
@@ -0,0 +1,76 @@
+addTestBundle(BazingaGeocoderBundle::class);
+ $kernel->addTestCompilerPass(new PublicServicePass('|[Bb]azinga:*|'));
+ $kernel->addTestCompilerPass(new PublicServicePass('|[gG]eocoder:*|'));
+ $kernel->handleOptions($options);
+
+ return $kernel;
+ }
+
+ public function testCachePluginUsesIpFromFakeIpPlugin(): void
+ {
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->setClearCacheAfterShutdown(false);
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/cache_symfony.yml');
+ $kernel->addTestConfig(__DIR__.'/config/geo_plugin_fakeip_with_cache_cn.yml');
+ }]);
+ $kernel->setClearCacheAfterShutdown(false);
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $geoPluginGeocoder = $container->get('bazinga_geocoder.provider.geoPlugin');
+ $result = $geoPluginGeocoder->geocodeQuery(GeocodeQuery::create('::1'));
+ $country = $result->first()->getCountry()->getCode();
+ self::assertEquals('CN', $country);
+
+ $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
+ $kernel->setClearCacheAfterShutdown(false);
+ $kernel->addTestConfig(__DIR__.'/config/framework.yml');
+ $kernel->addTestConfig(__DIR__.'/config/cache_symfony.yml');
+ $kernel->addTestConfig(__DIR__.'/config/geo_plugin_fakeip_with_cache_fr.yml');
+ }]);
+ $kernel->setClearCacheAfterShutdown(false);
+ $container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();
+
+ $geoPluginGeocoder = $container->get('bazinga_geocoder.provider.geoPlugin');
+ $result = $geoPluginGeocoder->geocodeQuery(GeocodeQuery::create('::1'));
+ $country = $result->first()->getCountry()->getCode();
+ self::assertEquals('FR', $country);
+ }
+}
diff --git a/tests/Functional/config/cache_symfony.yml b/tests/Functional/config/cache_symfony.yml
new file mode 100644
index 00000000..419c11d6
--- /dev/null
+++ b/tests/Functional/config/cache_symfony.yml
@@ -0,0 +1,12 @@
+framework:
+ cache:
+ app: cache.adapter.filesystem
+ system: cache.adapter.system
+ pools:
+ app.cache.geoPlugin:
+ adapter: cache.app
+ default_lifetime: 600
+services:
+ app.simple_cache:
+ class: Symfony\Component\Cache\Psr16Cache
+ arguments: ['@app.cache.geoPlugin']
\ No newline at end of file
diff --git a/tests/Functional/config/deprecated_httplug_client_option.yml b/tests/Functional/config/deprecated_httplug_client_option.yml
index 6864ca3f..c52ac75b 100644
--- a/tests/Functional/config/deprecated_httplug_client_option.yml
+++ b/tests/Functional/config/deprecated_httplug_client_option.yml
@@ -5,8 +5,8 @@ bazinga_geocoder:
acme:
factory: Bazinga\GeocoderBundle\ProviderFactory\GoogleMapsFactory
options:
- httplug_client: '@test_http_client'
+ httplug_client: '@test_http_client'
services:
- test_http_client:
- class: Http\Client\Curl\Client
+ test_http_client:
+ class: Http\Client\Curl\Client
diff --git a/tests/Functional/config/framework.yml b/tests/Functional/config/framework.yml
index 26f9bb5a..d745002e 100644
--- a/tests/Functional/config/framework.yml
+++ b/tests/Functional/config/framework.yml
@@ -1,2 +1,8 @@
framework:
http_method_override: false
+ secret: 6825c711ef47cfc1530d322b62adac3e2c43844c
+ session:
+ storage_factory_id: session.storage.factory.mock_file
+ cookie_secure: auto
+ cookie_samesite: lax
+ handler_id: null
diff --git a/tests/Functional/config/geo_plugin_fakeip_with_cache_cn.yml b/tests/Functional/config/geo_plugin_fakeip_with_cache_cn.yml
new file mode 100644
index 00000000..6bf704cc
--- /dev/null
+++ b/tests/Functional/config/geo_plugin_fakeip_with_cache_cn.yml
@@ -0,0 +1,14 @@
+# See the docs at https://github.com/geocoder-php/BazingaGeocoderBundle
+bazinga_geocoder:
+ # The local IP (127.0.0.1) will be replaced by the fake_ip
+ # see https://github.com/geocoder-php/BazingaGeocoderBundle/blob/5.0.0/Resources/doc/index.md#fake-local-ip
+ fake_ip:
+ local_ip: ::1
+ ip: 123.123.123.128
+ # this ip is in china
+ providers:
+ geoPlugin:
+ factory: Bazinga\GeocoderBundle\ProviderFactory\GeoPluginFactory
+ cache: 'app.simple_cache'
+ cache_lifetime: 42
+ cache_precision: ~
\ No newline at end of file
diff --git a/tests/Functional/config/geo_plugin_fakeip_with_cache_fr.yml b/tests/Functional/config/geo_plugin_fakeip_with_cache_fr.yml
new file mode 100644
index 00000000..f0b4b321
--- /dev/null
+++ b/tests/Functional/config/geo_plugin_fakeip_with_cache_fr.yml
@@ -0,0 +1,14 @@
+# See the docs at https://github.com/geocoder-php/BazingaGeocoderBundle
+bazinga_geocoder:
+ # The local IP (127.0.0.1) will be replaced by the fake_ip
+ # see https://github.com/geocoder-php/BazingaGeocoderBundle/blob/5.0.0/Resources/doc/index.md#fake-local-ip
+ fake_ip:
+ local_ip: ::1
+ ip: 87.98.128.10
+ # this ip is in france
+ providers:
+ geoPlugin:
+ factory: Bazinga\GeocoderBundle\ProviderFactory\GeoPluginFactory
+ cache: 'app.simple_cache'
+ cache_lifetime: 42
+ cache_precision: ~
\ No newline at end of file
diff --git a/tests/Functional/config/listener.yml b/tests/Functional/config/listener.yml
new file mode 100644
index 00000000..6e6aa968
--- /dev/null
+++ b/tests/Functional/config/listener.yml
@@ -0,0 +1,11 @@
+bazinga_geocoder:
+ profiling:
+ enabled: false
+ providers:
+ acme:
+ factory: Bazinga\GeocoderBundle\ProviderFactory\NominatimFactory
+ options:
+ http_client: '@Http\Mock\Client'
+
+services:
+ Http\Mock\Client: ~
diff --git a/tests/Functional/config/listener_php7.yml b/tests/Functional/config/listener_php7.yml
new file mode 100644
index 00000000..7d6903ec
--- /dev/null
+++ b/tests/Functional/config/listener_php7.yml
@@ -0,0 +1,43 @@
+doctrine:
+ dbal:
+ default_connection: default
+ connections:
+ default:
+ driver: pdo_sqlite
+ path: '%kernel.cache_dir%/test.sqlite'
+ orm:
+ auto_generate_proxy_classes: true
+ naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
+ auto_mapping: true
+ mappings:
+ App:
+ is_bundle: false
+ type: annotation
+ dir: '%kernel.project_dir%/tests/Functional/Fixtures/Entity'
+ prefix: 'Bazinga\GeocoderBundle\Tests\Functional\Fixtures\Entity'
+ alias: App
+
+bazinga_geocoder:
+ profiling:
+ enabled: false
+ providers:
+ acme:
+ factory: Bazinga\GeocoderBundle\ProviderFactory\NominatimFactory
+ options:
+ http_client: '@Http\Mock\Client'
+ root_url: 'https://nominatim.openstreetmap.org'
+ user_agent: 'geocoder-php test_suite'
+
+services:
+ Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver:
+ class: Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver
+ arguments:
+ - '@annotations.reader'
+
+ Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener:
+ class: Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener
+ arguments:
+ - '@bazinga_geocoder.provider.acme'
+ - '@Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver'
+ tags:
+ - { name: doctrine.event_listener, event: onFlush }
diff --git a/tests/Functional/config/listener_php8.yml b/tests/Functional/config/listener_php8.yml
new file mode 100644
index 00000000..90efa681
--- /dev/null
+++ b/tests/Functional/config/listener_php8.yml
@@ -0,0 +1,42 @@
+doctrine:
+ dbal:
+ default_connection: default
+ connections:
+ default:
+ driver: pdo_sqlite
+ path: '%kernel.cache_dir%/test.sqlite'
+ orm:
+ auto_generate_proxy_classes: true
+ validate_xml_mapping: true
+ report_fields_where_declared: true
+ naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
+ auto_mapping: true
+ mappings:
+ App:
+ is_bundle: false
+ type: attribute
+ dir: '%kernel.project_dir%/tests/Functional/Fixtures/Entity'
+ prefix: 'Bazinga\GeocoderBundle\Tests\Functional\Fixtures\Entity'
+ alias: App
+
+bazinga_geocoder:
+ profiling:
+ enabled: false
+ providers:
+ acme:
+ factory: Bazinga\GeocoderBundle\ProviderFactory\NominatimFactory
+ options:
+ http_client: '@Http\Mock\Client'
+ root_url: 'https://nominatim.openstreetmap.org'
+ user_agent: 'geocoder-php test_suite'
+
+services:
+ Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver: ~
+
+ Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener:
+ class: Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener
+ arguments:
+ - '@bazinga_geocoder.provider.acme'
+ - '@Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver'
+ tags:
+ - { name: doctrine.event_listener, event: onFlush }
diff --git a/tests/Mapping/Driver/AnnotationDriverTest.php b/tests/Mapping/Driver/AnnotationDriverTest.php
index 818bbb56..d90de53c 100644
--- a/tests/Mapping/Driver/AnnotationDriverTest.php
+++ b/tests/Mapping/Driver/AnnotationDriverTest.php
@@ -14,9 +14,9 @@
use Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver;
use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;
-use Doctrine\Common\Annotations\AnnotationRegistry;
-use Doctrine\Common\Annotations\Reader;
-use Doctrine\Common\Annotations\SimpleAnnotationReader;
+use Bazinga\GeocoderBundle\Tests\Mapping\Driver\Fixtures\Dummy;
+use Bazinga\GeocoderBundle\Tests\Mapping\Driver\Fixtures\Dummy2;
+use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
/**
@@ -25,16 +25,10 @@
final class AnnotationDriverTest extends TestCase
{
private AnnotationDriver $driver;
- private Reader $reader;
protected function setUp(): void
{
- AnnotationRegistry::registerLoader('class_exists');
-
- $this->reader = new SimpleAnnotationReader();
- $this->reader->addNamespace('Bazinga\GeocoderBundle\Mapping\Annotations');
-
- $this->driver = new AnnotationDriver($this->reader);
+ $this->driver = new AnnotationDriver(new AnnotationReader());
}
public function testLoadMetadata(): void
@@ -60,28 +54,3 @@ public function testIsGeocodable(): void
self::assertTrue($this->driver->isGeocodeable(new Dummy()));
}
}
-
-/**
- * @Geocodeable
- */
-class Dummy
-{
- /**
- * @Latitude
- */
- public $latitude;
-
- /**
- * @Longitude
- */
- public $longitude;
-
- /**
- * @Address
- */
- public $address;
-}
-
-class Dummy2
-{
-}
diff --git a/tests/Mapping/Driver/AttributeDriverTest.php b/tests/Mapping/Driver/AttributeDriverTest.php
index 1ad68f08..0df14500 100644
--- a/tests/Mapping/Driver/AttributeDriverTest.php
+++ b/tests/Mapping/Driver/AttributeDriverTest.php
@@ -12,12 +12,10 @@
namespace Bazinga\GeocoderBundle\Tests\Mapping\Driver;
-use Bazinga\GeocoderBundle\Mapping\Annotations\Address;
-use Bazinga\GeocoderBundle\Mapping\Annotations\Geocodeable;
-use Bazinga\GeocoderBundle\Mapping\Annotations\Latitude;
-use Bazinga\GeocoderBundle\Mapping\Annotations\Longitude;
use Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver;
use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;
+use Bazinga\GeocoderBundle\Tests\Mapping\Driver\Fixtures\Dummy;
+use Bazinga\GeocoderBundle\Tests\Mapping\Driver\Fixtures\Dummy2;
use PHPUnit\Framework\TestCase;
/**
@@ -44,7 +42,7 @@ protected function setUp(): void
*/
public function testLoadMetadata(): void
{
- $obj = new Dummy3();
+ $obj = new Dummy();
$metadata = $this->driver->loadMetadataFromObject($obj);
self::assertInstanceOf('ReflectionProperty', $metadata->addressProperty);
@@ -58,9 +56,9 @@ public function testLoadMetadata(): void
public function testLoadMetadataFromWrongObject(): void
{
$this->expectException(MappingException::class);
- $this->expectExceptionMessage('The class '.Dummy4::class.' is not geocodeable');
+ $this->expectExceptionMessage('The class '.Dummy2::class.' is not geocodeable');
- $this->driver->loadMetadataFromObject(new Dummy4());
+ $this->driver->loadMetadataFromObject(new Dummy2());
}
/**
@@ -68,23 +66,6 @@ public function testLoadMetadataFromWrongObject(): void
*/
public function testIsGeocodable(): void
{
- self::assertTrue($this->driver->isGeocodeable(new Dummy3()));
+ self::assertTrue($this->driver->isGeocodeable(new Dummy()));
}
}
-
-#[Geocodeable()]
-class Dummy3
-{
- #[Latitude()]
- public $latitude;
-
- #[Longitude()]
- public $longitude;
-
- #[Address()]
- public $address;
-}
-
-class Dummy4
-{
-}
diff --git a/tests/Mapping/Driver/Fixtures/Dummy.php b/tests/Mapping/Driver/Fixtures/Dummy.php
new file mode 100644
index 00000000..79a7391d
--- /dev/null
+++ b/tests/Mapping/Driver/Fixtures/Dummy.php
@@ -0,0 +1,43 @@
+on($requestMatcher, function (RequestInterface $request) {
+ switch ((string) $request->getUri()) {
+ case 'https://nominatim.openstreetmap.org/search?format=jsonv2&q=Berlin%2C%20Germany&addressdetails=1&extratags=1&limit=5':
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::once())
+ ->method('getStatusCode')
+ ->willReturn(200);
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[{"place_id":159647018,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":62422,"lat":"52.5170365","lon":"13.3888599","category":"boundary","type":"administrative","place_rank":8,"importance":0.7875390282491362,"addresstype":"city","name":"Berlin","display_name":"Berlin, Deutschland","address":{"city":"Berlin","ISO3166-2-lvl4":"DE-BE","country":"Deutschland","country_code":"de"},"extratags":{"ele": "35", "email": "info@berlin.de", "place": "city", "capital": "yes", "website": "http://www.berlin.de", "de:place": "city", "ref:nuts": "DE3;DE30;DE300", "wikidata": "Q64", "wikipedia": "de:Berlin", "population": "3769962", "ref:LOCODE": "DEBER", "ref:nuts:1": "DE3", "ref:nuts:2": "DE30", "ref:nuts:3": "DE300", "state_code": "BE", "name:prefix": "Land und Kreisfreie Stadt", "linked_place": "city", "official_status": "Land", "contact:facebook": "http://www.facebook.com/Berlin", "name:prefix:city": "Kreisfreie Stadt", "openGeoDB:loc_id": "14356", "capital_ISO3166-1": "yes", "name:prefix:state": "Land", "source:population": "https://download.statistik-berlin-brandenburg.de/fa93e3bd19a2e885/a5ecfb2fff6a/SB_A01-05-00_2020h02_BE.pdf", "license_plate_code": "B", "official_status:de": "Land", "official_status:en": "State", "official_status:ru": "земля", "geographical_region": "Barnim;Berliner Urstromtal;Teltow;Nauener Platte", "blind:description:de": "Auf www.berlinfuerblinde.de gibt es einen kostenlosen Audioguide und weitere Informationen.", "de:regionalschluessel": "110000000000", "openGeoDB:postal_codes": "10178,10115,10117,10119,10179,10243,10245,10247,10249,10315,10317,10318,10319,10365,10367,10369,10405,10407,10409,10435,10437,10439,10551,10553,10555,10557,10559,10585,10587,10589,10623,10625,10627,10629,10707,10709,10711,10713,10715,10717,10719,10777,10", "report_problems:website": "https://ordnungsamt.berlin.de/", "TMC:cid_58:tabcd_1:Class": "Area", "openGeoDB:license_plate_code": "B", "TMC:cid_58:tabcd_1:LCLversion": "12.0", "openGeoDB:telephone_area_code": "030", "TMC:cid_58:tabcd_1:LocationCode": "266", "de:amtlicher_gemeindeschluessel": "11000000", "openGeoDB:community_identification_number": "11000000"},"boundingbox":["52.3382448","52.6755087","13.0883450","13.7611609"]}]');
+
+ return $response;
+ case 'https://nominatim.openstreetmap.org/search?format=jsonv2&q=Bifrost%2C%20Nine%20Realms&addressdetails=1&extratags=1&limit=5':
+ $response = $this->createMock(ResponseInterface::class);
+ $response->expects(self::once())
+ ->method('getStatusCode')
+ ->willReturn(200);
+ $response->expects(self::once())
+ ->method('getBody')
+ ->willReturn('[]');
+
+ return $response;
+ }
+
+ self::fail(sprintf('Unexpected http call "%s %s".', $request->getMethod(), (string) $request->getUri()));
+ });
+
+ $geocoder = Nominatim::withOpenStreetMapServer($httpClient, 'BazingaGeocoderBundle/Test');
return new AddressValidator($geocoder);
}