geocoder-php/plugin
Lightweight plugin system for Geocoder PHP. Add features like caching, logging, rate limiting, and more by wrapping Geocoder providers with reusable plugins. Designed to be framework-agnostic, easy to compose, and simple to integrate in any PHP app.
Installation
composer require geocoder-php/plugin
Ensure geocoder-php/geocoder is also installed as the base package.
First Use Case: Extending a Provider
Register a custom plugin for an existing provider (e.g., Google Maps) in your config/geocoder.php:
'plugins' => [
'Geocoder\Provider\GoogleMaps\GoogleMapsProvider' => [
'MyCustomPlugin' => \App\Plugins\GooglePlugin::class,
],
],
Basic Plugin Class
Create a plugin class extending Geocoder\Plugin\AbstractPlugin:
namespace App\Plugins;
use Geocoder\Plugin\AbstractPlugin;
use Geocoder\Result\Address;
class GooglePlugin extends AbstractPlugin
{
public function getName(): string
{
return 'google_custom';
}
public function modifyResult(Address $address): Address
{
$address->street .= ' (Enhanced)';
return $address;
}
}
Usage in Code
$geocoder = new \Geocoder\Geocoder();
$geocoder->plugin('google_custom'); // Enable plugin
$results = $geocoder->geocode('1600 Amphitheatre Parkway, Mountain View');
Provider-Specific Enhancements Use plugins to modify results from specific providers (e.g., adding custom fields to Google Maps responses):
public function modifyResult(Address $address): Address
{
$address->setExtra('provider', 'google');
return $address;
}
Batch Processing Apply plugins to multiple results in a loop:
foreach ($geocoder->geocodeQuery('New York')->all() as $result) {
$geocoder->plugin('custom_formatter')->modifyResult($result);
}
Conditional Plugin Activation Dynamically enable plugins based on input:
if ($request->has('enhanced')) {
$geocoder->plugin('premium_enhancer');
}
Plugin Chaining Combine multiple plugins for layered transformations:
$geocoder->plugin('cleanup')->plugin('enrich');
$app->bind(\App\Plugins\GooglePlugin::class, function ($app) {
return new \App\Plugins\GooglePlugin($app['config']);
});
config('geocoder.plugins') to dynamically load plugins.$plugin = $this->createMock(\App\Plugins\GooglePlugin::class);
$plugin->method('modifyResult')->willReturn($address);
$geocoder->setPlugin($plugin);
Plugin Order Matters
Plugins execute in registration order. Use Geocoder::plugin() to explicitly set priority:
$geocoder->plugin('first')->plugin('second'); // 'second' runs last.
Provider Compatibility
Plugins are tied to specific providers. Misconfiguration (e.g., attaching a GooglePlugin to OpenStreetMap) will silently fail.
Performance Overhead
Avoid heavy computations in modifyResult(). Cache or batch operations where possible.
Result Immutability
The AbstractPlugin expects modifyResult() to return a new Address object. Modifying the input directly may cause unexpected behavior.
Geocoder::setLogger() to trace plugin execution:
$geocoder->setLogger(new \Monolog\Logger('geocoder', [$handler]));
dd($geocoder->getPlugins());
Bing) may require additional setup in the plugin constructor.Custom Events
Extend plugins to dispatch events (e.g., Geocoder\Event\ResultModified):
use Geocoder\Event\ResultModified;
$this->dispatch(new ResultModified($address));
Async Processing Offload heavy transformations to queues:
public function modifyResult(Address $address): Address
{
dispatch(new ProcessAddress($address))->delay(now()->addMinutes(1));
return $address;
}
Dynamic Plugin Loading
Load plugins from a directory (e.g., app/Plugins) via a service provider:
$plugins = collect(glob(app_path('Plugins/*Plugin.php')))
->map(fn ($path) => class_basename($path, '.php'))
->filter(fn ($class) => class_exists($class))
->toArray();
Fallback Logic
Implement shouldApply() to conditionally skip execution:
public function shouldApply(Address $address): bool
{
return str_contains($address->country, 'US');
}
How can I help you explore Laravel packages today?