Added Other AP

This commit is contained in:
dev-chiefworks
2022-04-26 11:30:34 -04:00
parent 5e006a6a21
commit 47f4fad75c
251 changed files with 29298 additions and 4 deletions
+415
View File
@@ -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;
}
}
+51
View File
@@ -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
+83
View File
@@ -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
";
}
+109
View File
@@ -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;
}
}
+388
View File
@@ -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;
}
}
+102
View File
@@ -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);
}
}
}
+152
View File
@@ -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);
}
}
+50
View File
@@ -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));
}
}
+403
View File
@@ -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;
}
}
+10
View File
@@ -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.