Skip to content

Commit df4b6e3

Browse files
authored
Implement PluginProvider and awesome plugin support (#150)
* First look how to add FakeIpPlugin * Applied changes from StyleCI * Typo * Adding plugin support * Adding PluginProvider * Applied changes from StyleCI * Fixed the tests * Applied changes from StyleCI * Added docs * Fixed #151
1 parent 6c4efec commit df4b6e3

File tree

18 files changed

+353
-170
lines changed

18 files changed

+353
-170
lines changed

Changelog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Version 5 does only support Symfony 3.3+ and PHP7. We dropped some complexity an
1010

1111
- Support for Geocoder 4.0
1212
- Provider factories
13+
- Support for plugins
1314

1415
### Changed
1516

@@ -23,4 +24,4 @@ Version 5 does only support Symfony 3.3+ and PHP7. We dropped some complexity an
2324

2425
## Version 4.1.0
2526

26-
No changelog before this version
27+
No changelog before this version

DataCollector/GeocoderDataCollector.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Bazinga\GeocoderBundle\DataCollector;
1212

13+
use Bazinga\GeocoderBundle\Plugin\ProfilingPlugin;
1314
use Symfony\Component\HttpFoundation\Request;
1415
use Symfony\Component\HttpFoundation\Response;
1516
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
@@ -20,7 +21,7 @@
2021
class GeocoderDataCollector extends DataCollector
2122
{
2223
/**
23-
* @var ProfilingProvider[]
24+
* @var ProfilingPlugin[]
2425
*/
2526
private $instances = [];
2627

@@ -35,6 +36,10 @@ public function __construct()
3536
*/
3637
public function collect(Request $request, Response $response, \Exception $exception = null)
3738
{
39+
if (!empty($this->data['queries'])) {
40+
// To avoid collection more that once.
41+
return;
42+
}
3843
foreach ($this->instances as $instance) {
3944
foreach ($instance->getQueries() as $query) {
4045
$query['query'] = $this->cloneVar($query['query']);
@@ -90,9 +95,9 @@ public function getProviderQueries($provider): array
9095
}
9196

9297
/**
93-
* @param ProfilingProvider $instance
98+
* @param ProfilingPlugin $instance
9499
*/
95-
public function addInstance(ProfilingProvider $instance)
100+
public function addInstance(ProfilingPlugin $instance)
96101
{
97102
$this->instances[] = $instance;
98103
$this->data['providers'][] = $instance->getName();

DependencyInjection/BazingaGeocoderExtension.php

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,17 @@
1010

1111
namespace Bazinga\GeocoderBundle\DependencyInjection;
1212

13-
use Bazinga\GeocoderBundle\EventListener\FakeRequestListener;
13+
use Bazinga\GeocoderBundle\DataCollector\GeocoderDataCollector;
14+
use Bazinga\GeocoderBundle\Plugin\FakeIpPlugin;
15+
use Bazinga\GeocoderBundle\Plugin\ProfilingPlugin;
16+
use Bazinga\GeocoderBundle\ProviderFactory\PluginProviderFactory;
1417
use Bazinga\GeocoderBundle\ProviderFactory\ProviderFactoryInterface;
18+
use Geocoder\Plugin\Plugin\CachePlugin;
19+
use Geocoder\Plugin\Plugin\LimitPlugin;
20+
use Geocoder\Plugin\Plugin\LocalePlugin;
21+
use Geocoder\Plugin\Plugin\LoggerPlugin;
22+
use Geocoder\Plugin\PluginProvider;
1523
use Geocoder\Provider\Cache\ProviderCache;
16-
use Geocoder\Provider\Provider;
1724
use Symfony\Component\Config\Definition\Processor;
1825
use Symfony\Component\Config\FileLocator;
1926
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -38,41 +45,111 @@ public function load(array $configs, ContainerBuilder $container)
3845
if (true === $config['profiling']['enabled']) {
3946
$loader->load('profiling.yml');
4047
}
41-
$this->loadProviders($container, $config);
4248

4349
if ($config['fake_ip']['enabled']) {
44-
$definition = $container->getDefinition(FakeRequestListener::class);
45-
$definition->replaceArgument(0, $config['fake_ip']['ip']);
50+
$definition = $container->getDefinition(FakeIpPlugin::class);
51+
$definition->replaceArgument(1, $config['fake_ip']['ip']);
4652
} else {
47-
$container->removeDefinition(FakeRequestListener::class);
53+
$container->removeDefinition(FakeIpPlugin::class);
4854
}
55+
56+
$this->loadProviders($container, $config);
4957
}
5058

5159
private function loadProviders(ContainerBuilder $container, array $config)
5260
{
5361
foreach ($config['providers'] as $providerName => $providerConfig) {
5462
$factoryService = $container->getDefinition($providerConfig['factory']);
5563
$factoryClass = $factoryService->getClass() ?: $providerConfig['factory'];
56-
if (!(is_a($factoryClass, ProviderFactoryInterface::class))) {
57-
//throw new \LogicException(sprintf('Provider factory "%s" must implement ProviderFactoryInterface', $providerConfig['factory']));
64+
if (!class_implements($factoryClass, ProviderFactoryInterface::class)) {
65+
throw new \LogicException(sprintf('Provider factory "%s" must implement ProviderFactoryInterface', $providerConfig['factory']));
5866
}
5967
$factoryClass::validate($providerConfig['options'], $providerName);
6068

6169
// See if any option has a service reference
6270
$providerConfig['options'] = $this->findReferences($providerConfig['options']);
6371

6472
$serviceId = 'bazinga_geocoder.provider.'.$providerName;
65-
$def = $container->register($serviceId, Provider::class);
66-
$def->setFactory([new Reference($providerConfig['factory']), 'createProvider'])
73+
$plugins = $this->configureProviderPlugins($container, $providerConfig, $serviceId);
74+
75+
$def = $container->register($serviceId, PluginProvider::class)
76+
->setFactory([PluginProviderFactory::class, 'createPluginProvider'])
77+
->addArgument($plugins)
78+
->addArgument(new Reference($providerConfig['factory']))
6779
->addArgument($providerConfig['options']);
6880

6981
$def->addTag('bazinga_geocoder.provider');
7082
foreach ($providerConfig['aliases'] as $alias) {
7183
$container->setAlias($alias, $serviceId);
7284
}
85+
}
86+
}
87+
88+
/**
89+
* Configure plugins for a client.
90+
*
91+
* @param ContainerBuilder $container
92+
* @param array $config
93+
* @param string $providerServiceId
94+
*
95+
* @return array
96+
*/
97+
public function configureProviderPlugins(ContainerBuilder $container, array $config, string $providerServiceId): array
98+
{
99+
$plugins = [];
100+
foreach ($config['plugins'] as $plugin) {
101+
$plugins[] = $plugin['id'];
102+
}
103+
104+
if (isset($config['cache']) || isset($config['cache_lifetime'])) {
105+
if (null === $cacheServiceId = $config['cache']) {
106+
if (!$container->has('app.cache')) {
107+
throw new \LogicException('You need to specify a service for cache.');
108+
}
109+
$cacheServiceId = 'app.cache';
110+
}
111+
$plugins[] = $providerServiceId.'.cache';
112+
$container->register($providerServiceId.'.cache', CachePlugin::class)
113+
->setPublic(false)
114+
->setArguments([new Reference($cacheServiceId), (int) $config['cache_lifetime']]);
115+
}
73116

74-
$this->configureCache($container, $serviceId, $providerConfig);
117+
if (isset($config['limit'])) {
118+
$plugins[] = $providerServiceId.'.limit';
119+
$container->register($providerServiceId.'.limit', LimitPlugin::class)
120+
->setPublic(false)
121+
->setArguments([(int) $config['limit']]);
75122
}
123+
124+
if (isset($config['locale'])) {
125+
$plugins[] = $providerServiceId.'.locale';
126+
$container->register($providerServiceId.'.locale', LocalePlugin::class)
127+
->setPublic(false)
128+
->setArguments([$config['locale']]);
129+
}
130+
131+
if (isset($config['logger'])) {
132+
$plugins[] = $providerServiceId.'.logger';
133+
$container->register($providerServiceId.'.logger', LoggerPlugin::class)
134+
->setPublic(false)
135+
->setArguments([new Reference($config['logger'])]);
136+
}
137+
138+
if ($container->has(FakeIpPlugin::class)) {
139+
$plugins[] = FakeIpPlugin::class;
140+
}
141+
142+
if ($container->has(GeocoderDataCollector::class)) {
143+
$plugins[] = $providerServiceId.'.profiler';
144+
$container->register($providerServiceId.'.profiler', ProfilingPlugin::class)
145+
->setPublic(false)
146+
->setArguments([substr($providerServiceId, strlen('bazinga_geocoder.provider.'))])
147+
->addTag('bazinga_geocoder.profiling_plugin');
148+
}
149+
150+
return array_map(function (string $id) {
151+
return new Reference($id);
152+
}, $plugins);
76153
}
77154

78155
/**

DependencyInjection/Compiler/AddProvidersPass.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Bazinga\GeocoderBundle\DependencyInjection\Compiler;
1212

13+
use Geocoder\ProviderAggregator;
1314
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516
use Symfony\Component\DependencyInjection\Reference;
@@ -32,7 +33,7 @@ class AddProvidersPass implements CompilerPassInterface
3233
*/
3334
public function process(ContainerBuilder $container)
3435
{
35-
if (!$container->hasDefinition('Geocoder\\ProviderAggregator')) {
36+
if (!$container->hasDefinition(ProviderAggregator::class)) {
3637
return;
3738
}
3839

@@ -41,7 +42,7 @@ public function process(ContainerBuilder $container)
4142
$providers[] = new Reference($providerId);
4243
}
4344

44-
$geocoderDefinition = $container->getDefinition('Geocoder\\ProviderAggregator');
45+
$geocoderDefinition = $container->getDefinition(ProviderAggregator::class);
4546
$geocoderDefinition->addMethodCall('registerProviders', [$providers]);
4647
}
4748
}

DependencyInjection/Compiler/ProfilerPass.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
namespace Bazinga\GeocoderBundle\DependencyInjection\Compiler;
1212

1313
use Bazinga\GeocoderBundle\DataCollector\GeocoderDataCollector;
14-
use Bazinga\GeocoderBundle\DataCollector\ProfilingProvider;
1514
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1615
use Symfony\Component\DependencyInjection\ContainerBuilder;
1716
use Symfony\Component\DependencyInjection\Reference;
@@ -34,12 +33,7 @@ public function process(ContainerBuilder $container)
3433

3534
$dataCollector = $container->getDefinition(GeocoderDataCollector::class);
3635

37-
foreach ($container->findTaggedServiceIds('bazinga_geocoder.provider') as $providerId => $attributes) {
38-
$container->register($providerId.'.debug', ProfilingProvider::class)
39-
->setDecoratedService($providerId)
40-
->setArguments([
41-
new Reference($providerId.'.debug.inner'),
42-
]);
36+
foreach ($container->findTaggedServiceIds('bazinga_geocoder.profiling_plugin') as $providerId => $attributes) {
4337
$dataCollector->addMethodCall('addInstance', [new Reference($providerId)]);
4438
}
4539
}

DependencyInjection/Configuration.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,68 @@ private function getProvidersNode()
9595
->variableNode('options')->defaultValue([])->end()
9696
->scalarNode('cache')->defaultNull()->end()
9797
->scalarNode('cache_lifetime')->defaultNull()->end()
98+
->scalarNode('limit')->defaultNull()->end()
99+
->scalarNode('locale')->defaultNull()->end()
100+
->scalarNode('logger')->defaultNull()->end()
98101
->arrayNode('aliases')
99102
->prototype('scalar')->end()
100103
->end()
104+
->append($this->createClientPluginNode())
101105
->end()
102106
->end();
103107

104108
return $node;
105109
}
110+
111+
/**
112+
* Create plugin node of a client.
113+
*
114+
* @return ArrayNodeDefinition The plugin node
115+
*/
116+
private function createClientPluginNode()
117+
{
118+
$builder = new TreeBuilder();
119+
$node = $builder->root('plugins');
120+
121+
/** @var ArrayNodeDefinition $pluginList */
122+
$pluginList = $node
123+
->info('A list of plugin service ids. The order is important.')
124+
->prototype('array')
125+
;
126+
$pluginList
127+
// support having just a service id in the list
128+
->beforeNormalization()
129+
->always(function ($plugin) {
130+
if (is_string($plugin)) {
131+
return [
132+
'reference' => [
133+
'enabled' => true,
134+
'id' => $plugin,
135+
],
136+
];
137+
}
138+
139+
return $plugin;
140+
})
141+
->end()
142+
;
143+
144+
$pluginList
145+
->children()
146+
->arrayNode('reference')
147+
->canBeEnabled()
148+
->info('Reference to a plugin service')
149+
->children()
150+
->scalarNode('id')
151+
->info('Service id of a plugin')
152+
->isRequired()
153+
->cannotBeEmpty()
154+
->end()
155+
->end()
156+
->end()
157+
->end()
158+
->end();
159+
160+
return $node;
161+
}
106162
}

EventListener/FakeRequestListener.php

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)