{"id":13563414,"url":"https://github.com/stevebauman/location","last_synced_at":"2025-05-13T00:21:11.240Z","repository":{"id":16682239,"uuid":"19438278","full_name":"stevebauman/location","owner":"stevebauman","description":"Retrieve a visitor's location from their IP address.","archived":false,"fork":false,"pushed_at":"2025-02-20T16:04:12.000Z","size":24352,"stargazers_count":1206,"open_issues_count":6,"forks_count":188,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-05-08T23:11:59.314Z","etag":null,"topics":["laravel","location","maxmind","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevebauman.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-05-04T23:10:58.000Z","updated_at":"2025-05-07T00:07:15.000Z","dependencies_parsed_at":"2024-04-09T18:28:07.049Z","dependency_job_id":"abefe454-af72-4426-abef-6aa86821201b","html_url":"https://github.com/stevebauman/location","commit_stats":{"total_commits":337,"total_committers":27,"mean_commits":"12.481481481481481","dds":"0.35311572700296734","last_synced_commit":"49f28e58daa0382bdc571b20f27b3c57c8691f1c"},"previous_names":[],"tags_count":63,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevebauman%2Flocation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevebauman%2Flocation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevebauman%2Flocation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevebauman%2Flocation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevebauman","download_url":"https://codeload.github.com/stevebauman/location/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253324483,"owners_count":21890854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["laravel","location","maxmind","php"],"created_at":"2024-08-01T13:01:19.003Z","updated_at":"2025-05-13T00:21:11.217Z","avatar_url":"https://github.com/stevebauman.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eLocation\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\nRetrieve a visitor's location from their IP address using various services (online \u0026 local).\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/stevebauman/location/actions\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/stevebauman/location/run-tests.yml?branch=master\u0026style=flat-square\"\u003e\u003c/a\u003e\n\u003ca href=\"https://packagist.org/packages/stevebauman/location\"\u003e\u003cimg src=\"https://img.shields.io/packagist/dt/stevebauman/location.svg?style=flat-square\"\u003e\u003c/a\u003e\n\u003ca href=\"https://packagist.org/packages/stevebauman/location\"\u003e\u003cimg src=\"https://img.shields.io/packagist/l/stevebauman/location.svg?style=flat-square\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Index\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Testing](#testing)\n- [Drivers](#drivers)\n- [Upgrading from v6](#upgrading-from-v6)\n- [Versioning](#versioning)\n\n## Requirements\n\n- PHP \u003e= 8.1\n- Laravel \u003e= 8.0\n\n## Installation\n\nInstall location using `composer require`:\n\n```bash\ncomposer require stevebauman/location\n```\n\nPublish the configuration file (this will create a `location.php` file inside the `config` directory):\n\n```bash\nphp artisan vendor:publish --provider=\"Stevebauman\\Location\\LocationServiceProvider\"\n```\n\n## Usage\n\n### Retrieve a client's location\n\n```php\nuse Stevebauman\\Location\\Facades\\Location;\n\nif ($position = Location::get()) {\n    // Successfully retrieved position.\n    echo $position-\u003ecountryName;\n} else {\n    // Failed retrieving position.\n}\n```\n\n\u003e **Important**:\n\u003e - This method retrieves the user's IP address via `request()-\u003eip()`.\n\u003e - In the default configuration, `testing.enabled` is active, the returned IP address is in the USA. Disable it to get the client's real IP address.\n\n### Retrieve the location of a specific IP address\n\n```php\n$position = Location::get('192.168.1.1');\n```\n\n## Testing\n\nYou may call `Location::fake` with an array of IP address patterns and their expected positions to fake the location of an IP address:\n\n```php\nuse Stevebauman\\Location\\Position;\nuse Stevebauman\\Location\\Facades\\Location;\n\nLocation::fake([\n    '127.0.0.1' =\u003e Position::make([\n        'countryName' =\u003e 'United States',\n        'countryCode' =\u003e 'US',\n        // ...\n    ])\n]);\n\n// Somewhere in your application...\n\n$position = Location::get('127.0.0.1'); // Position\n```\n\nIf you prefer, you may use an asterisk to return the same position for any IP address that is given: \n\n```php\nLocation::fake([\n    '*' =\u003e Position::make([\n        'countryName' =\u003e 'United States',\n        'countryCode' =\u003e 'US',\n        // ...\n    ])\n]);\n\n$position = Location::get($anyIpAddress); // Position\n```\n\nIf no expectations are given, or an expectation is not matched, `Location::get` will return `false`:\n\n```php\nLocation::fake();\n\nLocation::get($anyIpAddress); // false\n```\n\nIf your application attempts to retrieve the location's of multiple IP addresses, you may provide multiple IP address expectation patterns:\n\n```php\nLocation::fake([\n    '127.0.0.1' =\u003e Position::make([\n        'countryName' =\u003e 'United States',\n        'countryCode' =\u003e 'US',\n        // ...\n    ]),\n    '192.168.1.1' =\u003e Position::make([\n        'countryName' =\u003e 'Canada',\n        'countryCode' =\u003e 'CA',\n        // ...\n    ]),\n]);\n```\n\nYou may also use an asterisk to fake several IP patterns:\n\n```php\nLocation::fake([\n    '192.123.*.*' =\u003e Position::make([\n        'countryName' =\u003e 'United States',\n        'countryCode' =\u003e 'US',\n        // ...\n    ]),\n    '192.456.*.*' =\u003e Position::make([\n        'countryName' =\u003e 'Canada',\n        'countryCode' =\u003e 'CA',\n        // ...\n    ]),\n]);\n```\n\n## Drivers\n\n### Available Drivers\n\nAvailable drivers:\n\n- [IpApi](http://ip-api.com) - Default\n- [IpApiPro](https://pro.ip-api.com)\n- [IpData](https://ipdata.co)\n- [IpInfo](https://ipinfo.io)\n- [Kloudend](https://ipapi.co)\n- [GeoPlugin](http://www.geoplugin.com)\n- [MaxMind](https://www.maxmind.com/en/home)\n- [Cloudflare](https://support.cloudflare.com/hc/en-us/articles/200168236-Configuring-IP-geolocation)\n- [IP2Location.io](https://www.ip2location.io/)\n\n#### Setting up MaxMind with a self-hosted database (optional)\n\nIt is encouraged to set up MaxMind as a fallback driver using a local database \nso that some location information is returned in the event of hitting\na rate limit from the external web services.\n\nTo set up MaxMind to retrieve the user's location from your own server, you must:\n\n1. Create a [maxmind account](https://www.maxmind.com/en/geolite2/signup) and sign in\n2. Click \"Manage License Keys\" from the profile menu dropdown\n3. Generate a new license key\n4. Copy the license key and save it into your `.env` file using the `MAXMIND_LICENSE_KEY` key\n5. Run `php artisan location:update` to download the latest `.mmdb` file into your `database/maxmind` directory\n6. That's it, you're all set!\n\n\u003e **Note**: Keep in mind, you'll need to update this file by running `location:update` \n\u003e on a regular basis to retrieve the most current information from your visitors.\n\n### Fallback Drivers\n\nIn the config file, you can specify as many fallback drivers as you wish. It is\nrecommended to configure the MaxMind driver with the local database `.mmdb`\nfile (mentioned above), so you are alwaysretrieving some generic location\ninformation from the visitor.\n\nIf an exception occurs trying to grab a driver (such as a 400/500 error if the\nproviders API changes), it will automatically use the next driver in line.\n\n### Creating your own drivers\n\nTo create your own driver, simply create a class in your application, and extend the abstract Driver:\n\n```php\nnamespace App\\Location\\Drivers;\n\nuse Illuminate\\Support\\Fluent;\nuse Illuminate\\Support\\Facades\\Http;\nuse Stevebauman\\Location\\Position;\nuse Stevebauman\\Location\\Request;\nuse Stevebauman\\Location\\Drivers\\Driver;\n\nclass MyDriver extends Driver\n{    \n    protected function process(Request $request): Fluent\n    {\n         $response = Http::get(\"https://driver-url.com\", ['ip' =\u003e $request-\u003egetIp()]);\n         \n         return new Fluent($response-\u003ejson());\n    }\n\n    protected function hydrate(Position $position, Fluent $location): Position\n    {\n        $position-\u003ecountryCode = $location-\u003ecountry_code;\n\n        return $position;\n    }\n}\n```\n\nThen, insert your driver class name into the configuration file:\n\n```php\n// config/location.php\n\n'driver' =\u003e App\\Location\\Drivers\\MyDriver::class,\n```\n\n## Upgrading from v6\n\nIn version 7, the codebase has been updated with strict PHP types, \nupdated PHP and Laravel version requirements, an updated `Driver` \nstructure, as well as a small configuration addition.\n\n### Configuration\n\nIn version 7, location drivers can now implement an `Updatable` interface \nthat allows them to be updated using the `location:update` command. \nCurrently, only the MaxMind driver supports this.\n\nTo update your configuration file to be able to download the latest\nMaxMind database file automatically, insert the below `url` \nconfiguration option in your `config/location.php` file:\n\n```diff\n// config/location.php\n\nreturn [\n    'maxmind' =\u003e [\n        // ...\n        \n        'local' =\u003e [\n            // ...\n            \n+            'url' =\u003e sprintf('https://download.maxmind.com/app/geoip_download_by_token?edition_id=GeoLite2-City\u0026license_key=%s\u0026suffix=tar.gz', env('MAXMIND_LICENSE_KEY')),\n        ],\n    ],\n];\n```\n\nOnce done, you may execute the below artisan command to download the latest `.mmdb` file:\n\n```bash\nphp artisan location:update\n```\n\n### Drivers\n\nIn version 7, the codebase has been updated with strict PHP \ntypes, updated PHP and Laravel version requirements, \nand an updated `Driver` structure.\n\nIf you have created your own custom driver implementation, \nyou must update it to use the base `Driver` or `HttpDriver` class.\n\nIf you're fetching a location using an HTTP service, it\nmay be useful to extend the `HttpDriver` to reduce \nthe code you need to write:\n\n```diff\nnamespace App\\Location\\Drivers;\n\nuse Illuminate\\Support\\Fluent;\nuse Stevebauman\\Location\\Position;\n- use Stevebauman\\Location\\Drivers\\Driver;\n+ use Stevebauman\\Location\\Drivers\\HttpDriver;\n\n- class MyDriver extends Driver\n+ class MyDriver extends HttpDriver\n{\n-    public function url($ip)\n+    public function url(string $ip): string;\n    {\n        return \"http://driver-url.com?ip=$ip\";\n    }\n    \n-    protected function process($ip)\n-    {\n-        return rescue(function () use ($ip) {\n-            $response = json_decode(file_get_contents($this-\u003eurl($ip)), true);\n-            \n-            return new Fluent($response);\n-        }, $rescue = false);\n-    }\n\n-    protected function hydrate(Position $position, Fluent $location)\n+    protected function hydrate(Position $position, Fluent $location): Position;\n    {\n        $position-\u003ecountryCode = $location-\u003ecountry_code;\n\n        return $position;\n    }\n}\n```\n\n## Versioning\n\nLocation is versioned under the Semantic Versioning guidelines as much as possible.\n\nReleases will be numbered with the following format:\n\n```\n\u003cmajor\u003e.\u003cminor\u003e.\u003cpatch\u003e\n```\n\nAnd constructed with the following guidelines:\n\n- Breaking backward compatibility bumps the major and resets the minor and patch.\n- New additions without breaking backward compatibility bumps the minor and resets the patch.\n- Bug fixes and misc changes bumps the patch.\n\nMinor versions are not maintained individually, and you're encouraged to upgrade through to the next minor version.\n\nMajor versions are maintained individually through separate branches.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevebauman%2Flocation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevebauman%2Flocation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevebauman%2Flocation/lists"}