Initial version

This commit is contained in:
Benjamin Renard 2017-11-15 05:23:00 +01:00
commit 4673a7a133
28 changed files with 2610 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*~
.*.swp

6
README.txt Normal file
View file

@ -0,0 +1,6 @@
SMS Gateway Application
=======================
Could be use with "My SMS Gateway" Android app :
https://play.google.com/store/apps/details?id=fr.nope.smsgateway

1
data/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
app.log

82
data/mysql.init.sql Normal file
View file

@ -0,0 +1,82 @@
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `smsgw`
--
-- --------------------------------------------------------
--
-- Table structure for table `incoming_msg`
--
CREATE TABLE `incoming_msg` (
`uuid` varchar(40) NOT NULL,
`number` text NOT NULL,
`text` text NOT NULL,
`timestampms` int(11) NOT NULL,
`status` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
-- Table structure for table `outgoing_msg`
--
CREATE TABLE `outgoing_msg` (
`datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`uuid` varchar(40) NOT NULL,
`number` text NOT NULL,
`text` text NOT NULL,
`uniqueid` bigint(11) UNSIGNED NOT NULL,
`nbfrag` int(11) NOT NULL,
`status` varchar(30) NOT NULL DEFAULT 'pending',
`lastupdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
-- Table structure for table `outgoing_msg_frag`
--
CREATE TABLE `outgoing_msg_frag` (
`msguid` bigint(11) UNSIGNED NOT NULL,
`fragid` int(11) NOT NULL,
`status` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `incoming_msg`
--
ALTER TABLE `incoming_msg`
ADD PRIMARY KEY (`uuid`);
--
-- Indexes for table `outgoing_msg`
--
ALTER TABLE `outgoing_msg`
ADD PRIMARY KEY (`uuid`),
ADD UNIQUE KEY `uniqueid` (`uniqueid`);
--
-- Indexes for table `outgoing_msg_frag`
--
ALTER TABLE `outgoing_msg_frag`
ADD PRIMARY KEY (`fragid`,`msguid`);
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

1
includes/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
config.local.php

96
includes/config.inc.php Normal file
View file

@ -0,0 +1,96 @@
<?php
// Log configuration
// Log file path
$log_file=$root_dir_path.'/data/app.log';
// Log level (DEBUG / INFO / WARNING / ERROR)
$log_level='INFO';
// Enable debug mode (on screen)
$debug=False;
// SMS Gateway
$smsgw_url="http://192.168.8.28:8080";
$smsgw_ssl_verify=true;
// Database
// Example for MySQL :
$db_dsn="mysql:host=localhost;dbname=smsgw";
$db_user="smsgw";
$db_pwd="smsgw";
$db_options=array();
// FluentPDO path
$fluentpdo_path="FluentPDO/FluentPDO.php";
/*
* Config Mail
*/
$phpmail_path="Mail.php";
/*
* Méthode d'envoie :
* - mail : envoie avec la méthode PHP mail()
* - sendmail : envoie la commande sendmail du système
* - smtp : envoie en utilisant un serveur SMTP
*/
$mail_send_method='smtp';
/*
* Paramètres d'envoie :
* Ces paramètres dépende de la méthode utilisé. Repporté vous à la documentation
* de PEAR :: Mail pour plus d'information.
* Lien : http://pear.php.net/manual/en/package.mail.mail.factory.php
* Infos :
* List of parameter for the backends
* mail
* o If safe mode is disabled, $params will be passed as the fifth
* argument to the PHP mail() function. If $params is an array,
* its elements will be joined as a space-delimited string.
* sendmail
* o $params["sendmail_path"] - The location of the sendmail program
* on the filesystem. Default is /usr/bin/sendmail.
* o $params["sendmail_args"] - Additional parameters to pass to the
* sendmail. Default is -i.
* smtp
* o $params["host"] - The server to connect. Default is localhost.
* o $params["port"] - The port to connect. Default is 25.
* o $params["auth"] - Whether or not to use SMTP authentication.
* Default is FALSE.
* o $params["username"] - The username to use for SMTP authentication.
* o $params["password"] - The password to use for SMTP authentication.
* o $params["localhost"] - The value to give when sending EHLO or HELO.
* Default is localhost
* o $params["timeout"] - The SMTP connection timeout.
* Default is NULL (no timeout).
* o $params["verp"] - Whether to use VERP or not. Default is FALSE.
* o $params["debug"] - Whether to enable SMTP debug mode or not.
* Default is FALSE.
* o $params["persist"] - Indicates whether or not the SMTP connection
* should persist over multiple calls to the send() method.
*/
$mail_send_params = NULL;
/*
* Headers :
*/
$mail_hearders = array(
'MIME-Version' => '1.0',
'Content-Type' => 'text/plain; charset=UTF-8; format=flowed',
'Content-Transfer-Encoding' => '8bit',
);
// Mail sender address
$mail_sender='sms-noreply@example.fr';
// Catch all email to the following specified address
//$mail_catch='root@example.fr';
// Load local configuration file is present
if (is_file($root_dir_path.'/includes/config.local.php')) {
require $root_dir_path.'/includes/config.local.php';
}

25
includes/core.php Normal file
View file

@ -0,0 +1,25 @@
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
// Root directory path
$root_dir_path=realpath(dirname(__FILE__).'/../');
// Include App's includes and vendor directories to PHP include paths
set_include_path($root_dir_path.'/includes' . PATH_SEPARATOR . $root_dir_path.'/vendor' . PATH_SEPARATOR . get_include_path());
require_once('config.inc.php');
// Check $public_root_url end
if (substr($public_root_url, -1)=='/') {
$public_root_url=substr($public_root_url, 0, -1);
}
require_once('logging.php');
require_once('functions.php');
require_once('sms_gw_api.php');
$smsgw = new sms_gw_api($smsgw_url, $smsgw_ssl_verify);
require('db.php');

302
includes/db.php Normal file
View file

@ -0,0 +1,302 @@
<?php
try {
require $fluentpdo_path;
$pdo = new PDO($db_dsn,$db_user,$db_pwd,$db_options);
$fpdo = new FluentPDO($pdo);
$fpdo -> debug = function ($q) {
$time = sprintf('%0.3f', $q->getTime() * 1000) . ' ms';
$rows = ($q->getResult()) ? $q->getResult()->rowCount() : 0;
$query = $q->getQuery();
$msg = "# DB query ($time; rows = $rows) : $query";
$parameters = $q->getParameters();
if ($parameters) {
if (is_array($parameters)) {
$msg .= "\n# Parameters: '" . implode("', '", $parameters) . "'";
}
else {
$msg .= "\n# Parameters: '" . varDump($parameters) . "'";
}
}
logging('DEBUG',$msg);
};
}
catch(Exception $e) {
logging('ERROR',"Fail to connect to DB (DSN : '$db_dsn') : ".$e->getMessage());
fatal_error("Impossible de se connecter à la base de données");
}
function db_now() {
// 1970-01-01 00:00:01
return date('Y-m-d G:i:s');
}
function get_incoming_msg($uuid) {
global $fpdo;
$result = $fpdo -> from('incoming_msg')
-> where('uuid=?', $uuid)
-> execute();
if ($result !== false) {
return $result -> fetch();
}
return -1;
}
function create_incoming_msg($number, $text, $timestampms) {
global $fpdo;
$uuid = generate_uuid();
$values = array (
'uuid' => $uuid,
'number' => $number,
'text' => $text,
'timestampms' => $timestampms,
);
$result = $fpdo -> insertInto('incoming_msg',$values)
-> execute();
if ($result === false)
return -1;
return $uuid;
}
function get_outgoing_msg($uuid, $status=false) {
global $fpdo;
$where=array();
if ($uuid)
$where['uuid']=$uuid;
if ($status)
$where['status']=$status;
if (empty($where))
return -1;
$result = $fpdo -> from('outgoing_msg')
-> where($where)
-> execute();
if ($result !== false) {
return $result -> fetch();
}
return -1;
}
function get_outgoing_msg_by_uniqueid($uniqueid) {
global $fpdo;
$where=array('uniqueid' => $uniqueid);
$result = $fpdo -> from('outgoing_msg')
-> where($where)
-> execute();
if ($result !== false) {
return $result -> fetch();
}
return -1;
}
function create_outgoing_msg($number, $text) {
global $fpdo;
$uuid = generate_uuid();
$values = array (
'uuid' => $uuid,
'number' => $number,
'text' => $text,
);
$result = $fpdo -> insertInto('outgoing_msg',$values)
-> execute();
if ($result === false)
return -1;
return $uuid;
}
function handle_outgoing_msg($msg) {
if (!is_array($msg))
$msg=get_outgoing_msg($msg);
if (is_array($msg)) {
if ($msg['status']=='pending') {
global $smsgw;
/* Exemple :
{
"number": "0612345678",
"text": "Hello world",
"nbfrag": 1,
"id": "1510712464683"
}
*/
$return=$smsgw->send_sms($msg['number'], $msg['text']);
if (is_array($return)) {
global $fpdo;
$result = $fpdo -> update('outgoing_msg')
-> set(
array (
'status' => 'pushed',
'uniqueid' => $return['id'],
'nbfrag' => $return['nbfrag'],
'lastupdate' => db_now(),
)
)
-> where(
array (
'uuid' => $msg['uuid'],
)
)
-> execute();
if ($result === false)
return -1;
return True;
}
else
return -1;
}
return True;
}
return -1;
}
function get_outgoing_msg_frag($msguid, $fragid) {
global $fpdo;
$where=array(
'msguid' => $msguid,
'fragid' => $fragid,
);
$result = $fpdo -> from('outgoing_msg_frag')
-> where($where)
-> execute();
if ($result !== false) {
return $result -> fetch();
}
return -1;
}
function get_outgoing_msg_status_from_frags($msg) {
if (!is_array($msg))
$msg=get_outgoing_msg($msg);
if (!is_array($msg))
return -1;
global $fpdo;
$where=array(
'msguid' => $msg['uniqueid'],
);
$result = $fpdo -> from('outgoing_msg_frag')
-> where($where)
-> execute();
if ($result !== false) {
$frags=$result -> fetchAll();
if (!is_array($frags))
return -1;
logging('DEBUG', "Frags : ".print_r($frags,1));
$frag_states=array('pending','pushed','sent','delivered');
$state_idx=0;
$state_frag_count=0;
foreach($frags as $frag) {
$idx=array_search($frag['status'], $frag_states);
if ($idx !== false) {
if ($idx > $state_idx) {
$state_idx=$idx;
$state_frag_count=1;
}
elseif ($state_idx==$idx)
$state_frag_count++;
}
}
$status=$frag_states[$state_idx];
if ($status=="pending" || $status=="pushed" || $state_frag_count==$msg['nbfrag']) {
return $status;
}
else {
return "partially_$status";
}
}
return -1;
}
function update_outgoing_msg_status($uuid, $status) {
global $fpdo;
$result = $fpdo -> update('outgoing_msg')
-> set(
array (
'status' => $status,
'lastupdate' => db_now(),
)
)
-> where(
array (
'uuid' => $uuid,
)
)
-> execute();
if ($result === false)
return -1;
return True;
}
function create_outgoing_msg_frag($msguid, $fragid, $status=false) {
global $fpdo;
$values = array (
'msguid' => $msguid,
'fragid' => $fragid,
);
if ($status)
$values['status']=strtolower($status);
$result = $fpdo -> insertInto('outgoing_msg_frag',$values)
-> execute();
if ($result === false)
return -1;
return true;
}
function update_outgoing_msg_frag($msguid, $fragid, $status) {
global $fpdo;
$frag=get_outgoing_msg_frag($msguid, $fragid);
if ($frag==-1)
return -1;
if (is_array($frag)) {
$result = $fpdo -> update('outgoing_msg_frag')
-> set(
array (
'status' => strtolower($status),
)
)
-> where(
array (
'msguid' => $msguid,
'fragid' => $fragid,
)
)
-> execute();
if ($result === false)
return -1;
return True;
}
else {
return create_outgoing_msg_frag($msguid, $fragid, $status);
}
}
function get_smsq() {
global $fpdo;
$where=array(
'status != ?' => 'delivered',
);
$result = $fpdo -> from('outgoing_msg')
-> where($where)
-> execute();
if ($result !== false) {
return $result -> fetchAll();
}
return -1;
}

158
includes/functions.php Normal file
View file

@ -0,0 +1,158 @@
<?php
function generate_uuid() {
if (function_exists('random_bytes')) {
$data=random_bytes(16);
}
else {
$data=openssl_random_pseudo_bytes(16);
}
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
function check_phone_number($number) {
if (preg_match('/^\+?[0-9]{10,}$/iu',$number))
return true;
return false;
}
function check_sms_text($text) {
if (is_string($text) && !empty($text))
return true;
return false;
}
function check_integer($int) {
if (preg_match('/^[0-9]+$/iu',$int))
return true;
return false;
}
$frag_states=array('Delivered','Sent');
function check_frag_state($state) {
global $frag_states;
if (in_array($state, $frag_states))
return true;
return false;
}
function check_uuid($uuid) {
if (preg_match('/^[0-9A-F]{8}\-[0-9A-F]{4}\-[0-9A-F]{4}\-[0-9A-F]{4}\-[0-9A-F]{12}$/i',$uuid))
return true;
return false;
}
/*
* From LdapSaisie
*/
/*
* Check email
*/
function check_email($value,$domain=NULL,$checkDns=true) {
$regex = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/';
if (!preg_match($regex, $value)) {
return false;
}
$nd = explode('@', $value);
$nd=$nd[1];
if ($domain) {
if(is_array($domain)) {
if (!in_array($nd,$domain)) {
return false;
}
}
else {
if($nd!=$domain) {
return false;
}
}
}
if ($checkDns && function_exists('checkdnsrr')) {
if (!(checkdnsrr($nd, 'MX') || checkdnsrr($nd, 'A'))) {
return false;
}
}
return true;
}
/**
* Supprime les accents d'une chaine
*
* @param[in] $string La chaine originale
*
* @retval string La chaine sans les accents
*/
function withoutAccents($string){
$replaceAccent = Array(
"à" => "a",
"á" => "a",
"â" => "a",
"ã" => "a",
"ä" => "a",
"ç" => "c",
"è" => "e",
"é" => "e",
"ê" => "e",
"ë" => "e",
"ì" => "i",
"í" => "i",
"î" => "i",
"ï" => "i",
"ñ" => "n",
"ò" => "o",
"ó" => "o",
"ô" => "o",
"õ" => "o",
"ö" => "o",
"ù" => "u",
"ú" => "u",
"û" => "u",
"ü" => "u",
"ý" => "y",
"ÿ" => "y",
"À" => "A",
"Á" => "A",
"Â" => "A",
"Ã" => "A",
"Ä" => "A",
"Ç" => "C",
"È" => "E",
"É" => "E",
"Ê" => "E",
"Ë" => "E",
"Ì" => "I",
"Í" => "I",
"Î" => "I",
"Ï" => "I",
"Ñ" => "N",
"Ò" => "O",
"Ó" => "O",
"Ô" => "O",
"Õ" => "O",
"Ö" => "O",
"Ù" => "U",
"Ú" => "U",
"Û" => "U",
"Ü" => "U",
"Ý" => "Y"
);
return strtr($string, $replaceAccent);
}
function varDump($data) {
ob_start();
var_dump($data);
$data=ob_get_contents();
ob_end_clean();
return $data;
}

43
includes/logging.php Normal file
View file

@ -0,0 +1,43 @@
<?php
/*
* Configuration :
* // Log file
* $log_file='/path/to/app.log';
*
* // Log level (DEBUG / INFO / WARNING / ERROR)
* $log_level='INFO';
*/
// Log file descriptor (Do not change !!!)
$_log_file_fd=null;
// Log Levels
$_log_levels=array(
'DEBUG' => 0,
'INFO' => 1,
'WARNING' => 2,
'ERROR' => 3,
);
function logging($level,$message) {
global $log_file, $_log_file_fd, $_log_levels, $log_level;
if (!array_key_exists($level, $_log_levels)) $level=='INFO';
$level_id=$_log_levels[$level];
if (!array_key_exists($log_level, $_log_levels)) $log_level=='INFO';
$log_level_id=$_log_levels[$log_level];
if ($level_id<$log_level_id) return true;
if(is_null($_log_file_fd)) {
$_log_file_fd=fopen($log_file,'a');
}
$msg=date('Y/m/d H:i:s').' - '.$_SERVER['REQUEST_URI'].' - '.$_SERVER['REMOTE_ADDR']." - $level - $message\n";
fwrite($_log_file_fd,$msg);
return true;
}

111
includes/sms_gw_api.php Normal file
View file

@ -0,0 +1,111 @@
<?php
Class sms_gw_api {
// WebServices parameters
private $ws_url = null;
private $ws_ssl_verify = true;
function sms_gw_api($ws_url, $ws_ssl_verify=true){
$this -> ws_url = $ws_url;
$this -> ws_ssl_verify = $ws_ssl_verify;
}
/*
* REST Request
*/
private function _ws_call($method, $url, $data = false, $json = true) {
if (is_null($this -> ws_url)) return;
$curl = curl_init();
switch ($method) {
case "POST":
curl_setopt($curl, CURLOPT_POST, 1);
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
break;
case "PUT":
curl_setopt($curl, CURLOPT_PUT, 1);
break;
default:
$method='GET';
if ($data)
$url = sprintf("%s?%s", $url, http_build_query($data));
}
if ($this -> ws_ssl_verify) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, TRUE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
}
else {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
}
$complete_url=$this -> ws_url.'/'.$url;
curl_setopt($curl, CURLOPT_URL, $complete_url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
logging("DEBUG", "sms_gw_api : Call URL $complete_url");
$result = curl_exec($curl);
if(curl_errno($curl)) {
logging("ERROR", "sms_gw_api : Error during $method request to ".$complete_url." : ".curl_error($curl));
curl_close($curl);
return False;
}
curl_close($curl);
logging("DEBUG","sms_gw_api : Return => '$result'");
if (!$json)
return $result;
try {
$data = json_decode($result,true);
logging("DEBUG","sms_gw_api : Decoded data :".print_r($data,1));
return $data;
}
catch (Exception $e) {
logging("ERROR","sms_gw_api : Error parsing $method request JSON result on ".$complete_url." : ".$e);
return False;
}
}
/**
* send sms
* @param string number
* @param string text
* @return struct sms gw return struct
*/
function send_sms($number, $text){
return self :: _ws_call(
'GET',
"",
array(
'number' => $number,
'text' => $text,
)
);
}
/**
* check gateway is alive
* @return boolean sms gw status
*/
function check_gateway(){
$return=self :: _ws_call(
'GET',
"",
array(),
false
);
return ($return !== false && preg_match('/My SMS Gateway/', $return));
}
}

9
public_html/.htaccess Normal file
View file

@ -0,0 +1,9 @@
RewriteEngine On
RewriteBase /
# If the request is not for a valid file
RewriteCond %{REQUEST_FILENAME} !-f
# If the request is not for a valid link
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^([^?]+)(\?(.*))?$ index.php?go=$1&$3 [L,QSA]

126
public_html/index.php Normal file
View file

@ -0,0 +1,126 @@
<?php
include '../includes/core.php';
$go=false;
if (isset($_REQUEST['go'])) {
$go=$_REQUEST['go'];
}
$post_body=file_get_contents('php://input');
if ($post_body) {
$post_body=json_decode($post_body, true);
if(!is_array($post_body)) $post_body=false;
}
else {
$post_body=false;
}
logging('DEBUG','POST Body : '.print_r($post_body,1));
$data=array();
switch($go) {
case 'send_sms':
if (check_phone_number($_REQUEST['number']) && check_sms_text($_REQUEST['text'])) {
logging('INFO','New outgoing message for '.$_REQUEST['number']);
$uuid = create_outgoing_msg($_REQUEST['number'], $_REQUEST['text']);
if (check_uuid($uuid)) {
$data['uuid']=$uuid;
if(handle_outgoing_msg($uuid)===True) {
$data['status']='ok';
}
else {
$data['status']='pending';
}
}
else {
$data['status']='error';
$data['msg']='Error creating your outgoing message';
}
}
else {
$data['status']='error';
$data['msg']='Invalid parameters';
}
break;
case 'ack':
/*
Post body example : Array (
[id] => 1510702925705
[state] => Delivered
[frag] => 0
[text] => qsdmlkqsdmlqskdmqlksdmqlsdkmqlsdqsdmlkqsdmlqskdmqlksdmqlsdkmqlsdqsdmlkqsdmlqskdmqlksdmqlsdkmqlsdqsdmlkqsdmlqskdmqlksdmqlsdkmqlsdqsdmlkqsdmlqskdmqlksdmqls
)
*/
if (is_array($post_body) && check_integer($post_body['id']) && check_integer($post_body['frag']) && check_frag_state($post_body['state'])) {
logging('INFO','Ack for message '.$post_body['id'].' (Frag : '.$post_body['frag'].') with status '.$post_body['state']);
$msg=get_outgoing_msg_by_uniqueid($post_body['id']);
if (is_array($msg)) {
logging('INFO',"Corresponding outgoing msg : ".$msg['uuid']);
logging('DEBUG',"Outgoing msg : ".print_r($msg,1));
if (update_outgoing_msg_frag($post_body['id'], $post_body['frag'], strtolower($post_body['state']))===True) {
$msg_status=get_outgoing_msg_status_from_frags($msg);
if ($msg_status!=$mgs['status'] && update_outgoing_msg_status($msg['uuid'], $msg_status)==-1) {
$data['status']='error';
$data['msg']='Error updating outgoing msg status';
}
else {
$data['status']='ok';
}
}
else {
$data['status']='error';
$data['msg']='Fail to update outgoing msg frag';
}
}
else {
$data['status']='error';
$data['msg']='Outgoing message not found';
}
}
else {
$data['status']='error';
$data['msg']='Invalid parameters';
}
break;
case 'incoming':
/*
Post body example : Array (
[number] => +33612345678
[text] => qsdmlkqsdmlqskdmqlksd
[timestampMillis] => 1510702926820
)
*/
if (is_array($post_body) && check_phone_number($post_body['number']) && check_sms_text($post_body['text']) && check_integer($post_body['timestampMillis'])) {
logging('INFO','Incoming SMS from '.$post_body['number']);
$msg=create_incoming_msg($post_body['number'], $post_body['text'], $post_body['timestampMillis']);
if (is_array($msg)) {
$data['status']='ok';
}
else {
$data['status']='error';
$data['msg']='Error creating your incoming message';
}
}
else {
$data['status']='error';
$data['msg']='Invalid parameters';
}
break;
case 'smsq':
$data['messages']=get_smsq();
logging('DEBUG','SMSQ : '.print_r($data['messages'],1));
break;
case 'check_gateway':
$data['status']=($smsgw->check_gateway()?'alive':'not responding');
logging('DEBUG','SMS Gateway Status : '.$data['status']);
break;
case '':
print "Welcome on SMS Gateway WebService";
exit();
break;
default:
$data['status']='error';
$data['msg']='Invalid request';
}
print json_encode($data, (isset($_REQUEST['pretty'])?JSON_PRETTY_PRINT:0));

View file

@ -0,0 +1,8 @@
<?php
require '../includes/core.php';
if (count($argv) != 2 || !check_integer($argv[1])) {
die("Usage : $argv[0] [uuid]\n");
}
var_dump(get_outgoing_msg_by_uniqueid($argv[1]));

View file

@ -0,0 +1,8 @@
<?php
require '../includes/core.php';
if (count($argv) != 2 || !check_uuid($argv[1])) {
die("Usage : $argv[0] [uuid]\n");
}
var_dump(get_outgoing_msg_status_from_frags($argv[1]));

View file

@ -0,0 +1,8 @@
<?php
require '../includes/core.php';
if (count($argv) != 2 || !check_uuid($argv[1])) {
die("Usage : $argv[0] [uuid]\n");
}
var_dump(get_outgoing_msg($argv[1]));

View file

@ -0,0 +1,8 @@
<?php
require '../includes/core.php';
if (count($argv) != 2 || !check_uuid($argv[1])) {
die("Usage : $argv[0] [uuid]\n");
}
var_dump(handle_outgoing_msg($argv[1]));

View file

@ -0,0 +1,9 @@
<?php
require '../includes/core.php';
if (count($argv) != 4 || !check_integer($argv[1]) || !check_integer($argv[2]) || !check_frag_state($argv[3])) {
die("Usage : $argv[0] [uniqueid] [frag id] [status]\n");
}
// function update_outgoing_msg_frag($msguid, $fragid, $status)
var_dump(update_outgoing_msg_frag($argv[1], $argv[2], $argv[3]));

361
vendor/FluentPDO/BaseQuery.php vendored Normal file
View file

@ -0,0 +1,361 @@
<?php
/** Base query builder
*/
abstract class BaseQuery implements IteratorAggregate
{
/** @var FluentPDO */
private $fpdo;
/** @var PDOStatement */
private $result;
/** @var float */
private $time;
/** @var bool */
private $object = false;
/** @var array - definition clauses */
protected $clauses = array();
/** @var array */
protected $statements = array();
/** @var array */
protected $parameters = array();
/**
* BaseQuery constructor.
*
* @param FluentPDO $fpdo
* @param $clauses
*/
protected function __construct(FluentPDO $fpdo, $clauses) {
$this->fpdo = $fpdo;
$this->clauses = $clauses;
$this->initClauses();
}
/**
* Initialize statement and parameter clauses.
*/
private function initClauses() {
foreach ($this->clauses as $clause => $value) {
if ($value) {
$this->statements[$clause] = array();
$this->parameters[$clause] = array();
} else {
$this->statements[$clause] = null;
$this->parameters[$clause] = null;
}
}
}
/**
* Add statement for all kind of clauses
*
* @param $clause
* @param $statement
* @param array $parameters
*
* @return $this
*/
protected function addStatement($clause, $statement, $parameters = array()) {
if ($statement === null) {
return $this->resetClause($clause);
}
// $statement !== null
if ($this->clauses[$clause]) {
if (is_array($statement)) {
$this->statements[$clause] = array_merge($this->statements[$clause], $statement);
} else {
$this->statements[$clause][] = $statement;
}
$this->parameters[$clause] = array_merge($this->parameters[$clause], $parameters);
} else {
$this->statements[$clause] = $statement;
$this->parameters[$clause] = $parameters;
}
return $this;
}
/**
* Remove all prev defined statements
*
* @param $clause
*
* @return $this
*/
protected function resetClause($clause) {
$this->statements[$clause] = null;
$this->parameters[$clause] = array();
if (isset($this->clauses[$clause]) && $this->clauses[$clause]) {
$this->statements[$clause] = array();
}
return $this;
}
/**
* Implements method from IteratorAggregate
*
* @return \PDOStatement
*/
public function getIterator() {
return $this->execute();
}
/**
* Execute query with earlier added parameters
*
* @return \PDOStatement
*/
public function execute() {
$query = $this->buildQuery();
$parameters = $this->buildParameters();
$result = $this->fpdo->getPdo()->prepare($query);
// At this point, $result is a PDOStatement instance, or false.
// PDO::prepare() does not reliably return errors. Some database drivers
// do not support prepared statements, and PHP emulates them. Postgresql
// does support prepared statements, but PHP does not call Postgresql's
// prepare function until we call PDOStatement::execute() below.
// If PDO::prepare() worked properly, this is where we would check
// for prepare errors, such as invalid SQL.
if ($this->object !== false) {
if (class_exists($this->object)) {
$result->setFetchMode(PDO::FETCH_CLASS, $this->object);
} else {
$result->setFetchMode(PDO::FETCH_OBJ);
}
} elseif ($this->fpdo->getPdo()->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) == PDO::FETCH_BOTH) {
$result->setFetchMode(PDO::FETCH_ASSOC);
}
$time = microtime(true);
if ($result && $result->execute($parameters)) {
$this->time = microtime(true) - $time;
} else {
$result = false;
}
$this->result = $result;
$this->debugger();
return $result;
}
/**
* Echo/pass a debug string
*/
private function debugger() {
if ($this->fpdo->debug) {
if (!is_callable($this->fpdo->debug)) {
$backtrace = '';
$query = $this->getQuery();
$parameters = $this->getParameters();
$debug = '';
if ($parameters) {
$debug = '# parameters: ' . implode(', ', array_map(array($this, 'quote'), $parameters)) . "\n";
}
$debug .= $query;
$pattern = '(^' . preg_quote(__DIR__) . '(\\.php$|[/\\\\]))'; // can be static
foreach (debug_backtrace() as $backtrace) {
if (isset($backtrace['file']) && !preg_match($pattern, $backtrace['file'])) {
// stop on first file outside FluentPDO source codes
break;
}
}
$time = sprintf('%0.3f', $this->time * 1000) . ' ms';
$rows = ($this->result) ? $this->result->rowCount() : 0;
$finalString = "# $backtrace[file]:$backtrace[line] ($time; rows = $rows)\n$debug\n\n";
if (is_resource(STDERR)) { // if STDERR is set, send there, otherwise just output the string
fwrite(STDERR, $finalString);
}
else {
echo $finalString;
}
} else {
call_user_func($this->fpdo->debug, $this);
}
}
}
/**
* @return \PDO
*/
protected function getPDO() {
return $this->fpdo->getPdo();
}
/**
* @return \FluentStructure
*/
protected function getStructure() {
return $this->fpdo->getStructure();
}
/**
* Get PDOStatement result
*
* @return \PDOStatement
*/
public function getResult() {
return $this->result;
}
/**
* Get time of execution
*
* @return float
*/
public function getTime() {
return $this->time;
}
/**
* Get query parameters
*
* @return array
*/
public function getParameters() {
return $this->buildParameters();
}
/**
* Get query string
*
* @param bool $formatted - Return formatted query
*
* @return string
*/
public function getQuery($formatted = true) {
$query = $this->buildQuery();
if ($formatted) {
$query = FluentUtils::formatQuery($query);
}
return $query;
}
/**
* Generate query
*
* @return string
* @throws Exception
*/
protected function buildQuery() {
$query = '';
foreach ($this->clauses as $clause => $separator) {
if ($this->clauseNotEmpty($clause)) {
if (is_string($separator)) {
$query .= " $clause " . implode($separator, $this->statements[$clause]);
} elseif ($separator === null) {
$query .= " $clause " . $this->statements[$clause];
} elseif (is_callable($separator)) {
$query .= call_user_func($separator);
} else {
throw new Exception("Clause '$clause' is incorrectly set to '$separator'.");
}
}
}
return trim($query);
}
/**
* @param $clause
*
* @return bool
*/
private function clauseNotEmpty($clause) {
if ($this->clauses[$clause]) {
return (boolean)count($this->statements[$clause]);
} else {
return (boolean)$this->statements[$clause];
}
}
/**
* @return array
*/
protected function buildParameters() {
$parameters = array();
foreach ($this->parameters as $clauses) {
if (is_array($clauses)) {
foreach ($clauses as $value) {
if (is_array($value) && is_string(key($value)) && substr(key($value), 0, 1) == ':') {
// this is named params e.g. (':name' => 'Mark')
$parameters = array_merge($parameters, $value);
} else {
$parameters[] = $value;
}
}
} else {
if ($clauses) {
$parameters[] = $clauses;
}
}
}
return $parameters;
}
/**
* @param $value
*
* @return string
*/
protected function quote($value) {
if (!isset($value)) {
return "NULL";
}
if (is_array($value)) { // (a, b) IN ((1, 2), (3, 4))
return "(" . implode(", ", array_map(array($this, 'quote'), $value)) . ")";
}
$value = $this->formatValue($value);
if (is_float($value)) {
return sprintf("%F", $value); // otherwise depends on setlocale()
}
if ($value === false) {
return "0";
}
if (is_int($value) || $value instanceof FluentLiteral) { // number or SQL code - for example "NOW()"
return (string)$value;
}
return $this->fpdo->getPdo()->quote($value);
}
/**
* @param \DateTime $val
*
* @return string
*/
private function formatValue($val) {
if ($val instanceof DateTime) {
return $val->format("Y-m-d H:i:s"); //! may be driver specific
}
return $val;
}
/**
* Select an item as object
*
* @param boolean|object $object If set to true, items are returned as stdClass, otherwise a class
* name can be passed and a new instance of this class is return.
* Can be set to false to return items as an associative array.
*
* @return \BaseQuery
*/
public function asObject($object = true) {
$this->object = $object;
return $this;
}
}

290
vendor/FluentPDO/CommonQuery.php vendored Normal file
View file

@ -0,0 +1,290 @@
<?php
/**
* CommonQuery add JOIN and WHERE clauses for (SELECT, UPDATE, DELETE)
*/
abstract class CommonQuery extends BaseQuery
{
/** @var array - Query tables (also include table from clause FROM) */
protected $joins = array();
/** @var bool - Disable adding undefined joins to query? */
protected $isSmartJoinEnabled = true;
/**
* @return $this
*/
public function enableSmartJoin() {
$this->isSmartJoinEnabled = true;
return $this;
}
/**
* @return $this
*/
public function disableSmartJoin() {
$this->isSmartJoinEnabled = false;
return $this;
}
/**
* @return bool
*/
public function isSmartJoinEnabled() {
return $this->isSmartJoinEnabled;
}
/**
* Add where condition, more calls appends with AND
*
* @param string $condition possibly containing ? or :name (PDO syntax)
* @param mixed $parameters array or a scalar value
*
* @return \SelectQuery
*/
public function where($condition, $parameters = array()) {
if ($condition === null) {
return $this->resetClause('WHERE');
}
if (!$condition) {
return $this;
}
if (is_array($condition)) { // where(array("column1" => 1, "column2 > ?" => 2))
foreach ($condition as $key => $val) {
$this->where($key, $val);
}
return $this;
}
$args = func_get_args();
if (count($args) == 1) {
return $this->addStatement('WHERE', $condition);
}
// check that there are 2 arguments, a condition and a parameter value
// if the condition contains a parameter simply add them
// since its up to the user if it's valid sql or not
// Otherwise we're probably with just an identifier. So lets
// construct a new condition based on the passed parameter value.
if (count($args) == 2 && !preg_match('/(\?|:\w+)/i', $condition)) {
// condition is column only
if (is_null($parameters)) {
return $this->addStatement('WHERE', "$condition is NULL");
} elseif ($args[1] === array()) {
return $this->addStatement('WHERE', 'FALSE');
} elseif (is_array($args[1])) {
$in = $this->quote($args[1]);
return $this->addStatement('WHERE', "$condition IN $in");
}
$condition = "$condition = ?";
}
array_shift($args);
return $this->addStatement('WHERE', $condition, $args);
}
/**
* @param $clause
* @param array $parameters - first is $statement followed by $parameters
*
* @return $this|\SelectQuery
*/
public function __call($clause, $parameters = array()) {
$clause = FluentUtils::toUpperWords($clause);
if ($clause == 'GROUP') {
$clause = 'GROUP BY';
}
if ($clause == 'ORDER') {
$clause = 'ORDER BY';
}
if ($clause == 'FOOT NOTE') {
$clause = "\n--";
}
$statement = array_shift($parameters);
if (strpos($clause, 'JOIN') !== false) {
return $this->addJoinStatements($clause, $statement, $parameters);
}
return $this->addStatement($clause, $statement, $parameters);
}
/**
* @return string
*/
protected function getClauseJoin() {
return implode(' ', $this->statements['JOIN']);
}
/**
* Statement can contain more tables (e.g. "table1.table2:table3:")
*
* @param $clause
* @param $statement
* @param array $parameters
*
* @return $this|\SelectQuery
*/
private function addJoinStatements($clause, $statement, $parameters = array()) {
if ($statement === null) {
$this->joins = array();
return $this->resetClause('JOIN');
}
if (array_search(substr($statement, 0, -1), $this->joins) !== false) {
return $this;
}
// match "tables AS alias"
preg_match('/`?([a-z_][a-z0-9_\.:]*)`?(\s+AS)?(\s+`?([a-z_][a-z0-9_]*)`?)?/i', $statement, $matches);
$joinAlias = '';
$joinTable = '';
if ($matches) {
$joinTable = $matches[1];
if (isset($matches[4]) && !in_array(strtoupper($matches[4]), array('ON', 'USING'))) {
$joinAlias = $matches[4];
}
}
if (strpos(strtoupper($statement), ' ON ') || strpos(strtoupper($statement), ' USING')) {
if (!$joinAlias) {
$joinAlias = $joinTable;
}
if (in_array($joinAlias, $this->joins)) {
return $this;
} else {
$this->joins[] = $joinAlias;
$statement = " $clause $statement";
return $this->addStatement('JOIN', $statement, $parameters);
}
}
// $joinTable is list of tables for join e.g.: table1.table2:table3....
if (!in_array(substr($joinTable, -1), array('.', ':'))) {
$joinTable .= '.';
}
preg_match_all('/([a-z_][a-z0-9_]*[\.:]?)/i', $joinTable, $matches);
$mainTable = '';
if (isset($this->statements['FROM'])) {
$mainTable = $this->statements['FROM'];
} elseif (isset($this->statements['UPDATE'])) {
$mainTable = $this->statements['UPDATE'];
}
$lastItem = array_pop($matches[1]);
array_push($matches[1], $lastItem);
foreach ($matches[1] as $joinItem) {
if ($mainTable == substr($joinItem, 0, -1)) {
continue;
}
// use $joinAlias only for $lastItem
$alias = '';
if ($joinItem == $lastItem) {
$alias = $joinAlias;
}
$newJoin = $this->createJoinStatement($clause, $mainTable, $joinItem, $alias);
if ($newJoin) {
$this->addStatement('JOIN', $newJoin, $parameters);
}
$mainTable = $joinItem;
}
return $this;
}
/**
* Create join string
*
* @param $clause
* @param $mainTable
* @param $joinTable
* @param string $joinAlias
*
* @return string
*/
private function createJoinStatement($clause, $mainTable, $joinTable, $joinAlias = '') {
if (in_array(substr($mainTable, -1), array(':', '.'))) {
$mainTable = substr($mainTable, 0, -1);
}
$referenceDirection = substr($joinTable, -1);
$joinTable = substr($joinTable, 0, -1);
$asJoinAlias = '';
if ($joinAlias) {
$asJoinAlias = " AS $joinAlias";
} else {
$joinAlias = $joinTable;
}
if (in_array($joinAlias, $this->joins)) {
// if join exists don't create same again
return '';
} else {
$this->joins[] = $joinAlias;
}
if ($referenceDirection == ':') {
// back reference
$primaryKey = $this->getStructure()->getPrimaryKey($mainTable);
$foreignKey = $this->getStructure()->getForeignKey($mainTable);
return " $clause $joinTable$asJoinAlias ON $joinAlias.$foreignKey = $mainTable.$primaryKey";
} else {
$primaryKey = $this->getStructure()->getPrimaryKey($joinTable);
$foreignKey = $this->getStructure()->getForeignKey($joinTable);
return " $clause $joinTable$asJoinAlias ON $joinAlias.$primaryKey = $mainTable.$foreignKey";
}
}
/**
* @return string
*/
protected function buildQuery() {
// first create extra join from statements with columns with referenced tables
$statementsWithReferences = array('WHERE', 'SELECT', 'GROUP BY', 'ORDER BY');
foreach ($statementsWithReferences as $clause) {
if (array_key_exists($clause, $this->statements)) {
$this->statements[$clause] = array_map(array($this, 'createUndefinedJoins'), $this->statements[$clause]);
}
}
return parent::buildQuery();
}
/**
* Create undefined joins from statement with column with referenced tables
*
* @param string $statement
*
* @return string rewrited $statement (e.g. tab1.tab2:col => tab2.col)
*/
private function createUndefinedJoins($statement) {
if (!$this->isSmartJoinEnabled) {
return $statement;
}
preg_match_all('/\\b([a-z_][a-z0-9_.:]*[.:])[a-z_]*/i', $statement, $matches);
foreach ($matches[1] as $join) {
if (!in_array(substr($join, 0, -1), $this->joins)) {
$this->addJoinStatements('LEFT JOIN', $join);
}
}
// don't rewrite table from other databases
foreach ($this->joins as $join) {
if (strpos($join, '.') !== false && strpos($statement, $join) === 0) {
return $statement;
}
}
// remove extra referenced tables (rewrite tab1.tab2:col => tab2.col)
$statement = preg_replace('/(?:\\b[a-z_][a-z0-9_.:]*[.:])?([a-z_][a-z0-9_]*)[.:]([a-z_*])/i', '\\1.\\2', $statement);
return $statement;
}
}

94
vendor/FluentPDO/DeleteQuery.php vendored Normal file
View file

@ -0,0 +1,94 @@
<?php
/**
* DELETE query builder
*
* @method DeleteQuery leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method DeleteQuery innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method DeleteQuery from(string $table) add LIMIT to query
* @method DeleteQuery orderBy(string $column) add ORDER BY to query
* @method DeleteQuery limit(int $limit) add LIMIT to query
*/
class DeleteQuery extends CommonQuery
{
private $ignore = false;
/**
* DeleteQuery constructor.
*
* @param FluentPDO $fpdo
* @param string $table
*/
public function __construct(FluentPDO $fpdo, $table) {
$clauses = array(
'DELETE FROM' => array($this, 'getClauseDeleteFrom'),
'DELETE' => array($this, 'getClauseDelete'),
'FROM' => null,
'JOIN' => array($this, 'getClauseJoin'),
'WHERE' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
);
parent::__construct($fpdo, $clauses);
$this->statements['DELETE FROM'] = $table;
$this->statements['DELETE'] = $table;
}
/**
* Forces delete operation to fail silently
*
* @return \DeleteQuery
*/
public function ignore() {
$this->ignore = true;
return $this;
}
/**
* @return string
*/
protected function buildQuery() {
if ($this->statements['FROM']) {
unset($this->clauses['DELETE FROM']);
} else {
unset($this->clauses['DELETE']);
}
return parent::buildQuery();
}
/**
* Execute DELETE query
*
* @return bool
*/
public function execute() {
$result = parent::execute();
if ($result) {
return $result->rowCount();
}
return false;
}
/**
* @return string
*/
protected function getClauseDelete() {
return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' ' . $this->statements['DELETE'];
}
/**
* @return string
*/
protected function getClauseDeleteFrom() {
return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' FROM ' . $this->statements['DELETE FROM'];
}
}

30
vendor/FluentPDO/FluentLiteral.php vendored Normal file
View file

@ -0,0 +1,30 @@
<?php
/**
* SQL literal value
*/
class FluentLiteral
{
/** @var string */
protected $value = '';
/**
* Create literal value
*
* @param string $value
*/
function __construct($value) {
$this->value = $value;
}
/**
* Get literal value
*
* @return string
*/
function __toString() {
return $this->value;
}
}

164
vendor/FluentPDO/FluentPDO.php vendored Normal file
View file

@ -0,0 +1,164 @@
<?php
/**
* FluentPDO is simple and smart SQL query builder for PDO
*
* For more information @see readme.md
*
* @link http://github.com/lichtner/fluentpdo
* @author Marek Lichtner, marek@licht.sk
* @copyright 2012 Marek Lichtner
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
include_once 'FluentStructure.php';
include_once 'FluentUtils.php';
include_once 'FluentLiteral.php';
include_once 'BaseQuery.php';
include_once 'CommonQuery.php';
include_once 'SelectQuery.php';
include_once 'InsertQuery.php';
include_once 'UpdateQuery.php';
include_once 'DeleteQuery.php';
/**
* Class FluentPDO
*/
class FluentPDO
{
/** @var \PDO */
protected $pdo;
/** @var \FluentStructure|null */
protected $structure;
/** @var bool|callback */
public $debug;
/** @var boolean */
public $convertTypes = false;
/**
* FluentPDO constructor.
*
* @param \PDO $pdo
* @param \FluentStructure|null $structure
*/
function __construct(PDO $pdo, FluentStructure $structure = null) {
$this->pdo = $pdo;
if (!$structure) {
$structure = new FluentStructure();
}
$this->structure = $structure;
}
/**
* Create SELECT query from $table
*
* @param string $table - db table name
* @param integer $primaryKey - return one row by primary key
*
* @return \SelectQuery
*/
public function from($table, $primaryKey = null) {
$query = new SelectQuery($this, $table);
if ($primaryKey !== null) {
$tableTable = $query->getFromTable();
$tableAlias = $query->getFromAlias();
$primaryKeyName = $this->structure->getPrimaryKey($tableTable);
$query = $query->where("$tableAlias.$primaryKeyName", $primaryKey);
}
return $query;
}
/**
* Create INSERT INTO query
*
* @param string $table
* @param array $values - accepts one or multiple rows, @see docs
*
* @return \InsertQuery
*/
public function insertInto($table, $values = array()) {
$query = new InsertQuery($this, $table, $values);
return $query;
}
/**
* Create UPDATE query
*
* @param string $table
* @param array|string $set
* @param string $primaryKey
*
* @return \UpdateQuery
*/
public function update($table, $set = array(), $primaryKey = null) {
$query = new UpdateQuery($this, $table);
$query->set($set);
if ($primaryKey) {
$primaryKeyName = $this->getStructure()->getPrimaryKey($table);
$query = $query->where($primaryKeyName, $primaryKey);
}
return $query;
}
/**
* Create DELETE query
*
* @param string $table
* @param string $primaryKey delete only row by primary key
*
* @return \DeleteQuery
*/
public function delete($table, $primaryKey = null) {
$query = new DeleteQuery($this, $table);
if ($primaryKey) {
$primaryKeyName = $this->getStructure()->getPrimaryKey($table);
$query = $query->where($primaryKeyName, $primaryKey);
}
return $query;
}
/**
* Create DELETE FROM query
*
* @param string $table
* @param string $primaryKey
*
* @return \DeleteQuery
*/
public function deleteFrom($table, $primaryKey = null) {
$args = func_get_args();
return call_user_func_array(array($this, 'delete'), $args);
}
/**
* @return \PDO
*/
public function getPdo() {
return $this->pdo;
}
/**
* @return \FluentStructure
*/
public function getStructure() {
return $this->structure;
}
/**
* Closes the \PDO connection to the database
*
* @return null
*/
public function close() {
$this->pdo = null;
}
}

60
vendor/FluentPDO/FluentStructure.php vendored Normal file
View file

@ -0,0 +1,60 @@
<?php
/**
* Class FluentStructure
*/
class FluentStructure
{
/** @var string */
private $primaryKey;
/** @var string */
private $foreignKey;
/**
* FluentStructure constructor.
*
* @param string $primaryKey
* @param string $foreignKey
*/
function __construct($primaryKey = 'id', $foreignKey = '%s_id') {
if ($foreignKey === null) {
$foreignKey = $primaryKey;
}
$this->primaryKey = $primaryKey;
$this->foreignKey = $foreignKey;
}
/**
* @param string $table
*
* @return string
*/
public function getPrimaryKey($table) {
return $this->key($this->primaryKey, $table);
}
/**
* @param string $table
*
* @return string
*/
public function getForeignKey($table) {
return $this->key($this->foreignKey, $table);
}
/**
* @param string|callback $key
* @param string $table
*
* @return string
*/
private function key($key, $table) {
if (is_callable($key)) {
return $key($table);
}
return sprintf($key, $table);
}
}

86
vendor/FluentPDO/FluentUtils.php vendored Normal file
View file

@ -0,0 +1,86 @@
<?php
/**
* Class FluentUtils
*/
class FluentUtils
{
/**
* Convert "camelCaseWord" to "CAMEL CASE WORD"
*
* @param string $string
*
* @return string
*/
public static function toUpperWords($string) {
return trim(strtoupper(preg_replace('/(.)([A-Z]+)/', '$1 $2', $string)));
}
/**
* @param string $query
*
* @return string
*/
public static function formatQuery($query) {
$query = preg_replace(
'/WHERE|FROM|GROUP BY|HAVING|ORDER BY|LIMIT|OFFSET|UNION|ON DUPLICATE KEY UPDATE|VALUES/',
"\n$0", $query
);
$query = preg_replace(
'/INNER|LEFT|RIGHT|CASE|WHEN|END|ELSE|AND/',
"\n $0", $query
);
// remove trailing spaces
$query = preg_replace("/\s+\n/", "\n", $query);
return $query;
}
/**
* Converts columns from strings to types according to
* PDOStatement::columnMeta
* http://stackoverflow.com/a/9952703/3006989
*
* @param PDOStatement $st
* @param array $assoc returned by PDOStatement::fetch with PDO::FETCH_ASSOC
* @return copy of $assoc with matching type fields
*/
public static function convertToNativeTypes(PDOStatement $statement, $rows)
{
for ($i = 0; $columnMeta = $statement->getColumnMeta($i); $i++)
{
$type = $columnMeta['native_type'];
switch($type)
{
case 'DECIMAL':
case 'TINY':
case 'SHORT':
case 'LONG':
case 'LONGLONG':
case 'INT24':
if(isset($rows[$columnMeta['name']])){
$rows[$columnMeta['name']] = $rows[$columnMeta['name']] + 0;
}else{
if(is_array($rows) || $rows instanceof Traversable){
foreach($rows as &$row){
if(isset($row[$columnMeta['name']])){
$row[$columnMeta['name']] = $row[$columnMeta['name']] + 0;
}
}
}
}
break;
case 'DATETIME':
case 'DATE':
case 'TIMESTAMP':
// convert to date type?
break;
// default: keep as string
}
}
return $rows;
}
}

221
vendor/FluentPDO/InsertQuery.php vendored Normal file
View file

@ -0,0 +1,221 @@
<?php
/** INSERT query builder
*/
class InsertQuery extends BaseQuery
{
/** @var array */
private $columns = array();
/** @var array */
private $firstValue = array();
/** @var bool */
private $ignore = false;
/** @var bool */
private $delayed = false;
/**
* InsertQuery constructor.
*
* @param FluentPDO $fpdo
* @param string $table
* @param $values
*/
public function __construct(FluentPDO $fpdo, $table, $values) {
$clauses = array(
'INSERT INTO' => array($this, 'getClauseInsertInto'),
'VALUES' => array($this, 'getClauseValues'),
'ON DUPLICATE KEY UPDATE' => array($this, 'getClauseOnDuplicateKeyUpdate'),
);
parent::__construct($fpdo, $clauses);
$this->statements['INSERT INTO'] = $table;
$this->values($values);
}
/**
* Execute insert query
*
* @param mixed $sequence
*
* @return integer last inserted id or false
*/
public function execute($sequence = null) {
$result = parent::execute();
if ($result) {
return $this->getPDO()->lastInsertId($sequence);
}
return false;
}
/**
* Add ON DUPLICATE KEY UPDATE
*
* @param array $values
*
* @return \InsertQuery
*/
public function onDuplicateKeyUpdate($values) {
$this->statements['ON DUPLICATE KEY UPDATE'] = array_merge(
$this->statements['ON DUPLICATE KEY UPDATE'], $values
);
return $this;
}
/**
* Add VALUES
*
* @param $values
*
* @return \InsertQuery
* @throws Exception
*/
public function values($values) {
if (!is_array($values)) {
throw new Exception('Param VALUES for INSERT query must be array');
}
$first = current($values);
if (is_string(key($values))) {
// is one row array
$this->addOneValue($values);
} elseif (is_array($first) && is_string(key($first))) {
// this is multi values
foreach ($values as $oneValue) {
$this->addOneValue($oneValue);
}
}
return $this;
}
/**
* Force insert operation to fail silently
*
* @return \InsertQuery
*/
public function ignore() {
$this->ignore = true;
return $this;
}
/** Force insert operation delay support
*
* @return \InsertQuery
*/
public function delayed() {
$this->delayed = true;
return $this;
}
/**
* @return string
*/
protected function getClauseInsertInto() {
return 'INSERT' . ($this->ignore ? " IGNORE" : '') . ($this->delayed ? " DELAYED" : '') . ' INTO ' . $this->statements['INSERT INTO'];
}
/**
* @param $param
*
* @return string
*/
protected function parameterGetValue($param) {
return $param instanceof FluentLiteral ? (string)$param : '?';
}
/**
* @return string
*/
protected function getClauseValues() {
$valuesArray = array();
foreach ($this->statements['VALUES'] as $rows) {
// literals should not be parametrized.
// They are commonly used to call engine functions or literals.
// Eg: NOW(), CURRENT_TIMESTAMP etc
$placeholders = array_map(array($this, 'parameterGetValue'), $rows);
$valuesArray[] = '(' . implode(', ', $placeholders) . ')';
}
$columns = implode(', ', $this->columns);
$values = implode(', ', $valuesArray);
return " ($columns) VALUES $values";
}
/**
* Removes all FluentLiteral instances from the argument
* since they are not to be used as PDO parameters but rather injected directly into the query
*
* @param $statements
*
* @return array
*/
protected function filterLiterals($statements) {
$f = function ($item) {
return !$item instanceof FluentLiteral;
};
return array_map(function ($item) use ($f) {
if (is_array($item)) {
return array_filter($item, $f);
}
return $item;
}, array_filter($statements, $f));
}
/**
* @return array
*/
protected function buildParameters() {
$this->parameters = array_merge(
$this->filterLiterals($this->statements['VALUES']),
$this->filterLiterals($this->statements['ON DUPLICATE KEY UPDATE'])
);
return parent::buildParameters();
}
/**
* @return string
*/
protected function getClauseOnDuplicateKeyUpdate() {
$result = array();
foreach ($this->statements['ON DUPLICATE KEY UPDATE'] as $key => $value) {
$result[] = "$key = " . $this->parameterGetValue($value);
}
return ' ON DUPLICATE KEY UPDATE ' . implode(', ', $result);
}
/**
* @param array $oneValue
*
* @throws Exception
*/
private function addOneValue($oneValue) {
// check if all $keys are strings
foreach ($oneValue as $key => $value) {
if (!is_string($key)) {
throw new Exception('INSERT query: All keys of value array have to be strings.');
}
}
if (!$this->firstValue) {
$this->firstValue = $oneValue;
}
if (!$this->columns) {
$this->columns = array_keys($oneValue);
}
if ($this->columns != array_keys($oneValue)) {
throw new Exception('INSERT query: All VALUES have to same keys (columns).');
}
$this->statements['VALUES'][] = $oneValue;
}
}

194
vendor/FluentPDO/SelectQuery.php vendored Normal file
View file

@ -0,0 +1,194 @@
<?php
/**
* SELECT query builder
*
* @method SelectQuery select(string $column) add one or more columns in SELECT to query
* @method SelectQuery leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method SelectQuery innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method SelectQuery groupBy(string $column) add GROUP BY to query
* @method SelectQuery having(string $column) add HAVING query
* @method SelectQuery orderBy(string $column) add ORDER BY to query
* @method SelectQuery limit(int $limit) add LIMIT to query
* @method SelectQuery offset(int $offset) add OFFSET to query
*/
class SelectQuery extends CommonQuery implements Countable
{
/** @var mixed */
private $fromTable;
/** @var mixed */
private $fromAlias;
/** @var boolean */
private $convertTypes = false;
/**
* SelectQuery constructor.
*
* @param FluentPDO $fpdo
* @param $from
*/
function __construct(FluentPDO $fpdo, $from) {
$clauses = array(
'SELECT' => ', ',
'FROM' => null,
'JOIN' => array($this, 'getClauseJoin'),
'WHERE' => ' AND ',
'GROUP BY' => ',',
'HAVING' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
'OFFSET' => null,
"\n--" => "\n--",
);
parent::__construct($fpdo, $clauses);
// initialize statements
$fromParts = explode(' ', $from);
$this->fromTable = reset($fromParts);
$this->fromAlias = end($fromParts);
$this->statements['FROM'] = $from;
$this->statements['SELECT'][] = $this->fromAlias . '.*';
$this->joins[] = $this->fromAlias;
if(isset($fpdo->convertTypes) && $fpdo->convertTypes){
$this->convertTypes = true;
}
}
/** Return table name from FROM clause
*
* @internal
*/
public function getFromTable() {
return $this->fromTable;
}
/** Return table alias from FROM clause
*
* @internal
*/
public function getFromAlias() {
return $this->fromAlias;
}
/**
* Returns a single column
*
* @param int $columnNumber
*
* @return string
*/
public function fetchColumn($columnNumber = 0) {
if (($s = $this->execute()) !== false) {
return $s->fetchColumn($columnNumber);
}
return $s;
}
/**
* Fetch first row or column
*
* @param string $column column name or empty string for the whole row
*
* @return mixed string, array or false if there is no row
*/
public function fetch($column = '') {
$s = $this->execute();
if ($s === false) {
return false;
}
$row = $s->fetch();
if($this->convertTypes){
$row = FluentUtils::convertToNativeTypes($s,$row);
}
if ($row && $column != '') {
if (is_object($row)) {
return $row->{$column};
} else {
return $row[$column];
}
}
return $row;
}
/**
* Fetch pairs
*
* @param $key
* @param $value
* @param $object
*
* @return array of fetched rows as pairs
*/
public function fetchPairs($key, $value, $object = false) {
if (($s = $this->select(null)->select("$key, $value")->asObject($object)->execute()) !== false) {
return $s->fetchAll(PDO::FETCH_KEY_PAIR);
}
return $s;
}
/** Fetch all row
*
* @param string $index specify index column
* @param string $selectOnly select columns which could be fetched
*
* @return array of fetched rows
*/
public function fetchAll($index = '', $selectOnly = '') {
if ($selectOnly) {
$this->select(null)->select($index . ', ' . $selectOnly);
}
if ($index) {
$data = array();
foreach ($this as $row) {
if (is_object($row)) {
$data[$row->{$index}] = $row;
} else {
$data[$row[$index]] = $row;
}
}
return $data;
} else {
if (($s = $this->execute()) !== false) {
if($this->convertTypes){
return FluentUtils::convertToNativeTypes($s,$s->fetchAll());
}else{
return $s->fetchAll();
}
}
return $s;
}
}
/**
* Countable interface doesn't break current \FluentPDO select query
*
* @return int
*/
public function count() {
$fpdo = clone $this;
return (int)$fpdo->select(null)->select('COUNT(*)')->fetchColumn();
}
public function getIterator() {
if($this->convertTypes){
return new ArrayIterator($this->fetchAll());
}else{
return $this->execute();
}
}
}

107
vendor/FluentPDO/UpdateQuery.php vendored Normal file
View file

@ -0,0 +1,107 @@
<?php
/** UPDATE query builder
*
* @method UpdateQuery leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method UpdateQuery innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method UpdateQuery orderBy(string $column) add ORDER BY to query
* @method UpdateQuery limit(int $limit) add LIMIT to query
*/
class UpdateQuery extends CommonQuery
{
/**
* UpdateQuery constructor.
*
* @param FluentPDO $fpdo
* @param $table
*/
public function __construct(FluentPDO $fpdo, $table) {
$clauses = array(
'UPDATE' => array($this, 'getClauseUpdate'),
'JOIN' => array($this, 'getClauseJoin'),
'SET' => array($this, 'getClauseSet'),
'WHERE' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
);
parent::__construct($fpdo, $clauses);
$this->statements['UPDATE'] = $table;
$tableParts = explode(' ', $table);
$this->joins[] = end($tableParts);
}
/**
* @param string|array $fieldOrArray
* @param bool|string $value
*
* @return $this
* @throws Exception
*/
public function set($fieldOrArray, $value = false) {
if (!$fieldOrArray) {
return $this;
}
if (is_string($fieldOrArray) && $value !== false) {
$this->statements['SET'][$fieldOrArray] = $value;
} else {
if (!is_array($fieldOrArray)) {
throw new Exception('You must pass a value, or provide the SET list as an associative array. column => value');
} else {
foreach ($fieldOrArray as $field => $value) {
$this->statements['SET'][$field] = $value;
}
}
}
return $this;
}
/**
* Execute update query
*
* @param boolean $getResultAsPdoStatement true to return the pdo statement instead of row count
*
* @return int|boolean|\PDOStatement
*/
public function execute($getResultAsPdoStatement = false) {
$result = parent::execute();
if ($getResultAsPdoStatement) {
return $result;
}
if ($result) {
return $result->rowCount();
}
return false;
}
/**
* @return string
*/
protected function getClauseUpdate() {
return 'UPDATE ' . $this->statements['UPDATE'];
}
/**
* @return string
*/
protected function getClauseSet() {
$setArray = array();
foreach ($this->statements['SET'] as $field => $value) {
if ($value instanceof FluentLiteral) {
$setArray[] = $field . ' = ' . $value;
} else {
$setArray[] = $field . ' = ?';
$this->parameters['SET'][$field] = $value;
}
}
return ' SET ' . implode(', ', $setArray);
}
}