├── _config.yml ├── .gitignore ├── composer.json ├── src ├── Geolocation.php ├── Directions.php ├── DistanceMatrix.php ├── Elevation.php ├── Roads.php ├── Timezone.php ├── Routes.php ├── Geocoding.php ├── Service.php └── Client.php ├── .github └── workflows │ └── php.yml ├── LICENSE └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /composer.lock 2 | /vendor 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yidas/google-maps-services", 3 | "description": "PHP client library(SDK) for Google Maps API Web Services", 4 | "keywords": ["Google maps", "services", "php", "sdk"], 5 | "homepage": "https://github.com/yidas/google-maps-services-php", 6 | "type": "library", 7 | "license": "MIT", 8 | "support": { 9 | "issues": "https://github.com/yidas/google-maps-services-php/issues", 10 | "source": "https://github.com/yidas/google-maps-services-php" 11 | }, 12 | "require": { 13 | "php": ">=5.4", 14 | "guzzlehttp/guzzle": "~6.5.6|^7.4.3" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "yidas\\googleMaps\\": "src/" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Geolocation.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/geolocation/ 14 | */ 15 | class Geolocation extends Service 16 | { 17 | /** 18 | * Replace all 19 | */ 20 | const API_URL = 'https://www.googleapis.com/geolocation/v1/geolocate'; 21 | 22 | /** 23 | * Geolocate 24 | * 25 | * @param Client $client 26 | * @param array Body parameters 27 | * @return array Result 28 | */ 29 | public static function geolocate(Client $client, $bodyParams=[]) 30 | { 31 | return self::requestHandler($client, self::API_URL, [], 'POST', $bodyParams); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Directions.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/directions/ 14 | */ 15 | class Directions extends Service 16 | { 17 | const API_URL = 'https://maps.googleapis.com/maps/api/directions/json'; 18 | 19 | /** 20 | * Directions 21 | * 22 | * @param Client $client 23 | * @param string $origin 24 | * @param string $destination 25 | * @param array Query parameters 26 | * @return array Result 27 | */ 28 | public static function directions(Client $client, $origin, $destination, $params=[]) 29 | { 30 | $params['origin'] = (string) $origin; 31 | $params['destination'] = (string) $destination; 32 | 33 | return self::requestHandler($client, self::API_URL, $params); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Validate composer.json and composer.lock 21 | run: composer validate --strict 22 | 23 | - name: Cache Composer packages 24 | id: composer-cache 25 | uses: actions/cache@v3 26 | with: 27 | path: vendor 28 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-php- 31 | 32 | - name: Install dependencies 33 | run: composer install --prefer-dist --no-progress 34 | 35 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" 36 | # Docs: https://getcomposer.org/doc/articles/scripts.md 37 | 38 | # - name: Run test suite 39 | # run: composer run-script test 40 | -------------------------------------------------------------------------------- /src/DistanceMatrix.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/distance-matrix/ 14 | */ 15 | class DistanceMatrix extends Service 16 | { 17 | const API_URL = 'https://maps.googleapis.com/maps/api/distancematrix/json'; 18 | 19 | /** 20 | * Distance matrix 21 | * 22 | * @param Client $client 23 | * @param string $origin 24 | * @param string $destination 25 | * @param array Query parameters 26 | * @return array Result 27 | */ 28 | public static function distanceMatrix(Client $client, $origins, $destinations, $params=[]) 29 | { 30 | $params['origins'] = (string) $origins; 31 | $params['destinations'] = (string) $destinations; 32 | 33 | return self::requestHandler($client, self::API_URL, $params); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nick Tsai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Elevation.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/elevation/ 14 | */ 15 | class Elevation extends Service 16 | { 17 | const API_URL = 'https://maps.googleapis.com/maps/api/elevation/json'; 18 | 19 | /** 20 | * Elevation 21 | * 22 | * @param Client $client 23 | * @param string $locations 24 | * @param array Query parameters 25 | * @return array Result 26 | */ 27 | public static function elevation(Client $client, $locations, $params=[]) 28 | { 29 | // `locations` seems to only allow `lat,lng` pattern 30 | if (is_string($locations)) { 31 | 32 | $params['locations'] = $locations; 33 | 34 | } else { 35 | 36 | list($lat, $lng) = $locations; 37 | $params['locations'] = "{$lat},{$lng}"; 38 | } 39 | 40 | return self::requestHandler($client, self::API_URL, $params); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Roads.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/roads 14 | */ 15 | class Roads extends Service 16 | { 17 | const API_URL = 'https://roads.googleapis.com/v1/snapToRoads'; 18 | 19 | const LANGUAGE_METHOD = false; 20 | 21 | /** 22 | * Reverse Geocode 23 | * 24 | * @param Client $client 25 | * @param string $address 26 | * @param array Query parameters 27 | * @return array Result 28 | */ 29 | public static function snapToRoads(Client $client, $path=null, $params=[]) 30 | { 31 | if (is_array($path)) { 32 | $pathString = ''; 33 | foreach ($path as $key => $eachPathArray) { 34 | $pathString = ($key != 0) ? $pathString . '|' : $pathString; 35 | $pathString .= implode(',', $eachPathArray); 36 | } 37 | $params['path'] = $pathString; 38 | } 39 | 40 | $params['interpolate'] = $params['interpolate'] ?? 'true'; 41 | 42 | return self::requestHandler($client, self::API_URL, $params); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Timezone.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/timezone/ 14 | */ 15 | class Timezone extends Service 16 | { 17 | const API_URL = 'https://maps.googleapis.com/maps/api/timezone/json'; 18 | 19 | /** 20 | * Timezone 21 | * 22 | * @param Client $client 23 | * @param string $location 24 | * @param string $timestamp 25 | * @param array Query parameters 26 | * @return array Result 27 | */ 28 | public static function timezone(Client $client, $location, $timestamp=null, $params=[]) 29 | { 30 | // `location` seems to only allow `lat,lng` pattern 31 | if (is_string($location)) { 32 | 33 | $params['location'] = $location; 34 | 35 | } else { 36 | 37 | list($lat, $lng) = $location; 38 | $params['location'] = "{$lat},{$lng}"; 39 | } 40 | 41 | // Timestamp 42 | $params['timestamp'] = ($timestamp) ?: time(); 43 | 44 | return self::requestHandler($client, self::API_URL, $params); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Routes.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.2.0 13 | * @see https://developers.google.com/maps/documentation/routes/ 14 | */ 15 | class Routes extends Service 16 | { 17 | const API_URL = 'https://routes.googleapis.com/directions/v2'; 18 | 19 | const LANGUAGE_METHOD = 'body'; 20 | 21 | /** 22 | * Distance matrix 23 | * 24 | * @param Client $client 25 | * @param array $origin 26 | * @param array $destination 27 | * @param array $body Full body 28 | * @param array $headers 29 | * @return array Result 30 | */ 31 | public static function computeRoutes(Client $client, $origin, $destination, $body=[], $fieldMask=[]) 32 | { 33 | $fullApiUrl = self::API_URL . ':computeRoutes'; 34 | 35 | $requestBody = $body; 36 | $requestBody['origin'] = $origin ?? $requestBody['origin'] ?? []; 37 | $requestBody['destination'] = $destination ?? $requestBody['destination'] ?? []; 38 | 39 | // Header 40 | $fieldMask = $fieldMask ? $fieldMask : ['routes.duration', 'routes.distanceMeters', 'routes.polyline.encodedPolyline']; 41 | $fieldMask = is_array($fieldMask) ? implode(",", $fieldMask) : $fieldMask; 42 | $headers = [ 43 | 'X-Goog-Api-Key' => $client->getApiKey(), 44 | 'X-Goog-FieldMask' => $fieldMask, 45 | ]; 46 | 47 | return self::requestHandler($client, $fullApiUrl, [], 'POST', $requestBody, $headers); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Geocoding.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.0.0 13 | * @see https://developers.google.com/maps/documentation/geocoding/ 14 | */ 15 | class Geocoding extends Service 16 | { 17 | const API_URL = 'https://maps.googleapis.com/maps/api/geocode/json'; 18 | 19 | /** 20 | * Reverse Geocode 21 | * 22 | * @param Client $client 23 | * @param string $address 24 | * @param array Query parameters 25 | * @return array Result 26 | */ 27 | public static function geocode(Client $client, $address=null, $params=[]) 28 | { 29 | if (is_string($address)) 30 | $params['address'] = $address; 31 | 32 | return self::requestHandler($client, self::API_URL, $params); 33 | } 34 | 35 | /** 36 | * Reverse Geocode 37 | * 38 | * @param Client $client 39 | * @param array|string $latlng ['lat', 'lng'] or place_id string 40 | * @param array Query parameters 41 | * @return array Result 42 | */ 43 | public static function reverseGeocode(Client $client, $latlng, $params=[]) 44 | { 45 | // Check if latlng param is a place_id string. 46 | // place_id strings do not contain commas; latlng strings do. 47 | if (is_string($latlng)) { 48 | 49 | $params['place_id'] = $latlng; 50 | 51 | } else { 52 | 53 | list($lat, $lng) = $latlng; 54 | $params['latlng'] = "{$lat},{$lng}"; 55 | } 56 | 57 | return self::requestHandler($client, self::API_URL, $params); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Service.php: -------------------------------------------------------------------------------- 1 | 9 | * @since 1.2.0 10 | */ 11 | abstract class Service 12 | { 13 | /** 14 | * Define by each service 15 | * 16 | * @param string 17 | */ 18 | const API_URL = ''; 19 | 20 | /** 21 | * Language setting method 22 | * 23 | * 'query' for query string method, 'body' for request body 24 | * 25 | * @param string 26 | */ 27 | const LANGUAGE_METHOD = 'query'; 28 | 29 | /** 30 | * Request Handler 31 | * 32 | * @param Client $client 33 | * @param string $apiUrl 34 | * @param array $params 35 | * @param string $method HTTP request method 36 | * @param array $body 37 | * @param array $headers 38 | * @return array|mixed Formated result 39 | */ 40 | protected static function requestHandler(Client $client, $apiUrl, $params, $method='GET', $body=[], $headers=[]) 41 | { 42 | // Language setting for query string 43 | if (static::LANGUAGE_METHOD && $client->getLanguage()) { 44 | 45 | switch (static::LANGUAGE_METHOD) { 46 | case 'body': 47 | if (!isset($body['languageCode'])) { 48 | $body['languageCode'] = $client->getLanguage(); 49 | } 50 | break; 51 | 52 | case 'query': 53 | default: 54 | $params['language'] = $client->getLanguage(); 55 | break; 56 | } 57 | } 58 | 59 | // Body 60 | $bodyString = ($body) ? json_encode($body, JSON_UNESCAPED_SLASHES) : null; 61 | 62 | // Header 63 | $defaultHeaders = [ 64 | 'Content-Type' => 'application/json', 65 | ]; 66 | $headers = array_merge($defaultHeaders, $headers); 67 | 68 | $response = $client->request($apiUrl, $params, $method, $bodyString, $headers); 69 | $result = $response->getBody()->getContents(); 70 | $result = json_decode($result, true); 71 | 72 | // Error Handler 73 | if (200 != $response->getStatusCode()) 74 | return $result; 75 | // Error message Checker (200 situation form Google Maps API) 76 | elseif (isset($result['error_message'])) 77 | return $result; 78 | 79 | // `results` parsing from Google Maps API, while pass parsing on error 80 | return isset($result['results']) ? $result['results'] : $result; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 12 | * @version 1.2.0 13 | * 14 | * @method array directions(string $origin, string $destination, array $params=[]) 15 | * @method array distanceMatrix(string $origin, string $destination, array $params=[]) 16 | * @method array elevation(string $locations, array $params=[]) 17 | * @method array geocode(string $address, array $params=[]) 18 | * @method array reverseGeocode(string $latlng, array $params=[]) 19 | * @method array computeRoutes(array $origin, array $destination, array $body=[], array $fieldMask=[]) 20 | * @method array geolocate(array $bodyParams=[]) 21 | * @method array timezone(string $location, string $timestamp=null, array $params=[]) 22 | */ 23 | class Client 24 | { 25 | /** 26 | * Google Maps Platform base API host 27 | */ 28 | // const API_HOST = 'https://maps.googleapis.com'; 29 | 30 | /** 31 | * For service autoload 32 | * 33 | * @see http://php.net/manual/en/language.namespaces.rules.php 34 | */ 35 | const SERVICE_NAMESPACE = "\\yidas\\googleMaps\\"; 36 | 37 | /** 38 | * For Client-Service API method director 39 | * 40 | * @var array Method => Service Class name 41 | */ 42 | protected static $serviceMethodMap = [ 43 | 'directions' => 'Directions', 44 | 'distanceMatrix' => 'DistanceMatrix', 45 | 'elevation' => 'Elevation', 46 | 'geocode' => 'Geocoding', 47 | 'reverseGeocode' => 'Geocoding', 48 | 'geolocate' => 'Geolocation', 49 | 'timezone' => 'Timezone', 50 | 'computeRoutes' => 'Routes', 51 | 'snapToRoads' => 'Roads', 52 | ]; 53 | 54 | /** 55 | * Google API Key 56 | * 57 | * Authenticating by API Key, otherwise by client ID/digital signature 58 | * 59 | * @var string 60 | */ 61 | protected $apiKey; 62 | 63 | /** 64 | * Google client ID 65 | * 66 | * @var string 67 | */ 68 | protected $clientID; 69 | 70 | /** 71 | * Google client's digital signature 72 | * 73 | * @var string 74 | */ 75 | protected $clientSecret; 76 | 77 | /** 78 | * @var \GuzzleHttp\Client 79 | */ 80 | protected $httpClient; 81 | 82 | /** 83 | * Google Maps default language 84 | * 85 | * @var string ex. 'zh-TW' 86 | */ 87 | protected $language; 88 | 89 | /** 90 | * Constructor 91 | * 92 | * @param string|array $optParams API Key or option parameters 93 | * 'key' => Google API Key 94 | * 'clientID' => Google clientID 95 | * 'clientSecret' => Google clientSecret 96 | * @return self 97 | */ 98 | function __construct($optParams) 99 | { 100 | // Quick setting for API Key 101 | if (is_string($optParams)) { 102 | // Params as a string key 103 | $key = $optParams; 104 | $optParams = []; 105 | $optParams['key'] = $key; 106 | } 107 | 108 | // Assignment 109 | $key = isset($optParams['key']) ? $optParams['key'] : null; 110 | $clientID = isset($optParams['clientID']) ? $optParams['clientID'] : null; 111 | $clientSecret = isset($optParams['clientSecret']) ? $optParams['clientSecret'] : null; 112 | $defaultLang = isset($optParams['language']) ? $optParams['language'] : null; 113 | 114 | // Use API Key 115 | if ($key) { 116 | 117 | if ($clientID || $clientSecret) { 118 | throw new Exception("clientID/clientSecret should not set when using key", 400); 119 | } 120 | 121 | $this->apiKey = (string) $key; 122 | } 123 | // Use clientID/clientSecret 124 | elseif ($clientID && $clientSecret) { 125 | 126 | $this->clientID = (string) $clientID; 127 | $this->clientSecret = (string) $clientSecret; 128 | } 129 | else { 130 | 131 | throw new Exception("Unable to set Client credential due to your wrong params", 400); 132 | } 133 | 134 | // Default Language setting 135 | if ($defaultLang) { 136 | $this->setLanguage($defaultLang); 137 | } 138 | 139 | // Load GuzzleHttp\Client 140 | $this->httpClient = new HttpClient([ 141 | // 'base_uri' => self::API_HOST, 142 | 'timeout' => 5.0, 143 | ]); 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Request Google Map API 150 | * 151 | * @param string $url 152 | * @param array $params 153 | * @param string $method HTTP request method 154 | * @param string $body 155 | * @param array $headers 156 | * @return GuzzleHttp\Psr7\Response 157 | */ 158 | public function request($url, $params=[], $method='GET', $body=null, $headers=null) 159 | { 160 | // Guzzle request options 161 | $options = [ 162 | 'http_errors' => false, 163 | ]; 164 | 165 | // Parameters for Auth 166 | $defaultParams = ($this->apiKey) 167 | ? ['key' => $this->apiKey] 168 | : ['client' => $this->clientID, 'signature' => $this->clientSecret]; 169 | 170 | // Query String 171 | $options['query'] = array_merge($defaultParams, $params); 172 | 173 | // Body 174 | if ($body) { 175 | $options['body'] = $body; 176 | } 177 | 178 | // Headers 179 | if ($headers) { 180 | $options['headers'] = $headers; 181 | } 182 | 183 | // $options['debug'] = true; 184 | return $this->httpClient->request($method, $url, $options); 185 | } 186 | 187 | /** 188 | * Set default language for Google Maps API 189 | * 190 | * @param string $language ex. 'zh-TW' 191 | * @return self 192 | */ 193 | public function setLanguage($language=null) 194 | { 195 | $this->language = $language; 196 | 197 | return $this; 198 | } 199 | 200 | /** 201 | * Get current language setting for Google Maps API 202 | * 203 | * @return string 204 | */ 205 | public function getLanguage() 206 | { 207 | return $this->language; 208 | } 209 | 210 | /** 211 | * Get current API key in client 212 | * 213 | * @return string 214 | */ 215 | public function getApiKey() 216 | { 217 | return $this->apiKey; 218 | } 219 | 220 | /** 221 | * Client methods refer to each service 222 | * 223 | * All service methods from Client calling would leave out the first argument (Client itself). 224 | * 225 | * @param string Client's method name 226 | * @param array Method arguments 227 | * @return mixed Current service method return 228 | * @example 229 | * $equal = \yidas\googleMaps\Geocoding::geocode($client, 'Address'); 230 | * $equal = $client->geocode('Address'); 231 | */ 232 | public function __call($method, $arguments) 233 | { 234 | // Matching self::$serviceMethodMap is required 235 | if (!isset(self::$serviceMethodMap[$method])) 236 | throw new Exception("Call to undefined method ".__CLASS__."::{$method}()", 400); 237 | 238 | // Get the service mapped by method 239 | $service = self::$serviceMethodMap[$method]; 240 | 241 | // Fill Client in front of arguments 242 | array_unshift($arguments, $this); 243 | 244 | return call_user_func_array([self::SERVICE_NAMESPACE . $service, $method], $arguments); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |