Added Other AP
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
require_once('../common/vendor/autoload.php');
|
||||
|
||||
use Phpfastcache\CacheManager;
|
||||
use Phpfastcache\Drivers\Redis\Config;
|
||||
|
||||
abstract class Api
|
||||
{
|
||||
public $apiName = ''; //trips
|
||||
|
||||
protected $method = ''; //GET|POST|PUT|DELETE
|
||||
|
||||
public $requestUri = [];
|
||||
public $requestParams = [];
|
||||
public $requestHeaders = [];
|
||||
public $requestWhitelist = [];
|
||||
public $clientIP = null;
|
||||
|
||||
protected $action = '';
|
||||
//Method name to execute
|
||||
protected $encryption = true;
|
||||
|
||||
public $cacheWhitelist = [];
|
||||
public $cache;
|
||||
public $cacheEnabled = false;
|
||||
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
global $savvyext;
|
||||
header("Access-Control-Allow-Orgin: *");
|
||||
header("Access-Control-Allow-Methods: *");
|
||||
header("Content-Type: application/json");
|
||||
|
||||
//GET parameter array separated by slash
|
||||
//$this->requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
|
||||
$this->requestUri = $requestUri;
|
||||
|
||||
if (is_array($requestUri) && count($requestUri)>1) {
|
||||
if (($pos=strpos($requestUri[1],'?'))!==false) {
|
||||
$requestUri[1] = substr($requestUri[1],1+$pos);
|
||||
}
|
||||
parse_str($requestUri[1],$this->requestParams);
|
||||
}
|
||||
|
||||
$this->encryption = $encryption;
|
||||
|
||||
//Define the request method
|
||||
$this->method = $_SERVER['REQUEST_METHOD'];
|
||||
if ($this->method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
|
||||
if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') {
|
||||
$this->method = 'DELETE';
|
||||
}
|
||||
else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') {
|
||||
$this->method = 'PUT';
|
||||
}
|
||||
else {
|
||||
throw new Exception("Unexpected Header");
|
||||
}
|
||||
}
|
||||
|
||||
$this->cacheEnabled = ($savvyext->cfgReadLong('cache.enabled') != NULL && $savvyext->cfgReadLong('cache.enabled') == 1);
|
||||
if ($this->cacheEnabled ) {
|
||||
$hostsString = $savvyext->cfgReadChar( 'cache.servers' );
|
||||
$serverConf = [];
|
||||
if ( ! empty( $hostsString ) ) {
|
||||
$hostsInfo = explode( ",", $hostsString );
|
||||
foreach ( $hostsInfo as $hostInfo ) {
|
||||
$hostInfoItems = explode( ":", $hostInfo );
|
||||
if ( count( $hostInfoItems ) > 0 ) {
|
||||
$serverConf = [
|
||||
'host' => $hostInfoItems[0],
|
||||
'port' => $hostInfoItems[1] ? intval( $hostInfoItems[1] ) : 6379,
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( count( $serverConf ) != 0 ) {
|
||||
$conf = new Config($serverConf);
|
||||
$this->cache = CacheManager::getInstance( 'redis', $conf );
|
||||
} else {
|
||||
$this->cacheEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->method=="POST") {
|
||||
$raw_json = file_get_contents("php://input");
|
||||
$this->requestParams = json_decode($raw_json, true);
|
||||
}
|
||||
|
||||
if ($this->method == "PUT") {
|
||||
// Do we do key=val&key=val ?
|
||||
$raw_json = file_get_contents("php://input");
|
||||
if (strpos($raw_json, 'encrypted_payload') !== false) {
|
||||
$this->requestParams = json_decode($raw_json, true);
|
||||
} else {
|
||||
parse_str($raw_json, $this->requestParams);
|
||||
}
|
||||
}
|
||||
|
||||
// We can inspect the headers later on
|
||||
$this->loadRequestHeaders();
|
||||
|
||||
// Decrypt the input
|
||||
if (isset($this->requestParams['encrypted_payload'])) {
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$payload = openssl_decrypt(
|
||||
hex2bin(
|
||||
$this->requestParams['encrypted_payload']
|
||||
),
|
||||
$encryptionAlg,
|
||||
$encryptionKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$encryptionIV
|
||||
);
|
||||
unset($this->requestParams['encrypted_payload']);
|
||||
if (is_array($this->requestParams) && count($this->requestParams)>0) {
|
||||
$this->requestParams = array_merge($this->requestParams, json_decode($payload, true));
|
||||
} else {
|
||||
$data = json_decode($payload, true);
|
||||
$this->requestParams = is_array($data) ? $data : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No action taken YET!
|
||||
// TODO: delegate the decision into controller, the default (unset) behaviour is to block
|
||||
protected function checkRequestHeaders($db, $action) {
|
||||
error_log('Checking '.$this->apiName.'::'.$action.'...');
|
||||
if (array_key_exists($action,$this->requestWhitelist)) {
|
||||
error_log('whitelisted!');
|
||||
return true;
|
||||
}
|
||||
$sessionID = null;
|
||||
$deviceToken = null;
|
||||
if (array_key_exists("x-session-id",$this->requestHeaders)) {
|
||||
$sessionID = $this->requestHeaders["x-session-id"];
|
||||
}
|
||||
if (array_key_exists("x-devicetoken",$this->requestHeaders)) {
|
||||
$deviceToken = $this->requestHeaders["x-devicetoken"];
|
||||
}
|
||||
error_log('X-Session-ID: '.$sessionID);
|
||||
error_log('X-DeviceToken: '.$deviceToken);
|
||||
// Step 1a: Get member_id by X-DeviceToken
|
||||
$header_member_id = 0;
|
||||
$conn = $db->getConnect();
|
||||
$q = "SELECT * FROM members_devices WHERE access_token='".pg_escape_string($deviceToken)."'";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$header_member_id = $f['member_id'];
|
||||
$q = "UPDATE members_devices SET updated=now(), status=1 WHERE id=".((int)$f['id'])." RETURNING *";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
error_log('Status updated at: '.$f['updated']);
|
||||
}
|
||||
}
|
||||
if ($header_member_id<1) {
|
||||
//return false; //throw new RuntimeException('Invalid header member ID', 500);
|
||||
}
|
||||
// Step 1b: Get member_id by X-Session-ID
|
||||
$session_member_id = 0;
|
||||
$q = "SELECT * FROM members_session WHERE session='".pg_escape_string($sessionID)."'";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$session_member_id = $f['member_id'];
|
||||
if ($header_member_id<1) {
|
||||
$q = "UPDATE members_devices SET updated=now(), status=1 WHERE id=".((int)$f['id'])." RETURNING *";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
error_log('Status updated at: '.$f['updated']);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($session_member_id<1) {
|
||||
//return false; //throw new RuntimeException('Invalid session member ID', 500);
|
||||
}
|
||||
// Step 2: Get member_id from $this->requestParams
|
||||
$request_member_id = 0;
|
||||
if (array_key_exists('member_id',$this->requestParams)) {
|
||||
$request_member_id = (int)$this->requestParams['member_id'];
|
||||
}
|
||||
error_log('member_id[request] = '.$request_member_id);
|
||||
error_log('member_id[token] = '.$header_member_id);
|
||||
error_log('member_id[session] = '.$session_member_id);
|
||||
if ($request_member_id != $session_member_id && $session_member_id>0) {
|
||||
//$request_member_id = $session_member_id;
|
||||
$this->requestParams['member_id'] = $session_member_id;
|
||||
}
|
||||
// Step 3a: Match Step 1 and 2 result
|
||||
if ($request_member_id>0) {
|
||||
// Step 3b: Fallback to X-Session-ID?
|
||||
if ($request_member_id!=$header_member_id || $request_member_id!=$session_member_id) {
|
||||
//return false; //throw new RuntimeException('Invalid request member ID', 500);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function loadRequestHeaders() {
|
||||
$this->requestHeaders = [];
|
||||
foreach (getallheaders() as $key=>$val) {
|
||||
// https://cloud.google.com/load-balancing/docs/https/
|
||||
// After September 30, HTTP(S) Load Balancers will convert HTTP/1.1 header names to lowercase
|
||||
// in the request and response directions; header values will not be affected.
|
||||
//error_log('DEBUG: "'.$key.'" => "'.$val.'"');
|
||||
$this->requestHeaders[strtolower($key)] = $val;
|
||||
}
|
||||
return count($this->requestHeaders);
|
||||
}
|
||||
|
||||
protected function checkThrottling($db) {
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP)) {
|
||||
$ip = pg_escape_string($_SERVER['HTTP_CLIENT_IP']);
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP)) {
|
||||
$ip = pg_escape_string($_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||
} else {
|
||||
// Will not make much sense since we are behind the reverse proxy
|
||||
$ip = pg_escape_string($_SERVER['REMOTE_ADDR']);
|
||||
}
|
||||
$this->clientIP = $ip;
|
||||
$lastRec = NULL;
|
||||
$rec = NULL;
|
||||
$conn = $db->getConnect();
|
||||
$q = "SELECT *,(EXTRACT(EPOCH FROM time_last) * 1000)::bigint AS last_ms FROM throttling_ip WHERE ip='${ip}'";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$lastRec = $f;
|
||||
$q = "UPDATE throttling_ip SET total=total+1,time_last=NOW() WHERE ip='${ip}' RETURNING *,(EXTRACT(EPOCH FROM time_last) * 1000)::bigint AS last_ms";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$rec = $f;
|
||||
}
|
||||
} else {
|
||||
$q = "INSERT INTO throttling_ip (ip) VALUES ('${ip}') RETURNING *,(EXTRACT(EPOCH FROM time_last) * 1000)::bigint AS last_ms";
|
||||
$r = pg_query($conn, $q);
|
||||
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
|
||||
$rec = $f;
|
||||
}
|
||||
}
|
||||
if (!$lastRec && $rec && is_array($rec) && count($rec)>0) {
|
||||
error_log('New IP spotted!');
|
||||
return true; // New record - never seen this IP before...
|
||||
}
|
||||
if (!$lastRec && (!$rec || !is_array($rec) || !count($rec)<1)) {
|
||||
// Failed to insert new record
|
||||
error_log('Error: not throttling!');
|
||||
return true; // TODO: should we fail?
|
||||
}
|
||||
// Compare $lastRec to $rec
|
||||
if (($rec["last_ms"]-$lastRec["last_ms"]) > 10) {
|
||||
error_log('Not throttle!');
|
||||
return true;
|
||||
} else {
|
||||
error_log('Throttle: ' . $rec["last_ms"] . " - " . $lastRec["last_ms"] . " = ".($rec["last_ms"]-$lastRec["last_ms"]));
|
||||
return true; // Throttle if less than a second within
|
||||
}
|
||||
return true; // OK
|
||||
}
|
||||
|
||||
protected function isActionCached($action) {
|
||||
error_log('Check is it '.$this->apiName.'::'.$action.' could be cached...');
|
||||
if (array_key_exists($action,$this->cacheWhitelist) || in_array($action,$this->cacheWhitelist)) {
|
||||
$ttl = $this->cacheWhitelist[$action]['ttl'] ?? 60;
|
||||
if ($ttl == 0) {
|
||||
return false;
|
||||
}
|
||||
error_log('whitelisted!');
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
|
||||
//The first 2 elemets of URI array must by "api" and table name
|
||||
if(array_shift($this->requestUri) !== $this->apiName){
|
||||
throw new RuntimeException('API Not Found', 404);
|
||||
}
|
||||
//Select the action to execute
|
||||
$this->action = $this->getAction();
|
||||
|
||||
$db = new Db();
|
||||
// Inspect throttling
|
||||
if (!$this->checkThrottling($db)) {
|
||||
unset($db);
|
||||
throw new RuntimeException('Too Many Requests', 429);
|
||||
}
|
||||
|
||||
// Inspect header
|
||||
if (!$this->checkRequestHeaders($db, $this->action)) {
|
||||
unset($db);
|
||||
throw new RuntimeException('Request check failed', 500);
|
||||
}
|
||||
|
||||
unset($db);
|
||||
|
||||
//If the method (action) defined in the child API class
|
||||
if (method_exists($this, $this->action)) {
|
||||
|
||||
$result = null;
|
||||
|
||||
if( $this->cacheEnabled && $this->isActionCached($this->action)) {
|
||||
$key = $this->getCacheKey();
|
||||
$ttl = $this->cacheWhitelist[$this->action]['ttl'] ?? 300;
|
||||
$cachedString = $this->cache->getItem($key);
|
||||
$result = $cachedString->get();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
$result = $this->{
|
||||
$this->action
|
||||
}
|
||||
();
|
||||
if($this->storeInCache()) {
|
||||
$cachedString->set($result)->expiresAfter($ttl);
|
||||
$this->cache->save($cachedString);
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return $this->{
|
||||
$this->action
|
||||
}
|
||||
();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException('Invalid Method', 405);
|
||||
}
|
||||
}
|
||||
|
||||
protected function response($data, $status = 500) {
|
||||
global $savvyext;
|
||||
header("HTTP/1.1 " . $status . " " . $this->requestStatus($status));
|
||||
if ($this->encryption) {
|
||||
// encrypt data
|
||||
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
|
||||
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
|
||||
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
|
||||
$payload = json_encode($data);
|
||||
$encrypted_payload = bin2hex(
|
||||
openssl_encrypt(
|
||||
$payload,
|
||||
$encryptionAlg,
|
||||
$encryptionKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$encryptionIV
|
||||
)
|
||||
);
|
||||
$data = array(); // Comment out to see the data
|
||||
$data["payload"] = $encrypted_payload;
|
||||
}
|
||||
return json_encode($data);
|
||||
}
|
||||
|
||||
private function requestStatus($code) {
|
||||
$status = array(
|
||||
200 => 'OK',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
500 => 'Internal Server Error',
|
||||
);
|
||||
return ($status[$code])?$status[$code]:$status[500];
|
||||
}
|
||||
|
||||
protected function getAction()
|
||||
{
|
||||
$method = $this->method;
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$pos = strpos($this->requestUri[0],'?');
|
||||
if ($pos!==false && $pos==0) {
|
||||
// We want to get "all"
|
||||
$this->requestUri[0] = "all".$this->requestUri[0];
|
||||
}
|
||||
$tok = strtok($this->requestUri[0],'?');
|
||||
if($tok===false || $tok=='all'){
|
||||
return 'indexAction';
|
||||
} else {
|
||||
return 'viewAction';
|
||||
}
|
||||
break;
|
||||
case 'POST':
|
||||
return 'createAction';
|
||||
break;
|
||||
case 'PUT':
|
||||
return 'updateAction';
|
||||
break;
|
||||
case 'DELETE':
|
||||
return 'deleteAction';
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function indexAction();
|
||||
abstract protected function viewAction();
|
||||
abstract protected function createAction();
|
||||
abstract protected function updateAction();
|
||||
abstract protected function deleteAction();
|
||||
|
||||
protected function getCacheKey() {
|
||||
return hash('md5', $this->method.'|'.$this->apiName.'|'.$this->action.'|'.json_encode($this->requestParams));
|
||||
}
|
||||
|
||||
protected function storeInCache() {
|
||||
return http_response_code() === 200;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class Db {
|
||||
|
||||
private $conn;
|
||||
private $conn_gps;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function getConnect() {
|
||||
global $savvyext;
|
||||
if ($this->conn==NULL || !pg_version($this->conn)) {
|
||||
$db_host = $savvyext->cfgReadChar('database.host');
|
||||
$db_name = $savvyext->cfgReadChar('database.name');
|
||||
$db_user = $savvyext->cfgReadChar('database.user');
|
||||
$db_pass = $savvyext->cfgReadChar('database.pass');
|
||||
$db_port = $savvyext->cfgReadLong('database.port');
|
||||
$connstr = "host=${db_host} port=${db_port} dbname=${db_name} user=${db_user} password=${db_pass}";
|
||||
$this->conn = pg_connect($connstr);
|
||||
}
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
public function getConnectGPS() {
|
||||
global $savvyext;
|
||||
if ($this->conn_gps==NULL || !pg_version($this->conn_gps)) {
|
||||
$db_host = $savvyext->cfgReadChar('gpsdatabase.host');
|
||||
$db_name = $savvyext->cfgReadChar('gpsdatabase.name');
|
||||
$db_user = $savvyext->cfgReadChar('gpsdatabase.user');
|
||||
$db_pass = $savvyext->cfgReadChar('gpsdatabase.pass');
|
||||
$db_port = $savvyext->cfgReadLong('gpsdatabase.port');
|
||||
$connstr = "host=${db_host} port=${db_port} dbname=${db_name} user=${db_user} password=${db_pass}";
|
||||
$this->conn_gps = pg_connect($connstr);
|
||||
}
|
||||
return $this->conn_gps;
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
if(!empty($this->conn)){
|
||||
pg_close($this->conn);
|
||||
}
|
||||
if(!empty($this->conn_gps)){
|
||||
pg_close($this->conn_gps);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vi:ts=2
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
class Gis {
|
||||
|
||||
final public static function haversineDistanceBetweenTwoGpsCoordinates($lat1, $lon1, $lat2, $lon2, $unit="M") {
|
||||
if (($lat1 == $lat2) && ($lon1 == $lon2)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
$theta = $lon1 - $lon2;
|
||||
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
|
||||
$dist = acos($dist);
|
||||
$dist = rad2deg($dist);
|
||||
$miles = $dist * 60 * 1.1515;
|
||||
$unit = strtoupper($unit);
|
||||
|
||||
if ($unit == "K") {
|
||||
return ($miles * 1.609344);
|
||||
} else if ($unit == "N") {
|
||||
return ($miles * 0.8684);
|
||||
} else {
|
||||
return $miles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final public static function cosinesDistanceBetweenTwoGpsCoordinates($lat1, $lon1, $lat2, $lon2, $unit="M") {
|
||||
if (($lat1 == $lat2) && ($lon1 == $lon2)) {
|
||||
return 0;
|
||||
}
|
||||
$dist = acos(sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($lon2 - $lon1))) * 6371;
|
||||
$unit = strtoupper($unit);
|
||||
if ($unit == "K") {
|
||||
return $dist;
|
||||
} else {
|
||||
return $dist/1.609344;
|
||||
}
|
||||
}
|
||||
|
||||
final public static function getCityServicesAvailableForCoordinates($db_conn ,$lat, $lng, $providers=[]) {
|
||||
return self::getServicesAvailableForCoordinates($db_conn, self::GET_CITY_PROVIDERS_IN_RADIUS, $lat, $lng, $providers);
|
||||
}
|
||||
|
||||
final public static function getCountryServicesAvailableForCoordinates($db_conn ,$lat, $lng, $providers=[]) {
|
||||
return self::getServicesAvailableForCoordinates($db_conn, self::GET_COUNTRY_PROVIDERS_IN_RADIUS, $lat, $lng, $providers);
|
||||
}
|
||||
|
||||
final protected static function getServicesAvailableForCoordinates($db_conn, $q, $lat, $lng, $providers=[]) {
|
||||
|
||||
$result = [];
|
||||
|
||||
$params = [ $lng, $lat];
|
||||
|
||||
if (count($providers)) {
|
||||
$params[]="{".implode(",", $providers). "}";
|
||||
$q = $q."AND transport_provider_id = ANY($3)";
|
||||
}
|
||||
$req_result = pg_query_params($db_conn, $q, $params);
|
||||
if (!$req_result) {
|
||||
return $result;
|
||||
}
|
||||
while ($row = pg_fetch_assoc($req_result)) {
|
||||
array_push($result, $row);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
const GET_CITY_PROVIDERS_IN_RADIUS = "
|
||||
SELECT cs.* FROM geofence_area_city gac
|
||||
LEFT JOIN city_services cs ON gac.id = cs.city_id
|
||||
WHERE
|
||||
ST_DistanceSphere(location::geometry, ST_SetSRID(ST_MakePoint($1,$2),4326)) <= radius
|
||||
";
|
||||
|
||||
const GET_COUNTRY_PROVIDERS_IN_RADIUS = "
|
||||
SELECT cs.* FROM geofence_area_country gac
|
||||
LEFT JOIN country_services cs ON gac.id = cs.country_id
|
||||
WHERE
|
||||
ST_DistanceSphere(location::geometry, ST_SetSRID(ST_MakePoint($1,$2),4326)) <= radius
|
||||
";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
# Imports the Google Cloud client libraries
|
||||
use Google\ApiCore\ApiException;
|
||||
use Google\Cloud\Kms\V1\CryptoKey;
|
||||
use Google\Cloud\Kms\V1\CryptoKey\CryptoKeyPurpose;
|
||||
use Google\Cloud\Kms\V1\KeyManagementServiceClient;
|
||||
use Google\Cloud\Kms\V1\KeyRing;
|
||||
|
||||
class GoogleKMS {
|
||||
|
||||
private $client;
|
||||
private $projectId;
|
||||
private $authFile;
|
||||
|
||||
private $keyRing = NULL;
|
||||
private $keyRingId = NULL;
|
||||
private $keyRingName = NULL;
|
||||
private $keyName = NULL;
|
||||
private $cryptoKey = NULL;
|
||||
private $location = 'global';
|
||||
|
||||
public function __construct($projectId, $authFile, $keyRingId=NULL, $keyId=NULL) {
|
||||
// Your Google Cloud Platform project ID
|
||||
$this->projectId = $projectId; // 'float-app-224118';
|
||||
// The file path to credentials JSON
|
||||
//error_log($authFile);
|
||||
putenv("GOOGLE_APPLICATION_CREDENTIALS=${authFile}");
|
||||
apache_setenv("GOOGLE_APPLICATION_CREDENTIALS",$authFile,true);
|
||||
$this->authFile = $authFile; // './float-app-224118-52ef1783d2c5.json';
|
||||
// Instantiates a client
|
||||
$this->client = new KeyManagementServiceClient([
|
||||
'projectId' => $projectId,
|
||||
'keyFile' => json_decode(file_get_contents($authFile), true)
|
||||
]);
|
||||
if ($keyRingId!=NULL) {
|
||||
$this->createKeyring($keyRingId);
|
||||
if ($keyId!=NULL) {
|
||||
$this->createCryptokey($keyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createKeyring($keyRingId) {
|
||||
try {
|
||||
$locationName = $this->client::locationName(
|
||||
$this->projectId,
|
||||
$this->location
|
||||
);
|
||||
$keyRingName = $this->client::keyRingName(
|
||||
$this->projectId,
|
||||
$this->location,
|
||||
$keyRingId
|
||||
);
|
||||
$this->keyRing = $this->client->getKeyRing($keyRingName);
|
||||
$this->keyRingId = $keyRingId;
|
||||
$this->keyRingName = $keyRingName;
|
||||
} catch (ApiException $e) {
|
||||
if ($e->getStatus() === 'NOT_FOUND') {
|
||||
$this->keyRing = new KeyRing();
|
||||
$this->keyRing->setName($keyRingName);
|
||||
$this->client->createKeyRing(
|
||||
$locationName,
|
||||
$keyRingId,
|
||||
$this->keyRing);
|
||||
$this->keyRingId = $keyRingId;
|
||||
$this->keyRingName = $keyRingName;
|
||||
}
|
||||
}
|
||||
return $this->keyRing;
|
||||
}
|
||||
|
||||
public function createCryptokey($keyId) {
|
||||
try {
|
||||
$keyName = $this->client::cryptoKeyName(
|
||||
$this->projectId,
|
||||
$this->location,
|
||||
$this->keyRingId,
|
||||
$keyId);
|
||||
$this->cryptoKey = $this->client->getCryptoKey($keyName);
|
||||
$this->keyName = $keyName;
|
||||
} catch (ApiException $e) {
|
||||
if ($e->getStatus() === 'NOT_FOUND') {
|
||||
$this->cryptoKey = new CryptoKey();
|
||||
$this->cryptoKey->setPurpose(CryptoKeyPurpose::ENCRYPT_DECRYPT);
|
||||
$this->cryptoKey = $this->client->createCryptoKey(
|
||||
$this->keyRingName,
|
||||
$keyId,
|
||||
$this->cryptoKey);
|
||||
$this->keyName = $keyName;
|
||||
}
|
||||
}
|
||||
return $this->cryptoKey;
|
||||
}
|
||||
|
||||
public function encrypt($secret) {
|
||||
$response = $this->client->encrypt($this->keyName, $secret);
|
||||
$cipherText = $response->getCiphertext();
|
||||
return $cipherText;
|
||||
}
|
||||
|
||||
public function decrypt($cipherText) {
|
||||
$response = $this->client->decrypt($this->keyName, $cipherText);
|
||||
$plainText = $response->getPlaintext();
|
||||
return $plainText;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
class GooglePlacesClient
|
||||
{
|
||||
public function get($url)
|
||||
{
|
||||
$curl = curl_init();
|
||||
$options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
);
|
||||
curl_setopt_array($curl, $options);
|
||||
$response = curl_exec($curl);
|
||||
if ($error = curl_error($curl))
|
||||
{
|
||||
throw new \Exception('CURL Error: ' . $error);
|
||||
}
|
||||
curl_close($curl);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
class GooglePlaces
|
||||
{
|
||||
public $client = '';
|
||||
public $sleep = 3;
|
||||
private $key = '';
|
||||
private $base_url = 'https://maps.googleapis.com/maps/api/place';
|
||||
private $method = null;
|
||||
private $response = null;
|
||||
|
||||
public $keyword = null;
|
||||
public $language = 'en';
|
||||
public $location = null;
|
||||
public $output = 'json';
|
||||
public $name = null;
|
||||
public $pagetoken = null;
|
||||
public $radius = null;
|
||||
public $rankby = 'prominence';
|
||||
public $sensor = false;
|
||||
public $types = null;
|
||||
public $placeid = null;
|
||||
public $reference = null;
|
||||
public $opennow = null;
|
||||
public $input = null;
|
||||
|
||||
public $subradius = null;
|
||||
public $getmax = true;
|
||||
private $grid = null;
|
||||
|
||||
private $exceptions = array(
|
||||
'base_url', 'client', 'exceptions', 'getmax', 'grid', 'method',
|
||||
'output', 'pagetoken', 'response', 'sleep', 'subradius',
|
||||
);
|
||||
|
||||
public function __construct($key, $client = false)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->client = $client ? $client : new GooglePlacesClient();
|
||||
}
|
||||
|
||||
function __set($variable, $value)
|
||||
{
|
||||
// Compensates for mixed variable naming
|
||||
$variable = str_replace('_', '', strtolower($variable));
|
||||
$this->$variable = $value;
|
||||
}
|
||||
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
$this->output = strtolower($this->output);
|
||||
|
||||
if (!in_array($this->output, array('json', 'xml')))
|
||||
{
|
||||
throw new \Exception('Invalid output, please specify either "json" or "xml".');
|
||||
}
|
||||
|
||||
$method = $this->method = strtolower($method);
|
||||
$url = implode('/', array($this->base_url, $method, $this->output));
|
||||
$parameters = array();
|
||||
$parameters = $this->parameterBuilder($parameters);
|
||||
$parameters = $this->methodChecker($parameters, $method);
|
||||
|
||||
if (!empty($this->subradius))
|
||||
{
|
||||
return $this->subdivide($url, $parameters);
|
||||
}
|
||||
|
||||
if ($this->pagetoken !== null)
|
||||
{
|
||||
$parameters['pagetoken'] = $this->pagetoken;
|
||||
}
|
||||
|
||||
$result = $this->queryGoogle($url, $parameters);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through all of our variables to make a parameter list
|
||||
*/
|
||||
private function parameterBuilder($parameters)
|
||||
{
|
||||
foreach (get_object_vars($this) as $variable => $value)
|
||||
{
|
||||
// Except these variables
|
||||
if (!in_array($variable, $this->exceptions))
|
||||
{
|
||||
// Assuming it's not null
|
||||
if ($value !== null)
|
||||
{
|
||||
// Converts boolean to string
|
||||
if (is_bool($value))
|
||||
{
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
switch ($variable)
|
||||
{
|
||||
// Allows LatLng to be passed as an array
|
||||
case 'location':
|
||||
if (is_array($value))
|
||||
{
|
||||
// Just in case it's an associative array
|
||||
$value = array_values($value);
|
||||
$value = $value[0] . ',' . $value[1];
|
||||
}
|
||||
break;
|
||||
|
||||
// Checks that it's a value rank by value
|
||||
case 'rankby':
|
||||
$value = strtolower($value);
|
||||
|
||||
if (!in_array($value, array('prominence', 'distance')))
|
||||
{
|
||||
throw new \Exception('Invalid rank by value, please specify either "prominence" or "distance".');
|
||||
}
|
||||
break;
|
||||
|
||||
// Allows types to be passed as an array
|
||||
case 'types':
|
||||
if (is_array($value))
|
||||
{
|
||||
$value = implode('|', $value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$parameters[$variable] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* takes the parameters and method to throw exceptions or modify parameters as needed
|
||||
* @todo Method to sanity check passed types
|
||||
*/
|
||||
private function methodChecker($parameters, $method)
|
||||
{
|
||||
if (!isset($parameters['pagetoken']))
|
||||
{
|
||||
switch ($method)
|
||||
{
|
||||
case 'nearbysearch':
|
||||
if (!isset($parameters['location']))
|
||||
{
|
||||
throw new \Exception('You must specify a location before calling nearbysearch().');
|
||||
}
|
||||
elseif (isset($parameters['rankby']))
|
||||
{
|
||||
switch ($parameters['rankby'])
|
||||
{
|
||||
case 'distance':
|
||||
if (!isset($parameters['keyword'])
|
||||
&& !isset($parameters['name'])
|
||||
&& !isset($parameters['types']))
|
||||
{
|
||||
throw new \Exception('You much specify at least one of the following: "keyword", "name", "types".');
|
||||
}
|
||||
|
||||
if (isset($parameters['radius']))
|
||||
{
|
||||
unset($this->radius, $parameters['radius']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'prominence':
|
||||
if (!isset($parameters['radius']))
|
||||
{
|
||||
throw new \Exception('You must specify a radius.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'radarsearch':
|
||||
if (!isset($parameters['location']))
|
||||
{
|
||||
throw new \Exception('You must specify a location before calling radarsearch().');
|
||||
}
|
||||
elseif (!isset($parameters['radius']))
|
||||
{
|
||||
throw new \Exception('You must specify a radius.');
|
||||
}
|
||||
elseif (empty($parameters['keyword'])
|
||||
&& empty($parameters['name'])
|
||||
&& empty($parameters['types']))
|
||||
{
|
||||
throw new \Exception('You much specify at least one of the following: "keyword", "name", "types".');
|
||||
}
|
||||
|
||||
if (isset($parameters['rankby']))
|
||||
{
|
||||
unset($this->rankby, $parameters['rankby']);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'details':
|
||||
if (!(isset($parameters['reference'])
|
||||
^ isset($parameters['placeid'])))
|
||||
{
|
||||
throw new \Exception('You must specify either a "placeid" or a "reference" (but not both) before calling details().');
|
||||
}
|
||||
|
||||
if (isset($parameters['rankby']))
|
||||
{
|
||||
unset($this->rankby, $parameters['rankby']);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'autocomplete':
|
||||
/*if (!isset($parameters['location']))
|
||||
{
|
||||
throw new \Exception('You must specify a location before calling autocomplete().');
|
||||
}
|
||||
elseif (!isset($parameters['radius']))
|
||||
{
|
||||
throw new \Exception('You must specify a radius.');
|
||||
}
|
||||
else*/if (empty($parameters['input']))
|
||||
{
|
||||
throw new \Exception('You much specify the user entered input string.');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Submits request via curl, sets the response, then returns the response
|
||||
*/
|
||||
private function queryGoogle($url, $parameters)
|
||||
{
|
||||
if ($this->pagetoken !== null)
|
||||
{
|
||||
$parameters['pagetoken'] = $this->pagetoken;
|
||||
sleep($this->sleep);
|
||||
}
|
||||
|
||||
// Couldn't seem to get http_build_query() to work right so...
|
||||
$querystring = '';
|
||||
|
||||
foreach ($parameters as $variable => $value)
|
||||
{
|
||||
if ($querystring != '')
|
||||
{
|
||||
$querystring .= '&';
|
||||
}
|
||||
|
||||
$querystring .= $variable . '=' . urlencode($value);
|
||||
}
|
||||
|
||||
$response = $this->client->get($url . '?' . $querystring);
|
||||
|
||||
if ($this->output == 'json')
|
||||
{
|
||||
$response = json_decode($response, true);
|
||||
|
||||
if ($response === null)
|
||||
{
|
||||
throw new \Exception('The returned JSON was malformed or nonexistent.');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \Exception('XML is terrible, don\'t use it, ever.');
|
||||
}
|
||||
|
||||
$this->response = $response;
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the longitude equal to a given distance (meters) at a given latitude
|
||||
*/
|
||||
public function meters2lng($meters, $latitude)
|
||||
{
|
||||
return $meters / (cos(deg2rad($latitude)) * 40075160 / 360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latitude equal to a given distance (meters)
|
||||
*/
|
||||
public function meters2lat($meters)
|
||||
{
|
||||
return $meters / (40075160 / 360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aggregated responses for a subdivided search
|
||||
*/
|
||||
private function subdivide($url, $parameters)
|
||||
{
|
||||
if ($this->subradius < 200)
|
||||
{
|
||||
throw new \Exception('Subradius should be at least 200 meters.');
|
||||
}
|
||||
|
||||
$quotient = $parameters['radius'] / $this->subradius;
|
||||
|
||||
if ($parameters['radius'] % $this->subradius || $quotient % 2)
|
||||
{
|
||||
throw new \Exception('Subradius should divide evenly into radius.');
|
||||
}
|
||||
|
||||
$center = explode(',', $parameters['location']);
|
||||
$centerlat = $center[0];
|
||||
$centerlng = $center[1];
|
||||
$count = $quotient;
|
||||
$lati = $this->meters2lat($this->subradius * 2);
|
||||
|
||||
$this->grid['results'] = array();
|
||||
|
||||
for ($i = $count / 2 * -1; $i <= $count / 2; $i++)
|
||||
{
|
||||
$lat = $centerlat + $i * $lati;
|
||||
$lngi = $this->meters2lng($this->subradius * 2, $lat);
|
||||
|
||||
for ($j = $count / 2 * -1; $j <= $count / 2; $j++)
|
||||
{
|
||||
$lng = $centerlng + $j * $lngi;
|
||||
$loc = $lat . ',' . $lng;
|
||||
|
||||
$parameters['location'] = $loc;
|
||||
$parameters['radius'] = $this->subradius;
|
||||
|
||||
$pagetoken = true;
|
||||
|
||||
while ($pagetoken)
|
||||
{
|
||||
$this->queryGoogle($url, $parameters);
|
||||
|
||||
$this->grid[$i][$j] = $this->response;
|
||||
|
||||
$this->grid['results'] = array_merge(
|
||||
$this->grid['results'],
|
||||
$this->response['results']
|
||||
);
|
||||
|
||||
if (isset($this->response['next_page_token']))
|
||||
{
|
||||
$this->pagetoken = $this->response['next_page_token'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->pagetoken = null;
|
||||
$pagetoken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->grid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
class Logger
|
||||
{
|
||||
protected static $instance;
|
||||
private static $logger;
|
||||
|
||||
const ERROR = 0;
|
||||
const WARNING = 1;
|
||||
const INFO = 2;
|
||||
const DEBUG = 3;
|
||||
const DEBUG1 = 4;
|
||||
const DEBUG2 = 5;
|
||||
const DEBUG3 = 6;
|
||||
const DEBUG4 = 7;
|
||||
const SQL = 8;
|
||||
const FLOG_MAX = 9;
|
||||
|
||||
const LEVELS = [
|
||||
'ERROR', 'WARNING', 'INFO', 'DEBUG', 'DEBUG1', 'DEBUG2', 'DEBUG3', 'DEBUG4', 'SQL', 'FLOG_MAX'
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function getLogger()
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::initLogger();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function initLogger()
|
||||
{
|
||||
global $savvyext;
|
||||
$fluent_host = $savvyext->cfgReadChar('phplogger.host');
|
||||
$fluent_port = $savvyext->cfgReadLong('phplogger.port');
|
||||
$log_enabled = $savvyext->cfgReadLong('phplogger.enabled');
|
||||
|
||||
if ($log_enabled == 1) {
|
||||
self::$logger = new Fluent\Logger\FluentLogger($fluent_host, $fluent_port);
|
||||
}
|
||||
self::$instance = self::$logger;
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $arguments){
|
||||
if(is_array($arguments) && count($arguments)>0 && in_array(strtoupper($name), Logger::LEVELS)) {
|
||||
$data = $arguments[0];
|
||||
$tag = NULL;
|
||||
if (count($arguments)>1) {
|
||||
$tag = $arguments[1];
|
||||
}
|
||||
if (count($arguments)>2) {
|
||||
$name = $arguments[1];
|
||||
$tag = $arguments[2];
|
||||
}
|
||||
$level = Logger::levelByName($name);
|
||||
return Logger::log($level, $data, $name, $tag);
|
||||
}
|
||||
error_log("Logger::__callStatic($name, \$arguments) => Invalid method name!");
|
||||
}
|
||||
|
||||
public static function levelByName($name) {
|
||||
$val = strtoupper($name);
|
||||
if(in_array($val, Logger::LEVELS)) {
|
||||
return array_search($val, Logger::LEVELS, false);
|
||||
} else {
|
||||
return Logger::FLOG_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
public static function nameByLevel($level) {
|
||||
$level = (int)$level;
|
||||
return Logger::ERROR < $level || $level > Logger::FLOG_MAX ? Logger::LEVELS[Logger::FLOG_MAX] : Logger::LEVELS[$level];
|
||||
}
|
||||
|
||||
public static function log($level, $data = [], $name = NULL, $tag = NULL)
|
||||
{
|
||||
global $savvyext;
|
||||
$clevel = $savvyext->cfgReadLong('phplogger.level');
|
||||
$ilevel = (int)$level;
|
||||
if ($ilevel > $clevel) {
|
||||
error_log("Logger::log() invalid log level! '$level' => '$ilevel' > '$clevel'");
|
||||
return;
|
||||
}
|
||||
$tag = $tag ?? $savvyext->cfgReadChar('phplogger.tag');
|
||||
$name = $name ?? $savvyext->cfgReadChar('phplogger.name');
|
||||
$data = [
|
||||
"log" => $name,
|
||||
"level" => Logger::nameByLevel($level),
|
||||
"pid" => getmypid(),
|
||||
"zz" => is_array($data) || is_object($data) ? json_encode($data) : $data,
|
||||
];
|
||||
if (self::getLogger()) {
|
||||
self::getLogger()->post($tag, $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Polyline
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* A simple class to handle polyline-encoding for Google Maps
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Mapping
|
||||
* @package Polyline
|
||||
* @author E. McConville <emcconville@emcconville.com>
|
||||
* @copyright 2009-2015 E. McConville
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3
|
||||
* @version GIT: $Id$
|
||||
* @link https://github.com/emcconville/google-map-polyline-encoding-tool
|
||||
*/
|
||||
|
||||
/**
|
||||
* Polyline encoding & decoding class
|
||||
*
|
||||
* Convert list of points to encoded string following Google's Polyline
|
||||
* Algorithm.
|
||||
*
|
||||
* @category Mapping
|
||||
* @package Polyline
|
||||
* @author E. McConville <emcconville@emcconville.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3
|
||||
* @link https://github.com/emcconville/google-map-polyline-encoding-tool
|
||||
*/
|
||||
class Polyline
|
||||
{
|
||||
/**
|
||||
* Default precision level of 1e-5.
|
||||
*
|
||||
* Overwrite this property in extended class to adjust precision of numbers.
|
||||
* !!!CAUTION!!!
|
||||
* 1) Adjusting this value will not guarantee that third party
|
||||
* libraries will understand the change.
|
||||
* 2) Float point arithmetic IS NOT real number arithmetic. PHP's internal
|
||||
* float precision may contribute to undesired rounding.
|
||||
*
|
||||
* @var int $precision
|
||||
*/
|
||||
protected static $precision = 5;
|
||||
|
||||
/**
|
||||
* Apply Google Polyline algorithm to list of points.
|
||||
*
|
||||
* @param array $points List of points to encode. Can be a list of tuples,
|
||||
* or a flat, one-dimensional array.
|
||||
*
|
||||
* @return string encoded string
|
||||
*/
|
||||
final public static function encode( $points )
|
||||
{
|
||||
$points = self::flatten($points);
|
||||
$encodedString = '';
|
||||
$index = 0;
|
||||
$previous = array(0,0);
|
||||
foreach ( $points as $number ) {
|
||||
$number = (float)($number);
|
||||
$number = (int)round($number * pow(10, static::$precision));
|
||||
$diff = $number - $previous[$index % 2];
|
||||
$previous[$index % 2] = $number;
|
||||
$number = $diff;
|
||||
$index++;
|
||||
$number = ($number < 0) ? ~($number << 1) : ($number << 1);
|
||||
$chunk = '';
|
||||
while ( $number >= 0x20 ) {
|
||||
$chunk .= chr((0x20 | ($number & 0x1f)) + 63);
|
||||
$number >>= 5;
|
||||
}
|
||||
$chunk .= chr($number + 63);
|
||||
$encodedString .= $chunk;
|
||||
}
|
||||
return $encodedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse Google Polyline algorithm on encoded string.
|
||||
*
|
||||
* @param string $string Encoded string to extract points from.
|
||||
*
|
||||
* @return array points
|
||||
*/
|
||||
final public static function decode( $string )
|
||||
{
|
||||
$points = array();
|
||||
$index = $i = 0;
|
||||
$previous = array(0,0);
|
||||
while ($i < strlen($string)) {
|
||||
$shift = $result = 0x00;
|
||||
do {
|
||||
$bit = ord(substr($string, $i++)) - 63;
|
||||
$result |= ($bit & 0x1f) << $shift;
|
||||
$shift += 5;
|
||||
} while ($bit >= 0x20);
|
||||
|
||||
$diff = ($result & 1) ? ~($result >> 1) : ($result >> 1);
|
||||
$number = $previous[$index % 2] + $diff;
|
||||
$previous[$index % 2] = $number;
|
||||
$index++;
|
||||
$points[] = $number * 1 / pow(10, static::$precision);
|
||||
}
|
||||
return $points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce multi-dimensional to single list
|
||||
*
|
||||
* @param array $array Subject array to flatten.
|
||||
*
|
||||
* @return array flattened
|
||||
*/
|
||||
final public static function flatten( $array )
|
||||
{
|
||||
$flatten = array();
|
||||
array_walk_recursive(
|
||||
$array, // @codeCoverageIgnore
|
||||
function ($current) use (&$flatten) {
|
||||
$flatten[] = $current;
|
||||
}
|
||||
);
|
||||
return $flatten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concat list into pairs of points
|
||||
*
|
||||
* @param array $list One-dimensional array to segment into list of tuples.
|
||||
*
|
||||
* @return array pairs
|
||||
*/
|
||||
final public static function pair( $list )
|
||||
{
|
||||
return is_array($list) ? array_chunk($list, 2) : array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
//namespace Utils;
|
||||
|
||||
/**
|
||||
* Class RandomStringGenerator
|
||||
* @package Utils
|
||||
*
|
||||
* Solution taken from here:
|
||||
* http://stackoverflow.com/a/13733588/1056679
|
||||
*/
|
||||
class RandomStringGenerator
|
||||
{
|
||||
/** @var string */
|
||||
protected $alphabet;
|
||||
|
||||
/** @var int */
|
||||
protected $alphabetLength;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $alphabet
|
||||
*/
|
||||
public function __construct($alphabet = '')
|
||||
{
|
||||
if ('' !== $alphabet) {
|
||||
$this->setAlphabet($alphabet);
|
||||
} else {
|
||||
$this->setAlphabet(
|
||||
implode(range('a', 'z'))
|
||||
. implode(range('A', 'Z'))
|
||||
. implode(range(0, 9))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $alphabet
|
||||
*/
|
||||
public function setAlphabet($alphabet)
|
||||
{
|
||||
$this->alphabet = $alphabet;
|
||||
$this->alphabetLength = strlen($alphabet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public function generate($length)
|
||||
{
|
||||
$token = '';
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$randomKey = $this->getRandomInteger(0, $this->alphabetLength);
|
||||
$token .= $this->alphabet[$randomKey];
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return int
|
||||
*/
|
||||
protected function getRandomInteger($min, $max)
|
||||
{
|
||||
$range = ($max - $min);
|
||||
|
||||
if ($range < 0) {
|
||||
// Not so random...
|
||||
return $min;
|
||||
}
|
||||
|
||||
$log = log($range, 2);
|
||||
|
||||
// Length in bytes.
|
||||
$bytes = (int) ($log / 8) + 1;
|
||||
|
||||
// Length in bits.
|
||||
$bits = (int) $log + 1;
|
||||
|
||||
// Set all lower bits to 1.
|
||||
$filter = (int) (1 << $bits) - 1;
|
||||
|
||||
do {
|
||||
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
|
||||
|
||||
// Discard irrelevant bits.
|
||||
$rnd = $rnd & $filter;
|
||||
|
||||
} while ($rnd >= $range);
|
||||
|
||||
return ($min + $rnd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
class Utilities
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function convertUtcToLocal($datetime, $timezone, $format = 'Y-m-d H:i:s')
|
||||
{
|
||||
if (!empty($datetime) && !empty($timezone)) {
|
||||
$datetime = date($format, strtotime($datetime));
|
||||
$utc_date = DateTime::createFromFormat(
|
||||
$format,
|
||||
$datetime,
|
||||
new DateTimeZone('UTC')
|
||||
);
|
||||
if ($utc_date) {
|
||||
$utc_date->setTimeZone(new DateTimeZone($timezone));
|
||||
return $utc_date->format($format);
|
||||
}
|
||||
}
|
||||
return $datetime;
|
||||
}
|
||||
|
||||
public static function getClientIpAddress()
|
||||
{
|
||||
$ip="";
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
//ip from share internet
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
//ip pass from proxy
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
} else {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
public static function startBenchmark(&$benchmark=[]){
|
||||
$benchmark['start'] = microtime(true) * 1000;
|
||||
}
|
||||
|
||||
public static function finishBenchmark(&$benchmark){
|
||||
$benchmark['finish'] = microtime(true) * 1000;
|
||||
$benchmark['duration'] = $benchmark['finish'] - $benchmark['start'];
|
||||
error_log('benchmark'.json_encode($benchmark));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to validate the email address
|
||||
*
|
||||
* @author CodexWorld.com <contact@codexworld.com>
|
||||
* @copyright Copyright (c) 2018, CodexWorld.com
|
||||
* @url https://www.codexworld.com
|
||||
*/
|
||||
class VerifyEmail {
|
||||
|
||||
protected $stream = false;
|
||||
|
||||
/**
|
||||
* SMTP port number
|
||||
* @var int
|
||||
*/
|
||||
protected $port = 25;
|
||||
|
||||
/**
|
||||
* Email address for request
|
||||
* @var string
|
||||
*/
|
||||
protected $from = 'root@localhost';
|
||||
|
||||
/**
|
||||
* The connection timeout, in seconds.
|
||||
* @var int
|
||||
*/
|
||||
protected $max_connection_timeout = 30;
|
||||
|
||||
/**
|
||||
* Timeout value on stream, in seconds.
|
||||
* @var int
|
||||
*/
|
||||
protected $stream_timeout = 5;
|
||||
|
||||
/**
|
||||
* Wait timeout on stream, in seconds.
|
||||
* * 0 - not wait
|
||||
* @var int
|
||||
*/
|
||||
protected $stream_timeout_wait = 0;
|
||||
|
||||
/**
|
||||
* Whether to throw exceptions for errors.
|
||||
* @type boolean
|
||||
* @access protected
|
||||
*/
|
||||
protected $exceptions = false;
|
||||
|
||||
/**
|
||||
* The number of errors encountered.
|
||||
* @type integer
|
||||
* @access protected
|
||||
*/
|
||||
protected $error_count = 0;
|
||||
|
||||
/**
|
||||
* class debug output mode.
|
||||
* @type boolean
|
||||
*/
|
||||
public $Debug = false;
|
||||
|
||||
/**
|
||||
* How to handle debug output.
|
||||
* Options:
|
||||
* * `echo` Output plain-text as-is, appropriate for CLI
|
||||
* * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
|
||||
* * `log` Output to error log as configured in php.ini
|
||||
* @type string
|
||||
*/
|
||||
public $Debugoutput = 'echo';
|
||||
|
||||
/**
|
||||
* SMTP RFC standard line ending.
|
||||
*/
|
||||
const CRLF = "\r\n";
|
||||
|
||||
/**
|
||||
* Holds the most recent error message.
|
||||
* @type string
|
||||
*/
|
||||
public $ErrorInfo = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param boolean $exceptions Should we throw external exceptions?
|
||||
*/
|
||||
public function __construct($exceptions = false) {
|
||||
$this->exceptions = (boolean) $exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set email address for SMTP request
|
||||
* @param string $email Email address
|
||||
*/
|
||||
public function setEmailFrom($email) {
|
||||
if (!self::validate($email)) {
|
||||
$this->set_error('Invalid address : ' . $email);
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
}
|
||||
$this->from = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection timeout, in seconds.
|
||||
* @param int $seconds
|
||||
*/
|
||||
public function setConnectionTimeout($seconds) {
|
||||
if ($seconds > 0) {
|
||||
$this->max_connection_timeout = (int) $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeout value on stream, expressed in the seconds
|
||||
* @param int $seconds
|
||||
*/
|
||||
public function setStreamTimeout($seconds) {
|
||||
if ($seconds > 0) {
|
||||
$this->stream_timeout = (int) $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
public function setStreamTimeoutWait($seconds) {
|
||||
if ($seconds >= 0) {
|
||||
$this->stream_timeout_wait = (int) $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email address.
|
||||
* @param string $email
|
||||
* @return boolean True if valid.
|
||||
*/
|
||||
public static function validate($email) {
|
||||
return (boolean) filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of MX records for host. Sort by weight information.
|
||||
* @param string $hostname The Internet host name.
|
||||
* @return array Array of the MX records found.
|
||||
*/
|
||||
public function getMXrecords($hostname) {
|
||||
$mxhosts = array();
|
||||
$mxweights = array();
|
||||
if (getmxrr($hostname, $mxhosts, $mxweights) === FALSE) {
|
||||
$this->set_error('MX records not found or an error occurred');
|
||||
$this->edebug($this->ErrorInfo);
|
||||
} else {
|
||||
array_multisort($mxweights, $mxhosts);
|
||||
}
|
||||
/**
|
||||
* Add A-record as last chance (e.g. if no MX record is there).
|
||||
* Thanks Nicht Lieb.
|
||||
* @link http://www.faqs.org/rfcs/rfc2821.html RFC 2821 - Simple Mail Transfer Protocol
|
||||
*/
|
||||
if (empty($mxhosts)) {
|
||||
$mxhosts[] = $hostname;
|
||||
}
|
||||
return $mxhosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses input string to array(0=>user, 1=>domain)
|
||||
* @param string $email
|
||||
* @param boolean $only_domain
|
||||
* @return string|array
|
||||
* @access private
|
||||
*/
|
||||
public static function parse_email($email, $only_domain = TRUE) {
|
||||
sscanf($email, "%[^@]@%s", $user, $domain);
|
||||
return ($only_domain) ? $domain : array($user, $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error message to the error container.
|
||||
* @access protected
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
protected function set_error($msg) {
|
||||
$this->error_count++;
|
||||
$this->ErrorInfo = $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an error occurred.
|
||||
* @access public
|
||||
* @return boolean True if an error did occur.
|
||||
*/
|
||||
public function isError() {
|
||||
return ($this->error_count > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output debugging info
|
||||
* Only generates output if debug output is enabled
|
||||
* @see verifyEmail::$Debugoutput
|
||||
* @see verifyEmail::$Debug
|
||||
* @param string $str
|
||||
*/
|
||||
protected function edebug($str) {
|
||||
if (!$this->Debug) {
|
||||
return;
|
||||
}
|
||||
switch ($this->Debugoutput) {
|
||||
case 'log':
|
||||
//Don't output, just log
|
||||
error_log($str);
|
||||
break;
|
||||
case 'html':
|
||||
//Cleans up output a bit for a better looking, HTML-safe output
|
||||
echo htmlentities(
|
||||
preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8'
|
||||
)
|
||||
. "<br>\n";
|
||||
break;
|
||||
case 'echo':
|
||||
default:
|
||||
//Normalize line breaks
|
||||
$str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
|
||||
echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
|
||||
"\n", "\n \t ", trim($str)
|
||||
) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email
|
||||
* @param string $email Email address
|
||||
* @return boolean True if the valid email also exist
|
||||
*/
|
||||
public function check($email) {
|
||||
$result = FALSE;
|
||||
|
||||
if (!self::validate($email)) {
|
||||
$this->set_error("{$email} incorrect e-mail");
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
$this->error_count = 0; // Reset errors
|
||||
$this->stream = FALSE;
|
||||
|
||||
$mxs = $this->getMXrecords(self::parse_email($email));
|
||||
$timeout = ceil($this->max_connection_timeout / count($mxs));
|
||||
foreach ($mxs as $host) {
|
||||
/**
|
||||
* suppress error output from stream socket client...
|
||||
* Thanks Michael.
|
||||
*/
|
||||
$this->stream = @stream_socket_client("tcp://" . $host . ":" . $this->port, $errno, $errstr, $timeout);
|
||||
if ($this->stream === FALSE) {
|
||||
if ($errno == 0) {
|
||||
$this->set_error("Problem initializing the socket");
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
return FALSE;
|
||||
} else {
|
||||
$this->edebug($host . ":" . $errstr);
|
||||
}
|
||||
} else {
|
||||
stream_set_timeout($this->stream, $this->stream_timeout);
|
||||
stream_set_blocking($this->stream, 1);
|
||||
|
||||
if ($this->_streamCode($this->_streamResponse()) == '220') {
|
||||
$this->edebug("Connection success {$host}");
|
||||
break;
|
||||
} else {
|
||||
fclose($this->stream);
|
||||
$this->stream = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->stream === FALSE) {
|
||||
$this->set_error("All connection fails");
|
||||
$this->edebug($this->ErrorInfo);
|
||||
if ($this->exceptions) {
|
||||
throw new verifyEmailException($this->ErrorInfo);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$this->_streamQuery("HELO " . self::parse_email($this->from));
|
||||
$this->_streamResponse();
|
||||
$this->_streamQuery("MAIL FROM: <{$this->from}>");
|
||||
$this->_streamResponse();
|
||||
$this->_streamQuery("RCPT TO: <{$email}>");
|
||||
$code = $this->_streamCode($this->_streamResponse());
|
||||
$this->_streamResponse();
|
||||
$this->_streamQuery("RSET");
|
||||
$this->_streamResponse();
|
||||
$code2 = $this->_streamCode($this->_streamResponse());
|
||||
$this->_streamQuery("QUIT");
|
||||
fclose($this->stream);
|
||||
|
||||
$code = !empty($code2)?$code2:$code;
|
||||
switch ($code) {
|
||||
case '250':
|
||||
/**
|
||||
* http://www.ietf.org/rfc/rfc0821.txt
|
||||
* 250 Requested mail action okay, completed
|
||||
* email address was accepted
|
||||
*/
|
||||
case '450':
|
||||
case '451':
|
||||
case '452':
|
||||
/**
|
||||
* http://www.ietf.org/rfc/rfc0821.txt
|
||||
* 450 Requested action not taken: the remote mail server
|
||||
* does not want to accept mail from your server for
|
||||
* some reason (IP address, blacklisting, etc..)
|
||||
* Thanks Nicht Lieb.
|
||||
* 451 Requested action aborted: local error in processing
|
||||
* 452 Requested action not taken: insufficient system storage
|
||||
* email address was greylisted (or some temporary error occured on the MTA)
|
||||
* i believe that e-mail exists
|
||||
*/
|
||||
return TRUE;
|
||||
case '550':
|
||||
return FALSE;
|
||||
default :
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writes the contents of string to the file stream pointed to by handle
|
||||
* If an error occurs, returns FALSE.
|
||||
* @access protected
|
||||
* @param string $string The string that is to be written
|
||||
* @return string Returns a result code, as an integer.
|
||||
*/
|
||||
protected function _streamQuery($query) {
|
||||
$this->edebug($query);
|
||||
return stream_socket_sendto($this->stream, $query . self::CRLF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all the line long the answer and analyze it.
|
||||
* If an error occurs, returns FALSE
|
||||
* @access protected
|
||||
* @return string Response
|
||||
*/
|
||||
protected function _streamResponse($timed = 0) {
|
||||
$reply = stream_get_line($this->stream, 1);
|
||||
$status = stream_get_meta_data($this->stream);
|
||||
|
||||
if (!empty($status['timed_out'])) {
|
||||
$this->edebug("Timed out while waiting for data! (timeout {$this->stream_timeout} seconds)");
|
||||
}
|
||||
|
||||
if ($reply === FALSE && $status['timed_out'] && $timed < $this->stream_timeout_wait) {
|
||||
return $this->_streamResponse($timed + $this->stream_timeout);
|
||||
}
|
||||
|
||||
|
||||
if ($reply !== FALSE && $status['unread_bytes'] > 0) {
|
||||
$reply .= stream_get_line($this->stream, $status['unread_bytes'], self::CRLF);
|
||||
}
|
||||
$this->edebug($reply);
|
||||
return $reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Response code from Response
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function _streamCode($str) {
|
||||
preg_match('/^(?<code>[0-9]{3})(\s|-)(.*)$/ims', $str, $matches);
|
||||
$code = isset($matches['code']) ? $matches['code'] : false;
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* verifyEmail exception handler
|
||||
*/
|
||||
class verifyEmailException extends Exception {
|
||||
|
||||
/**
|
||||
* Prettify error message output
|
||||
* @return string
|
||||
*/
|
||||
public function errorMessage() {
|
||||
$errorMsg = $this->getMessage();
|
||||
return $errorMsg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require": {
|
||||
"fluent/logger": "^1.0",
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"google/apiclient": "^2.4",
|
||||
"google/cloud-kms": "^1.9",
|
||||
"ramsey/uuid": "^3.8",
|
||||
"phpfastcache/phpfastcache": "^7.1"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user