api de gestion de ticket, basé sur php-crud-api. Le but est de décorrélé les outils de gestion des données, afin
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

api.php 74KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313
  1. <?php
  2. //var_dump($_SERVER['REQUEST_METHOD'],$_SERVER['PATH_INFO']); die();
  3. interface DatabaseInterface {
  4. public function getSql($name);
  5. public function connect($hostname,$username,$password,$database,$port,$socket,$charset);
  6. public function query($sql,$params=array());
  7. public function fetchAssoc($result,$fields=false);
  8. public function fetchRow($result,$fields=false);
  9. public function insertId($result);
  10. public function affectedRows($result);
  11. public function close($result);
  12. public function fetchFields($table);
  13. public function addLimitToSql($sql,$limit,$offset);
  14. public function likeEscape($string);
  15. public function isBinaryType($field);
  16. public function isGeometryType($field);
  17. public function getDefaultCharset();
  18. public function beginTransaction();
  19. public function commitTransaction();
  20. public function rollbackTransaction();
  21. }
  22. class MySQL implements DatabaseInterface {
  23. protected $db;
  24. protected $queries;
  25. public function __construct() {
  26. $this->queries = array(
  27. 'list_tables'=>'SELECT
  28. "TABLE_NAME","TABLE_COMMENT"
  29. FROM
  30. "INFORMATION_SCHEMA"."TABLES"
  31. WHERE
  32. "TABLE_SCHEMA" = ?',
  33. 'reflect_table'=>'SELECT
  34. "TABLE_NAME"
  35. FROM
  36. "INFORMATION_SCHEMA"."TABLES"
  37. WHERE
  38. "TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
  39. "TABLE_SCHEMA" = ?',
  40. 'reflect_pk'=>'SELECT
  41. "COLUMN_NAME"
  42. FROM
  43. "INFORMATION_SCHEMA"."COLUMNS"
  44. WHERE
  45. "COLUMN_KEY" = \'PRI\' AND
  46. "TABLE_NAME" = ? AND
  47. "TABLE_SCHEMA" = ?',
  48. 'reflect_belongs_to'=>'SELECT
  49. "TABLE_NAME","COLUMN_NAME",
  50. "REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
  51. FROM
  52. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
  53. WHERE
  54. "TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
  55. "REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' IN ? AND
  56. "TABLE_SCHEMA" = ? AND
  57. "REFERENCED_TABLE_SCHEMA" = ?',
  58. 'reflect_has_many'=>'SELECT
  59. "TABLE_NAME","COLUMN_NAME",
  60. "REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
  61. FROM
  62. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
  63. WHERE
  64. "TABLE_NAME" COLLATE \'utf8_bin\' IN ? AND
  65. "REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
  66. "TABLE_SCHEMA" = ? AND
  67. "REFERENCED_TABLE_SCHEMA" = ?',
  68. 'reflect_habtm'=>'SELECT
  69. k1."TABLE_NAME", k1."COLUMN_NAME",
  70. k1."REFERENCED_TABLE_NAME", k1."REFERENCED_COLUMN_NAME",
  71. k2."TABLE_NAME", k2."COLUMN_NAME",
  72. k2."REFERENCED_TABLE_NAME", k2."REFERENCED_COLUMN_NAME"
  73. FROM
  74. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k1,
  75. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k2
  76. WHERE
  77. k1."TABLE_SCHEMA" = ? AND
  78. k2."TABLE_SCHEMA" = ? AND
  79. k1."REFERENCED_TABLE_SCHEMA" = ? AND
  80. k2."REFERENCED_TABLE_SCHEMA" = ? AND
  81. k1."TABLE_NAME" COLLATE \'utf8_bin\' = k2."TABLE_NAME" COLLATE \'utf8_bin\' AND
  82. k1."REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
  83. k2."REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' IN ?'
  84. );
  85. }
  86. public function getSql($name) {
  87. return isset($this->queries[$name])?$this->queries[$name]:false;
  88. }
  89. public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
  90. $db = mysqli_init();
  91. if (defined('MYSQLI_OPT_INT_AND_FLOAT_NATIVE')) {
  92. mysqli_options($db,MYSQLI_OPT_INT_AND_FLOAT_NATIVE,true);
  93. }
  94. $success = mysqli_real_connect($db,$hostname,$username,$password,$database,$port,$socket,MYSQLI_CLIENT_FOUND_ROWS);
  95. if (!$success) {
  96. throw new \Exception('Connect failed. '.mysqli_connect_error());
  97. }
  98. if (!mysqli_set_charset($db,$charset)) {
  99. throw new \Exception('Error setting charset. '.mysqli_error($db));
  100. }
  101. if (!mysqli_query($db,'SET SESSION sql_mode = \'ANSI_QUOTES\';')) {
  102. throw new \Exception('Error setting ANSI quotes. '.mysqli_error($db));
  103. }
  104. $this->db = $db;
  105. }
  106. public function query($sql,$params=array()) {
  107. $db = $this->db;
  108. $sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
  109. $param = array_shift($params);
  110. if ($matches[0]=='!') {
  111. $key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
  112. if (is_object($param) && $param->type=='base64') {
  113. return "TO_BASE64(\"$key\") as \"$key\"";
  114. }
  115. if (is_object($param) && $param->type=='wkt') {
  116. return "ST_AsText(\"$key\") as \"$key\"";
  117. }
  118. return '"'.$key.'"';
  119. } else {
  120. if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
  121. return "'".mysqli_real_escape_string($db,$v)."'";
  122. },$param)).')';
  123. if (is_object($param) && $param->type=='base64') {
  124. return "x'".bin2hex(base64_decode($param->value))."'";
  125. }
  126. if (is_object($param) && $param->type=='wkt') {
  127. return "ST_GeomFromText('".mysqli_real_escape_string($db,$param->value)."')";
  128. }
  129. if ($param===null) return 'NULL';
  130. return "'".mysqli_real_escape_string($db,$param)."'";
  131. }
  132. }, $sql);
  133. //if (!strpos($sql,'INFORMATION_SCHEMA')) echo "\n$sql\n";
  134. return mysqli_query($db,$sql);
  135. }
  136. protected function convertFloatAndInt($result,&$values, $fields) {
  137. array_walk($values, function(&$v,$i) use ($result,$fields){
  138. $t = $fields[$i]->type;
  139. if (is_string($v) && in_array($t,array(1,2,3,4,5,6,8,9))) {
  140. $v+=0;
  141. }
  142. });
  143. }
  144. public function fetchAssoc($result,$fields=false) {
  145. $values = mysqli_fetch_assoc($result);
  146. if ($values && $fields && !defined('MYSQLI_OPT_INT_AND_FLOAT_NATIVE')) {
  147. $this->convertFloatAndInt($result,$values,$fields);
  148. }
  149. return $values;
  150. }
  151. public function fetchRow($result,$fields=false) {
  152. $values = mysqli_fetch_row($result);
  153. if ($values && $fields && !defined('MYSQLI_OPT_INT_AND_FLOAT_NATIVE')) {
  154. $this->convertFloatAndInt($result,$values,array_values($fields));
  155. }
  156. return $values;
  157. }
  158. public function insertId($result) {
  159. return mysqli_insert_id($this->db);
  160. }
  161. public function affectedRows($result) {
  162. return mysqli_affected_rows($this->db);
  163. }
  164. public function close($result) {
  165. return mysqli_free_result($result);
  166. }
  167. public function fetchFields($table) {
  168. $result = $this->query('SELECT * FROM ! WHERE 1=2;',array($table));
  169. return mysqli_fetch_fields($result);
  170. }
  171. public function addLimitToSql($sql,$limit,$offset) {
  172. return "$sql LIMIT $limit OFFSET $offset";
  173. }
  174. public function addOrderByDifferenceToSql($sql,$field,$direction) {
  175. if ($this->isGeometryType($field)) {
  176. return "$sql ORDER BY ST_Distance(!, ?) $direction";
  177. } else {
  178. return "$sql ORDER BY ABS(! - ?) $direction";
  179. }
  180. }
  181. public function likeEscape($string) {
  182. return addcslashes($string,'%_');
  183. }
  184. public function convertFilter($field, $comparator, $value) {
  185. return false;
  186. }
  187. public function isBinaryType($field) {
  188. //echo "$field->name: $field->type ($field->flags)\n";
  189. return (($field->flags & 128) && ($field->type>=249) && ($field->type<=252));
  190. }
  191. public function isGeometryType($field) {
  192. return ($field->type==255);
  193. }
  194. public function getDefaultCharset() {
  195. return 'utf8';
  196. }
  197. public function beginTransaction() {
  198. mysqli_query($this->db,'BEGIN');
  199. //return mysqli_begin_transaction($this->db);
  200. }
  201. public function commitTransaction() {
  202. mysqli_query($this->db,'COMMIT');
  203. //return mysqli_commit($this->db);
  204. }
  205. public function rollbackTransaction() {
  206. mysqli_query($this->db,'ROLLBACK');
  207. //return mysqli_rollback($this->db);
  208. }
  209. }
  210. class PostgreSQL implements DatabaseInterface {
  211. protected $db;
  212. protected $queries;
  213. public function __construct() {
  214. $this->queries = array(
  215. 'list_tables'=>'select
  216. "table_name","table_comment"
  217. from
  218. "information_schema"."tables"
  219. where
  220. "table_catalog" = ?',
  221. 'reflect_table'=>'select
  222. "table_name"
  223. from
  224. "information_schema"."tables"
  225. where
  226. "table_name" like ? and
  227. "table_catalog" = ?',
  228. 'reflect_pk'=>'select
  229. "column_name"
  230. from
  231. "information_schema"."table_constraints" tc,
  232. "information_schema"."key_column_usage" ku
  233. where
  234. tc."constraint_type" = \'PRIMARY KEY\' and
  235. tc."constraint_name" = ku."constraint_name" and
  236. ku."table_name" = ? and
  237. ku."table_catalog" = ?',
  238. 'reflect_belongs_to'=>'select
  239. cu1."table_name",cu1."column_name",
  240. cu2."table_name",cu2."column_name"
  241. from
  242. "information_schema".referential_constraints rc,
  243. "information_schema".key_column_usage cu1,
  244. "information_schema".key_column_usage cu2
  245. where
  246. cu1."constraint_name" = rc."constraint_name" and
  247. cu2."constraint_name" = rc."unique_constraint_name" and
  248. cu1."table_name" = ? and
  249. cu2."table_name" in ? and
  250. cu1."table_catalog" = ? and
  251. cu2."table_catalog" = ?',
  252. 'reflect_has_many'=>'select
  253. cu1."table_name",cu1."column_name",
  254. cu2."table_name",cu2."column_name"
  255. from
  256. "information_schema".referential_constraints rc,
  257. "information_schema".key_column_usage cu1,
  258. "information_schema".key_column_usage cu2
  259. where
  260. cu1."constraint_name" = rc."constraint_name" and
  261. cu2."constraint_name" = rc."unique_constraint_name" and
  262. cu1."table_name" in ? and
  263. cu2."table_name" = ? and
  264. cu1."table_catalog" = ? and
  265. cu2."table_catalog" = ?',
  266. 'reflect_habtm'=>'select
  267. cua1."table_name",cua1."column_name",
  268. cua2."table_name",cua2."column_name",
  269. cub1."table_name",cub1."column_name",
  270. cub2."table_name",cub2."column_name"
  271. from
  272. "information_schema".referential_constraints rca,
  273. "information_schema".referential_constraints rcb,
  274. "information_schema".key_column_usage cua1,
  275. "information_schema".key_column_usage cua2,
  276. "information_schema".key_column_usage cub1,
  277. "information_schema".key_column_usage cub2
  278. where
  279. cua1."constraint_name" = rca."constraint_name" and
  280. cua2."constraint_name" = rca."unique_constraint_name" and
  281. cub1."constraint_name" = rcb."constraint_name" and
  282. cub2."constraint_name" = rcb."unique_constraint_name" and
  283. cua1."table_catalog" = ? and
  284. cub1."table_catalog" = ? and
  285. cua2."table_catalog" = ? and
  286. cub2."table_catalog" = ? and
  287. cua1."table_name" = cub1."table_name" and
  288. cua2."table_name" = ? and
  289. cub2."table_name" in ?'
  290. );
  291. }
  292. public function getSql($name) {
  293. return isset($this->queries[$name])?$this->queries[$name]:false;
  294. }
  295. public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
  296. $e = function ($v) { return str_replace(array('\'','\\'),array('\\\'','\\\\'),$v); };
  297. $conn_string = '';
  298. if ($hostname || $socket) {
  299. if ($socket) $hostname = $e($socket);
  300. else $hostname = $e($hostname);
  301. $conn_string.= " host='$hostname'";
  302. }
  303. if ($port) {
  304. $port = ($port+0);
  305. $conn_string.= " port='$port'";
  306. }
  307. if ($database) {
  308. $database = $e($database);
  309. $conn_string.= " dbname='$database'";
  310. }
  311. if ($username) {
  312. $username = $e($username);
  313. $conn_string.= " user='$username'";
  314. }
  315. if ($password) {
  316. $password = $e($password);
  317. $conn_string.= " password='$password'";
  318. }
  319. if ($charset) {
  320. $charset = $e($charset);
  321. $conn_string.= " options='--client_encoding=$charset'";
  322. }
  323. $db = pg_connect($conn_string);
  324. $this->db = $db;
  325. }
  326. public function query($sql,$params=array()) {
  327. $db = $this->db;
  328. $sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
  329. $param = array_shift($params);
  330. if ($matches[0]=='!') {
  331. $key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
  332. if (is_object($param) && $param->type=='base64') {
  333. return "encode(\"$key\",'base64') as \"$key\"";
  334. }
  335. if (is_object($param) && $param->type=='wkt') {
  336. return "ST_AsText(\"$key\") as \"$key\"";
  337. }
  338. return '"'.$key.'"';
  339. } else {
  340. if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
  341. return "'".pg_escape_string($db,$v)."'";
  342. },$param)).')';
  343. if (is_object($param) && $param->type=='base64') {
  344. return "'\x".bin2hex(base64_decode($param->value))."'";
  345. }
  346. if (is_object($param) && $param->type=='wkt') {
  347. return "ST_GeomFromText('".pg_escape_string($db,$param->value)."')";
  348. }
  349. if ($param===null) return 'NULL';
  350. return "'".pg_escape_string($db,$param)."'";
  351. }
  352. }, $sql);
  353. if (strtoupper(substr($sql,0,6))=='INSERT') {
  354. $sql .= ' RETURNING id;';
  355. }
  356. //echo "\n$sql\n";
  357. return @pg_query($db,$sql);
  358. }
  359. protected function convertFloatAndInt($result,&$values, $fields) {
  360. array_walk($values, function(&$v,$i) use ($result,$fields){
  361. $t = $fields[$i]->type;
  362. if (is_string($v) && in_array($t,array('int2','int4','int8','float4','float8'))) {
  363. $v+=0;
  364. }
  365. });
  366. }
  367. public function fetchAssoc($result,$fields=false) {
  368. $values = pg_fetch_assoc($result);
  369. if ($values && $fields) {
  370. $this->convertFloatAndInt($result,$values,$fields);
  371. }
  372. return $values;
  373. }
  374. public function fetchRow($result,$fields=false) {
  375. $values = pg_fetch_row($result);
  376. if ($values && $fields) {
  377. $this->convertFloatAndInt($result,$values,array_values($fields));
  378. }
  379. return $values;
  380. }
  381. public function insertId($result) {
  382. list($id) = pg_fetch_row($result);
  383. return (int)$id;
  384. }
  385. public function affectedRows($result) {
  386. return pg_affected_rows($result);
  387. }
  388. public function close($result) {
  389. return pg_free_result($result);
  390. }
  391. public function fetchFields($table) {
  392. $result = $this->query('SELECT * FROM ! WHERE 1=2;',array($table));
  393. $keys = array();
  394. for($i=0;$i<pg_num_fields($result);$i++) {
  395. $field = array();
  396. $field['name'] = pg_field_name($result,$i);
  397. $field['type'] = pg_field_type($result,$i);
  398. $keys[$i] = (object)$field;
  399. }
  400. return $keys;
  401. }
  402. public function addLimitToSql($sql,$limit,$offset) {
  403. return "$sql LIMIT $limit OFFSET $offset";
  404. }
  405. public function likeEscape($string) {
  406. return addcslashes($string,'%_');
  407. }
  408. public function convertFilter($field, $comparator, $value) {
  409. return false;
  410. }
  411. public function isBinaryType($field) {
  412. return $field->type == 'bytea';
  413. }
  414. public function isGeometryType($field) {
  415. return $field->type == 'geometry';
  416. }
  417. public function getDefaultCharset() {
  418. return 'UTF8';
  419. }
  420. public function beginTransaction() {
  421. return $this->query('BEGIN');
  422. }
  423. public function commitTransaction() {
  424. return $this->query('COMMIT');
  425. }
  426. public function rollbackTransaction() {
  427. return $this->query('ROLLBACK');
  428. }
  429. }
  430. class SQLServer implements DatabaseInterface {
  431. protected $db;
  432. protected $queries;
  433. public function __construct() {
  434. $this->queries = array(
  435. 'list_tables'=>'SELECT
  436. "TABLE_NAME",\'\' as "TABLE_COMMENT"
  437. FROM
  438. "INFORMATION_SCHEMA"."TABLES"
  439. WHERE
  440. "TABLE_CATALOG" = ?',
  441. 'reflect_table'=>'SELECT
  442. "TABLE_NAME"
  443. FROM
  444. "INFORMATION_SCHEMA"."TABLES"
  445. WHERE
  446. "TABLE_NAME" LIKE ? AND
  447. "TABLE_CATALOG" = ?',
  448. 'reflect_pk'=>'SELECT
  449. "COLUMN_NAME"
  450. FROM
  451. "INFORMATION_SCHEMA"."TABLE_CONSTRAINTS" tc,
  452. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" ku
  453. WHERE
  454. tc."CONSTRAINT_TYPE" = \'PRIMARY KEY\' AND
  455. tc."CONSTRAINT_NAME" = ku."CONSTRAINT_NAME" AND
  456. ku."TABLE_NAME" = ? AND
  457. ku."TABLE_CATALOG" = ?',
  458. 'reflect_belongs_to'=>'SELECT
  459. cu1."TABLE_NAME",cu1."COLUMN_NAME",
  460. cu2."TABLE_NAME",cu2."COLUMN_NAME"
  461. FROM
  462. "INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rc,
  463. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu1,
  464. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu2
  465. WHERE
  466. cu1."CONSTRAINT_NAME" = rc."CONSTRAINT_NAME" AND
  467. cu2."CONSTRAINT_NAME" = rc."UNIQUE_CONSTRAINT_NAME" AND
  468. cu1."TABLE_NAME" = ? AND
  469. cu2."TABLE_NAME" IN ? AND
  470. cu1."TABLE_CATALOG" = ? AND
  471. cu2."TABLE_CATALOG" = ?',
  472. 'reflect_has_many'=>'SELECT
  473. cu1."TABLE_NAME",cu1."COLUMN_NAME",
  474. cu2."TABLE_NAME",cu2."COLUMN_NAME"
  475. FROM
  476. "INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rc,
  477. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu1,
  478. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu2
  479. WHERE
  480. cu1."CONSTRAINT_NAME" = rc."CONSTRAINT_NAME" AND
  481. cu2."CONSTRAINT_NAME" = rc."UNIQUE_CONSTRAINT_NAME" AND
  482. cu1."TABLE_NAME" IN ? AND
  483. cu2."TABLE_NAME" = ? AND
  484. cu1."TABLE_CATALOG" = ? AND
  485. cu2."TABLE_CATALOG" = ?',
  486. 'reflect_habtm'=>'SELECT
  487. cua1."TABLE_NAME",cua1."COLUMN_NAME",
  488. cua2."TABLE_NAME",cua2."COLUMN_NAME",
  489. cub1."TABLE_NAME",cub1."COLUMN_NAME",
  490. cub2."TABLE_NAME",cub2."COLUMN_NAME"
  491. FROM
  492. "INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rca,
  493. "INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rcb,
  494. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cua1,
  495. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cua2,
  496. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cub1,
  497. "INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cub2
  498. WHERE
  499. cua1."CONSTRAINT_NAME" = rca."CONSTRAINT_NAME" AND
  500. cua2."CONSTRAINT_NAME" = rca."UNIQUE_CONSTRAINT_NAME" AND
  501. cub1."CONSTRAINT_NAME" = rcb."CONSTRAINT_NAME" AND
  502. cub2."CONSTRAINT_NAME" = rcb."UNIQUE_CONSTRAINT_NAME" AND
  503. cua1."TABLE_CATALOG" = ? AND
  504. cub1."TABLE_CATALOG" = ? AND
  505. cua2."TABLE_CATALOG" = ? AND
  506. cub2."TABLE_CATALOG" = ? AND
  507. cua1."TABLE_NAME" = cub1."TABLE_NAME" AND
  508. cua2."TABLE_NAME" = ? AND
  509. cub2."TABLE_NAME" IN ?'
  510. );
  511. }
  512. public function getSql($name) {
  513. return isset($this->queries[$name])?$this->queries[$name]:false;
  514. }
  515. public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
  516. $connectionInfo = array();
  517. if ($port) $hostname.=','.$port;
  518. if ($username) $connectionInfo['UID']=$username;
  519. if ($password) $connectionInfo['PWD']=$password;
  520. if ($database) $connectionInfo['Database']=$database;
  521. if ($charset) $connectionInfo['CharacterSet']=$charset;
  522. $connectionInfo['QuotedId']=1;
  523. $connectionInfo['ReturnDatesAsStrings']=1;
  524. $db = sqlsrv_connect($hostname, $connectionInfo);
  525. if (!$db) {
  526. throw new \Exception('Connect failed. '.print_r( sqlsrv_errors(), true));
  527. }
  528. if ($socket) {
  529. throw new \Exception('Socket connection is not supported.');
  530. }
  531. $this->db = $db;
  532. }
  533. public function query($sql,$params=array()) {
  534. $args = array();
  535. $db = $this->db;
  536. $sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params,&$args) {
  537. static $i=-1;
  538. $i++;
  539. $param = $params[$i];
  540. if ($matches[0]=='!') {
  541. $key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
  542. if (is_object($param) && $param->type=='base64') {
  543. return "CAST(N'' AS XML).value('xs:base64Binary(xs:hexBinary(sql:column(\"$key\")))', 'VARCHAR(MAX)') as \"$key\"";
  544. }
  545. if (is_object($param) && $param->type=='wkt') {
  546. return "\"$key\".STAsText() as \"$key\"";
  547. }
  548. return '"'.$key.'"';
  549. } else {
  550. // This is workaround because SQLSRV cannot accept NULL in a param
  551. if ($matches[0]=='?' && is_null($param)) {
  552. return 'NULL';
  553. }
  554. if (is_array($param)) {
  555. $args = array_merge($args,$param);
  556. return '('.implode(',',str_split(str_repeat('?',count($param)))).')';
  557. }
  558. if (is_object($param) && $param->type=='base64') {
  559. $args[] = bin2hex(base64_decode($param->value));
  560. return 'CONVERT(VARBINARY(MAX),?,2)';
  561. }
  562. if (is_object($param) && $param->type=='wkt') {
  563. $args[] = $param->value;
  564. return 'geometry::STGeomFromText(?,0)';
  565. }
  566. $args[] = $param;
  567. return '?';
  568. }
  569. }, $sql);
  570. //var_dump($params);
  571. //echo "\n$sql\n";
  572. //var_dump($args);
  573. //file_put_contents('sql.txt',"\n$sql\n".var_export($args,true)."\n",FILE_APPEND);
  574. if (strtoupper(substr($sql,0,6))=='INSERT') {
  575. $sql .= ';SELECT SCOPE_IDENTITY()';
  576. }
  577. return sqlsrv_query($db,$sql,$args)?:null;
  578. }
  579. public function fetchAssoc($result,$fields=false) {
  580. return sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC);
  581. }
  582. public function fetchRow($result,$fields=false) {
  583. return sqlsrv_fetch_array($result, SQLSRV_FETCH_NUMERIC);
  584. }
  585. public function insertId($result) {
  586. sqlsrv_next_result($result);
  587. sqlsrv_fetch($result);
  588. return (int)sqlsrv_get_field($result, 0);
  589. }
  590. public function affectedRows($result) {
  591. return sqlsrv_rows_affected($result);
  592. }
  593. public function close($result) {
  594. return sqlsrv_free_stmt($result);
  595. }
  596. public function fetchFields($table) {
  597. $result = $this->query('SELECT * FROM ! WHERE 1=2;',array($table));
  598. //var_dump(sqlsrv_field_metadata($result));
  599. return array_map(function($a){
  600. $p = array();
  601. foreach ($a as $k=>$v) {
  602. $p[strtolower($k)] = $v;
  603. }
  604. return (object)$p;
  605. },sqlsrv_field_metadata($result));
  606. }
  607. public function addLimitToSql($sql,$limit,$offset) {
  608. return "$sql OFFSET $offset ROWS FETCH NEXT $limit ROWS ONLY";
  609. }
  610. public function likeEscape($string) {
  611. return str_replace(array('%','_'),array('[%]','[_]'),$string);
  612. }
  613. public function convertFilter($field, $comparator, $value) {
  614. $comparator = strtolower($comparator);
  615. if ($comparator[0]!='n') {
  616. switch ($comparator) {
  617. case 'sco': return array('!.STContains(geometry::STGeomFromText(?,0))=1',$field,$value);
  618. case 'scr': return array('!.STCrosses(geometry::STGeomFromText(?,0))=1',$field,$value);
  619. case 'sdi': return array('!.STDisjoint(geometry::STGeomFromText(?,0))=1',$field,$value);
  620. case 'seq': return array('!.STEquals(geometry::STGeomFromText(?,0))=1',$field,$value);
  621. case 'sin': return array('!.STIntersects(geometry::STGeomFromText(?,0))=1',$field,$value);
  622. case 'sov': return array('!.STOverlaps(geometry::STGeomFromText(?,0))=1',$field,$value);
  623. case 'sto': return array('!.STTouches(geometry::STGeomFromText(?,0))=1',$field,$value);
  624. case 'swi': return array('!.STWithin(geometry::STGeomFromText(?,0))=1',$field,$value);
  625. case 'sic': return array('!.STIsClosed()=1',$field);
  626. case 'sis': return array('!.STIsSimple()=1',$field);
  627. case 'siv': return array('!.STIsValid()=1',$field);
  628. }
  629. } else {
  630. switch ($comparator) {
  631. case 'nsco': return array('!.STContains(geometry::STGeomFromText(?,0))=0',$field,$value);
  632. case 'nscr': return array('!.STCrosses(geometry::STGeomFromText(?,0))=0',$field,$value);
  633. case 'nsdi': return array('!.STDisjoint(geometry::STGeomFromText(?,0))=0',$field,$value);
  634. case 'nseq': return array('!.STEquals(geometry::STGeomFromText(?,0))=0',$field,$value);
  635. case 'nsin': return array('!.STIntersects(geometry::STGeomFromText(?,0))=0',$field,$value);
  636. case 'nsov': return array('!.STOverlaps(geometry::STGeomFromText(?,0))=0',$field,$value);
  637. case 'nsto': return array('!.STTouches(geometry::STGeomFromText(?,0))=0',$field,$value);
  638. case 'nswi': return array('!.STWithin(geometry::STGeomFromText(?,0))=0',$field,$value);
  639. case 'nsic': return array('!.STIsClosed()=0',$field);
  640. case 'nsis': return array('!.STIsSimple()=0',$field);
  641. case 'nsiv': return array('!.STIsValid()=0',$field);
  642. }
  643. }
  644. return false;
  645. }
  646. public function isBinaryType($field) {
  647. return ($field->type>=-4 && $field->type<=-2);
  648. }
  649. public function isGeometryType($field) {
  650. return ($field->type==-151);
  651. }
  652. public function getDefaultCharset() {
  653. return 'UTF-8';
  654. }
  655. public function beginTransaction() {
  656. return sqlsrv_begin_transaction($this->db);
  657. }
  658. public function commitTransaction() {
  659. return sqlsrv_commit($this->db);
  660. }
  661. public function rollbackTransaction() {
  662. return sqlsrv_rollback($this->db);
  663. }
  664. }
  665. class SQLite implements DatabaseInterface {
  666. protected $db;
  667. protected $queries;
  668. public function __construct() {
  669. $this->queries = array(
  670. 'list_tables'=>'SELECT
  671. "name", ""
  672. FROM
  673. "sys/tables"',
  674. 'reflect_table'=>'SELECT
  675. "name"
  676. FROM
  677. "sys/tables"
  678. WHERE
  679. "name"=?',
  680. 'reflect_pk'=>'SELECT
  681. "name"
  682. FROM
  683. "sys/columns"
  684. WHERE
  685. "pk"=1 AND
  686. "self"=?',
  687. 'reflect_belongs_to'=>'SELECT
  688. "self", "from",
  689. "table", "to"
  690. FROM
  691. "sys/foreign_keys"
  692. WHERE
  693. "self" = ? AND
  694. "table" IN ? AND
  695. ? like "%" AND
  696. ? like "%"',
  697. 'reflect_has_many'=>'SELECT
  698. "self", "from",
  699. "table", "to"
  700. FROM
  701. "sys/foreign_keys"
  702. WHERE
  703. "self" IN ? AND
  704. "table" = ? AND
  705. ? like "%" AND
  706. ? like "%"',
  707. 'reflect_habtm'=>'SELECT
  708. k1."self", k1."from",
  709. k1."table", k1."to",
  710. k2."self", k2."from",
  711. k2."table", k2."to"
  712. FROM
  713. "sys/foreign_keys" k1,
  714. "sys/foreign_keys" k2
  715. WHERE
  716. ? like "%" AND
  717. ? like "%" AND
  718. ? like "%" AND
  719. ? like "%" AND
  720. k1."self" = k2."self" AND
  721. k1."table" = ? AND
  722. k2."table" IN ?'
  723. );
  724. }
  725. public function getSql($name) {
  726. return isset($this->queries[$name])?$this->queries[$name]:false;
  727. }
  728. public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
  729. $this->db = new SQLite3($database);
  730. // optimizations
  731. $this->db->querySingle('PRAGMA synchronous = NORMAL');
  732. $this->db->querySingle('PRAGMA foreign_keys = on');
  733. $reflection = $this->db->querySingle('SELECT name FROM sqlite_master WHERE type = "table" and name like "sys/%"');
  734. if (!$reflection) {
  735. //create reflection tables
  736. $this->query('CREATE table "sys/version" ("version" integer)');
  737. $this->query('CREATE table "sys/tables" ("name" text)');
  738. $this->query('CREATE table "sys/columns" ("self" text,"cid" integer,"name" text,"type" integer,"notnull" integer,"dflt_value" integer,"pk" integer)');
  739. $this->query('CREATE table "sys/foreign_keys" ("self" text,"id" integer,"seq" integer,"table" text,"from" text,"to" text,"on_update" text,"on_delete" text,"match" text)');
  740. }
  741. $version = $this->db->querySingle('pragma schema_version');
  742. if ($version != $this->db->querySingle('SELECT "version" from "sys/version"')) {
  743. // reflection may take a while
  744. set_time_limit(3600);
  745. // update version data
  746. $this->query('DELETE FROM "sys/version"');
  747. $this->query('INSERT into "sys/version" ("version") VALUES (?)',array($version));
  748. // update tables data
  749. $this->query('DELETE FROM "sys/tables"');
  750. $result = $this->query('SELECT * FROM sqlite_master WHERE (type = "table" or type = "view") and name not like "sys/%" and name<>"sqlite_sequence"');
  751. $tables = array();
  752. while ($row = $this->fetchAssoc($result)) {
  753. $tables[] = $row['name'];
  754. $this->query('INSERT into "sys/tables" ("name") VALUES (?)',array($row['name']));
  755. }
  756. // update columns and foreign_keys data
  757. $this->query('DELETE FROM "sys/columns"');
  758. $this->query('DELETE FROM "sys/foreign_keys"');
  759. foreach ($tables as $table) {
  760. $result = $this->query('pragma table_info(!)',array($table));
  761. while ($row = $this->fetchRow($result)) {
  762. array_unshift($row, $table);
  763. $this->query('INSERT into "sys/columns" ("self","cid","name","type","notnull","dflt_value","pk") VALUES (?,?,?,?,?,?,?)',$row);
  764. }
  765. $result = $this->query('pragma foreign_key_list(!)',array($table));
  766. while ($row = $this->fetchRow($result)) {
  767. array_unshift($row, $table);
  768. $this->query('INSERT into "sys/foreign_keys" ("self","id","seq","table","from","to","on_update","on_delete","match") VALUES (?,?,?,?,?,?,?,?,?)',$row);
  769. }
  770. }
  771. }
  772. }
  773. public function query($sql,$params=array()) {
  774. $db = $this->db;
  775. $sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
  776. $param = array_shift($params);
  777. if ($matches[0]=='!') {
  778. $key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
  779. return '"'.$key.'"';
  780. } else {
  781. if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
  782. return "'".$db->escapeString($v)."'";
  783. },$param)).')';
  784. if (is_object($param) && $param->type=='base64') {
  785. return "'".$db->escapeString($param->value)."'";
  786. }
  787. if ($param===null) return 'NULL';
  788. return "'".$db->escapeString($param)."'";
  789. }
  790. }, $sql);
  791. //echo "\n$sql\n";
  792. try { $result=$db->query($sql); } catch(\Exception $e) { $result=null; }
  793. return $result;
  794. }
  795. public function fetchAssoc($result,$fields=false) {
  796. return $result->fetchArray(SQLITE3_ASSOC);
  797. }
  798. public function fetchRow($result,$fields=false) {
  799. return $result->fetchArray(SQLITE3_NUM);
  800. }
  801. public function insertId($result) {
  802. return $this->db->lastInsertRowID();
  803. }
  804. public function affectedRows($result) {
  805. return $this->db->changes();
  806. }
  807. public function close($result) {
  808. return $result->finalize();
  809. }
  810. public function fetchFields($table) {
  811. $result = $this->query('SELECT * FROM "sys/columns" WHERE "self"=?;',array($table));
  812. $fields = array();
  813. while ($row = $this->fetchAssoc($result)){
  814. $fields[strtolower($row['name'])] = (object)$row;
  815. }
  816. return $fields;
  817. }
  818. public function addLimitToSql($sql,$limit,$offset) {
  819. return "$sql LIMIT $limit OFFSET $offset";
  820. }
  821. public function likeEscape($string) {
  822. return addcslashes($string,'%_');
  823. }
  824. public function convertFilter($field, $comparator, $value) {
  825. return false;
  826. }
  827. public function isBinaryType($field) {
  828. return (substr($field->type,0,4)=='data');
  829. }
  830. public function isGeometryType($field) {
  831. return false;
  832. }
  833. public function getDefaultCharset() {
  834. return 'utf8';
  835. }
  836. public function beginTransaction() {
  837. return $this->query('BEGIN');
  838. }
  839. public function commitTransaction() {
  840. return $this->query('COMMIT');
  841. }
  842. public function rollbackTransaction() {
  843. return $this->query('ROLLBACK');
  844. }
  845. }
  846. class PHP_CRUD_API {
  847. protected $db;
  848. protected $settings;
  849. protected function mapMethodToAction($method,$key) {
  850. switch ($method) {
  851. case 'OPTIONS': return 'headers';
  852. case 'GET': return $key?'read':'list';
  853. case 'PUT': return 'update';
  854. case 'POST': return 'create';
  855. case 'DELETE': return 'delete';
  856. default: $this->exitWith404('method');
  857. }
  858. return false;
  859. }
  860. protected function parseRequestParameter(&$request,$characters) {
  861. if (!$request) return false;
  862. $pos = strpos($request,'/');
  863. $value = $pos?substr($request,0,$pos):$request;
  864. $request = $pos?substr($request,$pos+1):'';
  865. if (!$characters) return $value;
  866. return preg_replace("/[^$characters]/",'',$value);
  867. }
  868. protected function parseGetParameter($get,$name,$characters) {
  869. $value = isset($get[$name])?$get[$name]:false;
  870. return $characters?preg_replace("/[^$characters]/",'',$value):$value;
  871. }
  872. protected function parseGetParameterArray($get,$name,$characters) {
  873. $values = isset($get[$name])?$get[$name]:false;
  874. if (!is_array($values)) $values = array($values);
  875. if ($characters) {
  876. foreach ($values as &$value) {
  877. $value = preg_replace("/[^$characters]/",'',$value);
  878. }
  879. }
  880. return $values;
  881. }
  882. protected function applyTableAuthorizer($callback,$action,$database,&$tables) {
  883. if (is_callable($callback,true)) foreach ($tables as $i=>$table) {
  884. if (!$callback($action,$database,$table)) {
  885. unset($tables[$i]);
  886. }
  887. }
  888. }
  889. protected function applyRecordFilter($callback,$action,$database,$tables,&$filters) {
  890. if (is_callable($callback,true)) foreach ($tables as $i=>$table) {
  891. $this->addFilters($filters,$table,array($table=>'and'),$callback($action,$database,$table));
  892. }
  893. }
  894. protected function applyTenancyFunction($callback,$action,$database,$fields,&$filters) {
  895. if (is_callable($callback,true)) foreach ($fields as $table=>$keys) {
  896. foreach ($keys as $field) {
  897. $v = $callback($action,$database,$table,$field->name);
  898. if ($v!==null) {
  899. if (is_array($v)) $this->addFilter($filters,$table,'and',$field->name,'in',implode(',',$v));
  900. else $this->addFilter($filters,$table,'and',$field->name,'eq',$v);
  901. }
  902. }
  903. }
  904. }
  905. protected function applyColumnAuthorizer($callback,$action,$database,&$fields) {
  906. if (is_callable($callback,true)) foreach ($fields as $table=>$keys) {
  907. foreach ($keys as $field) {
  908. if (!$callback($action,$database,$table,$field->name)) {
  909. unset($fields[$table][$field->name]);
  910. }
  911. }
  912. }
  913. }
  914. protected function applyInputTenancy($callback,$action,$database,$table,&$input,$keys) {
  915. if (is_callable($callback,true)) foreach ($keys as $key=>$field) {
  916. $v = $callback($action,$database,$table,$key);
  917. if ($v!==null && (isset($input->$key) || $action=='create')) {
  918. if (is_array($v)) {
  919. if (!count($v)) {
  920. $input->$key = null;
  921. } elseif (!isset($input->$key)) {
  922. $input->$key = $v[0];
  923. } elseif (!in_array($input->$key,$v)) {
  924. $input->$key = null;
  925. }
  926. } else {
  927. $input->$key = $v;
  928. }
  929. }
  930. }
  931. }
  932. protected function applyInputSanitizer($callback,$action,$database,$table,&$input,$keys) {
  933. if (is_callable($callback,true)) foreach ((array)$input as $key=>$value) {
  934. if (isset($keys[$key])) {
  935. $input->$key = $callback($action,$database,$table,$key,$keys[$key]->type,$value);
  936. }
  937. }
  938. }
  939. protected function applyInputValidator($callback,$action,$database,$table,$input,$keys,$context) {
  940. $errors = array();
  941. if (is_callable($callback,true)) foreach ((array)$input as $key=>$value) {
  942. if (isset($keys[$key])) {
  943. $error = $callback($action,$database,$table,$key,$keys[$key]->type,$value,$context);
  944. if ($error!==true && $error!==null) $errors[$key] = $error;
  945. }
  946. }
  947. if (!empty($errors)) $this->exitWith422($errors);
  948. }
  949. protected function processTableAndIncludeParameters($database,$table,$include,$action) {
  950. $blacklist = array('information_schema','mysql','sys','pg_catalog');
  951. if (in_array(strtolower($database), $blacklist)) return array();
  952. $table_list = array();
  953. if ($result = $this->db->query($this->db->getSql('reflect_table'),array($table,$database))) {
  954. while ($row = $this->db->fetchRow($result)) $table_list[] = $row[0];
  955. $this->db->close($result);
  956. }
  957. if (empty($table_list)) $this->exitWith404('entity');
  958. if ($action=='list') {
  959. foreach (explode(',',$include) as $table) {
  960. if ($result = $this->db->query($this->db->getSql('reflect_table'),array($table,$database))) {
  961. while ($row = $this->db->fetchRow($result)) $table_list[] = $row[0];
  962. $this->db->close($result);
  963. }
  964. }
  965. }
  966. return $table_list;
  967. }
  968. protected function exitWith404($type) {
  969. if (isset($_SERVER['REQUEST_METHOD'])) {
  970. header('Content-Type:',true,404);
  971. die("Not found ($type)");
  972. } else {
  973. throw new \Exception("Not found ($type)");
  974. }
  975. }
  976. protected function exitWith422($object) {
  977. if (isset($_SERVER['REQUEST_METHOD'])) {
  978. header('Content-Type:',true,422);
  979. die(json_encode($object));
  980. } else {
  981. throw new \Exception(json_encode($object));
  982. }
  983. }
  984. protected function headersCommand($parameters) {
  985. $headers = array();
  986. $headers[]='Access-Control-Allow-Headers: Content-Type';
  987. $headers[]='Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST, DELETE';
  988. $headers[]='Access-Control-Allow-Credentials: true';
  989. $headers[]='Access-Control-Max-Age: 1728000';
  990. if (isset($_SERVER['REQUEST_METHOD'])) {
  991. foreach ($headers as $header) header($header);
  992. } else {
  993. echo json_encode($headers);
  994. }
  995. }
  996. protected function startOutput() {
  997. if (isset($_SERVER['REQUEST_METHOD'])) {
  998. header('Content-Type: application/json; charset=utf-8');
  999. }
  1000. }
  1001. protected function findPrimaryKeys($table,$database) {
  1002. $fields = array();
  1003. if ($result = $this->db->query($this->db->getSql('reflect_pk'),array($table,$database))) {
  1004. while ($row = $this->db->fetchRow($result)) {
  1005. $fields[] = $row[0];
  1006. }
  1007. $this->db->close($result);
  1008. }
  1009. return $fields;
  1010. }
  1011. protected function processKeyParameter($key,$tables,$database) {
  1012. if (!$key) return false;
  1013. $fields = $this->findPrimaryKeys($tables[0],$database);
  1014. if (count($fields)!=1) $this->exitWith404('1pk');
  1015. return array($key,$fields[0]);
  1016. }
  1017. protected function processOrderParameter($order) {
  1018. if (!$order) return false;
  1019. $order = explode(',',$order,2);
  1020. if (count($order)<2) $order[1]='ASC';
  1021. if (!strlen($order[0])) return false;
  1022. $order[1] = strtoupper($order[1])=='DESC'?'DESC':'ASC';
  1023. return $order;
  1024. }
  1025. protected function convertFilter($field, $comparator, $value) {
  1026. $result = $this->db->convertFilter($field,$comparator,$value);
  1027. if ($result) return $result;
  1028. // default behavior
  1029. $comparator = strtolower($comparator);
  1030. if ($comparator[0]!='n') {
  1031. if (strlen($comparator)==2) {
  1032. switch ($comparator) {
  1033. case 'cs': return array('! LIKE ?',$field,'%'.$this->db->likeEscape($value).'%');
  1034. case 'sw': return array('! LIKE ?',$field,$this->db->likeEscape($value).'%');
  1035. case 'ew': return array('! LIKE ?',$field,'%'.$this->db->likeEscape($value));
  1036. case 'eq': return array('! = ?',$field,$value);
  1037. case 'lt': return array('! < ?',$field,$value);
  1038. case 'le': return array('! <= ?',$field,$value);
  1039. case 'ge': return array('! >= ?',$field,$value);
  1040. case 'gt': return array('! > ?',$field,$value);
  1041. case 'bt': $v = explode(',',$value); if (count($v)<2) return false;
  1042. return array('! BETWEEN ? AND ?',$field,$v[0],$v[1]);
  1043. case 'in': return array('! IN ?',$field,explode(',',$value));
  1044. case 'is': return array('! IS NULL',$field);
  1045. }
  1046. } else {
  1047. switch ($comparator) {
  1048. case 'sco': return array('ST_Contains(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1049. case 'scr': return array('ST_Crosses(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1050. case 'sdi': return array('ST_Disjoint(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1051. case 'seq': return array('ST_Equals(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1052. case 'sin': return array('ST_Intersects(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1053. case 'sov': return array('ST_Overlaps(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1054. case 'sto': return array('ST_Touches(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1055. case 'swi': return array('ST_Within(!,ST_GeomFromText(?))=TRUE',$field,$value);
  1056. case 'sic': return array('ST_IsClosed(!)=TRUE',$field);
  1057. case 'sis': return array('ST_IsSimple(!)=TRUE',$field);
  1058. case 'siv': return array('ST_IsValid(!)=TRUE',$field);
  1059. }
  1060. }
  1061. } else {
  1062. if (strlen($comparator)==2) {
  1063. switch ($comparator) {
  1064. case 'ne': return $this->convertFilter($field, 'neq', $value); // deprecated
  1065. case 'ni': return $this->convertFilter($field, 'nin', $value); // deprecated
  1066. case 'no': return $this->convertFilter($field, 'nis', $value); // deprecated
  1067. }
  1068. } elseif (strlen($comparator)==3) {
  1069. switch ($comparator) {
  1070. case 'ncs': return array('! NOT LIKE ?',$field,'%'.$this->db->likeEscape($value).'%');
  1071. case 'nsw': return array('! NOT LIKE ?',$field,$this->db->likeEscape($value).'%');
  1072. case 'new': return array('! NOT LIKE ?',$field,'%'.$this->db->likeEscape($value));
  1073. case 'neq': return array('! <> ?',$field,$value);
  1074. case 'nlt': return array('! >= ?',$field,$value);
  1075. case 'nle': return array('! > ?',$field,$value);
  1076. case 'nge': return array('! < ?',$field,$value);
  1077. case 'ngt': return array('! <= ?',$field,$value);
  1078. case 'nbt': $v = explode(',',$value); if (count($v)<2) return false;
  1079. return array('! NOT BETWEEN ? AND ?',$field,$v[0],$v[1]);
  1080. case 'nin': return array('! NOT IN ?',$field,explode(',',$value));
  1081. case 'nis': return array('! IS NOT NULL',$field);
  1082. }
  1083. } else {
  1084. switch ($comparator) {
  1085. case 'nsco': return array('ST_Contains(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1086. case 'nscr': return array('ST_Crosses(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1087. case 'nsdi': return array('ST_Disjoint(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1088. case 'nseq': return array('ST_Equals(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1089. case 'nsin': return array('ST_Intersects(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1090. case 'nsov': return array('ST_Overlaps(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1091. case 'nsto': return array('ST_Touches(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1092. case 'nswi': return array('ST_Within(!,ST_GeomFromText(?))=FALSE',$field,$value);
  1093. case 'nsic': return array('ST_IsClosed(!)=FALSE',$field);
  1094. case 'nsis': return array('ST_IsSimple(!)=FALSE',$field);
  1095. case 'nsiv': return array('ST_IsValid(!)=FALSE',$field);
  1096. }
  1097. }
  1098. }
  1099. return false;
  1100. }
  1101. public function addFilter(&$filters,$table,$and,$field,$comparator,$value) {
  1102. if (!isset($filters[$table])) $filters[$table] = array();
  1103. if (!isset($filters[$table][$and])) $filters[$table][$and] = array();
  1104. $filter = $this->convertFilter($field,$comparator,$value);
  1105. if ($filter) $filters[$table][$and][] = $filter;
  1106. }
  1107. public function addFilters(&$filters,$table,$satisfy,$filterStrings) {
  1108. if ($filterStrings) {
  1109. for ($i=0;$i<count($filterStrings);$i++) {
  1110. $parts = explode(',',$filterStrings[$i],3);
  1111. if (count($parts)>=2) {
  1112. if (strpos($parts[0],'.')) list($t,$f) = explode('.',$parts[0],2);
  1113. else list($t,$f) = array($table,$parts[0]);
  1114. $comparator = $parts[1];
  1115. $value = isset($parts[2])?$parts[2]:null;
  1116. $and = isset($satisfy[$t])?$satisfy[$t]:'and';
  1117. $this->addFilter($filters,$t,$and,$f,$comparator,$value);
  1118. }
  1119. }
  1120. }
  1121. }
  1122. protected function processSatisfyParameter($tables,$satisfyString) {
  1123. $satisfy = array();
  1124. foreach (explode(',',$satisfyString) as $str) {
  1125. if (strpos($str,'.')) list($t,$s) = explode('.',$str,2);
  1126. else list($t,$s) = array($tables[0],$str);
  1127. $and = ($s && strtolower($s)=='any')?'or':'and';
  1128. $satisfy[$t] = $and;
  1129. }
  1130. return $satisfy;
  1131. }
  1132. protected function processFiltersParameter($tables,$satisfy,$filterStrings) {
  1133. $filters = array();
  1134. $this->addFilters($filters,$tables[0],$satisfy,$filterStrings);
  1135. return $filters;
  1136. }
  1137. protected function processPageParameter($page) {
  1138. if (!$page) return false;
  1139. $page = explode(',',$page,2);
  1140. if (count($page)<2) $page[1]=20;
  1141. $page[0] = ($page[0]-1)*$page[1];
  1142. return $page;
  1143. }
  1144. protected function retrieveObject($key,$fields,$filters,$tables) {
  1145. if (!$key) return false;
  1146. $table = $tables[0];
  1147. $params = array();
  1148. $sql = 'SELECT ';
  1149. $this->convertOutputs($sql,$params,$fields[$table]);
  1150. $sql .= ' FROM !';
  1151. $params[] = $table;
  1152. $this->addFilter($filters,$table,'and',$key[1],'eq',$key[0]);
  1153. $this->addWhereFromFilters($filters[$table],$sql,$params);
  1154. $object = null;
  1155. if ($result = $this->db->query($sql,$params)) {
  1156. $object = $this->db->fetchAssoc($result,$fields[$table]);
  1157. $this->db->close($result);
  1158. }
  1159. return $object;
  1160. }
  1161. protected function retrieveObjects($key,$fields,$filters,$tables) {
  1162. $keyField = $key[1];
  1163. $keys = explode(',',$key[0]);
  1164. $rows = array();
  1165. foreach ($keys as $key) {
  1166. $result = $this->retrieveObject(array($key,$keyField),$fields,$filters,$tables);
  1167. if ($result===null) {
  1168. return null;
  1169. }
  1170. $rows[] = $result;
  1171. }
  1172. return $rows;
  1173. }
  1174. protected function createObject($input,$tables) {
  1175. if (!$input) return false;
  1176. $input = (array)$input;
  1177. $keys = implode(',',str_split(str_repeat('!', count($input))));
  1178. $values = implode(',',str_split(str_repeat('?', count($input))));
  1179. $params = array_merge(array_keys($input),array_values($input));
  1180. array_unshift($params, $tables[0]);
  1181. $result = $this->db->query('INSERT INTO ! ('.$keys.') VALUES ('.$values.')',$params);
  1182. if (!$result) return null;
  1183. return $this->db->insertId($result);
  1184. }
  1185. protected function createObjects($inputs,$tables) {
  1186. if (!$inputs) return false;
  1187. $ids = array();
  1188. $this->db->beginTransaction();
  1189. foreach ($inputs as $input) {
  1190. $result = $this->createObject($input,$tables);
  1191. if ($result===null) {
  1192. $this->db->rollbackTransaction();
  1193. return null;
  1194. }
  1195. $ids[] = $result;
  1196. }
  1197. $this->db->commitTransaction();
  1198. return $ids;
  1199. }
  1200. protected function updateObject($key,$input,$filters,$tables) {
  1201. if (!$input) return false;
  1202. $input = (array)$input;
  1203. $table = $tables[0];
  1204. $sql = 'UPDATE ! SET ';
  1205. $params = array($table);
  1206. foreach (array_keys($input) as $j=>$k) {
  1207. if ($j) $sql .= ',';
  1208. $v = $input[$k];
  1209. $sql .= '!=?';
  1210. $params[] = $k;
  1211. $params[] = $v;
  1212. }
  1213. $this->addFilter($filters,$table,'and',$key[1],'eq',$key[0]);
  1214. $this->addWhereFromFilters($filters[$table],$sql,$params);
  1215. $result = $this->db->query($sql,$params);
  1216. if (!$result) return null;
  1217. return $this->db->affectedRows($result);
  1218. }
  1219. protected function updateObjects($key,$inputs,$filters,$tables) {
  1220. if (!$inputs) return false;
  1221. $keyField = $key[1];
  1222. $keys = explode(',',$key[0]);
  1223. if (count($inputs)!=count($keys)) {
  1224. $this->exitWith404('subject');
  1225. }
  1226. $rows = array();
  1227. $this->db->beginTransaction();
  1228. foreach ($inputs as $i=>$input) {
  1229. $result = $this->updateObject(array($keys[$i],$keyField),$input,$filters,$tables);
  1230. if ($result===null) {
  1231. $this->db->rollbackTransaction();
  1232. return null;
  1233. }
  1234. $rows[] = $result;
  1235. }
  1236. $this->db->commitTransaction();
  1237. return $rows;
  1238. }
  1239. protected function deleteObject($key,$filters,$tables) {
  1240. $table = $tables[0];
  1241. $sql = 'DELETE FROM !';
  1242. $params = array($table);
  1243. $this->addFilter($filters,$table,'and',$key[1],'eq',$key[0]);
  1244. $this->addWhereFromFilters($filters[$table],$sql,$params);
  1245. $result = $this->db->query($sql,$params);
  1246. if (!$result) return null;
  1247. return $this->db->affectedRows($result);
  1248. }
  1249. protected function deleteObjects($key,$filters,$tables) {
  1250. $keyField = $key[1];
  1251. $keys = explode(',',$key[0]);
  1252. $rows = array();
  1253. $this->db->beginTransaction();
  1254. foreach ($keys as $key) {
  1255. $result = $this->deleteObject(array($key,$keyField),$filters,$tables);
  1256. if ($result===null) {
  1257. $this->db->rollbackTransaction();
  1258. return null;
  1259. }
  1260. $rows[] = $result;
  1261. }
  1262. $this->db->commitTransaction();
  1263. return $rows;
  1264. }
  1265. protected function findRelations($tables,$database,$auto_include) {
  1266. $tableset = array();
  1267. $collect = array();
  1268. $select = array();
  1269. while (count($tables)>1) {
  1270. $table0 = array_shift($tables);
  1271. $tableset[] = $table0;
  1272. $result = $this->db->query($this->db->getSql('reflect_belongs_to'),array($table0,$tables,$database,$database));
  1273. while ($row = $this->db->fetchRow($result)) {
  1274. if (!$auto_include && !in_array($row[0],array_merge($tables,$tableset))) continue;
  1275. $collect[$row[0]][$row[1]]=array();
  1276. $select[$row[2]][$row[3]]=array($row[0],$row[1]);
  1277. if (!in_array($row[0],$tableset)) $tableset[] = $row[0];
  1278. }
  1279. $result = $this->db->query($this->db->getSql('reflect_has_many'),array($tables,$table0,$database,$database));
  1280. while ($row = $this->db->fetchRow($result)) {
  1281. if (!$auto_include && !in_array($row[2],array_merge($tables,$tableset))) continue;
  1282. $collect[$row[2]][$row[3]]=array();
  1283. $select[$row[0]][$row[1]]=array($row[2],$row[3]);
  1284. if (!in_array($row[2],$tableset)) $tableset[] = $row[2];
  1285. }
  1286. $result = $this->db->query($this->db->getSql('reflect_habtm'),array($database,$database,$database,$database,$table0,$tables));
  1287. while ($row = $this->db->fetchRow($result)) {
  1288. if (!$auto_include && !in_array($row[2],array_merge($tables,$tableset))) continue;
  1289. if (!$auto_include && !in_array($row[4],array_merge($tables,$tableset))) continue;
  1290. $collect[$row[2]][$row[3]]=array();
  1291. $select[$row[0]][$row[1]]=array($row[2],$row[3]);
  1292. $collect[$row[4]][$row[5]]=array();
  1293. $select[$row[6]][$row[7]]=array($row[4],$row[5]);
  1294. if (!in_array($row[2],$tableset)) $tableset[] = $row[2];
  1295. if (!in_array($row[4],$tableset)) $tableset[] = $row[4];
  1296. }
  1297. }
  1298. $tableset[] = array_shift($tables);
  1299. $tableset = array_unique($tableset);
  1300. return array($tableset,$collect,$select);
  1301. }
  1302. protected function retrieveInputs($data) {
  1303. $input = (object)array();
  1304. if (strlen($data)>0) {
  1305. if ($data[0]=='{' || $data[0]=='[') {
  1306. $input = json_decode($data);
  1307. } else {
  1308. parse_str($data, $input);
  1309. foreach ($input as $key => $value) {
  1310. if (substr($key,-9)=='__is_null') {
  1311. $input[substr($key,0,-9)] = null;
  1312. unset($input[$key]);
  1313. }
  1314. }
  1315. $input = (object)$input;
  1316. }
  1317. }
  1318. return is_array($input)?$input:array($input);
  1319. }
  1320. protected function addRelationColumns($columns,$select) {
  1321. if ($columns) {
  1322. foreach ($select as $table=>$keys) {
  1323. foreach ($keys as $key=>$other) {
  1324. $columns.=",$table.$key,".implode('.',$other);
  1325. }
  1326. }
  1327. }
  1328. return $columns;
  1329. }
  1330. protected function findFields($tables,$columns,$database) {
  1331. $fields = array();
  1332. foreach ($tables as $i=>$table) {
  1333. $fields[$table] = $this->findTableFields($table,$database);
  1334. $fields[$table] = $this->filterFieldsByColumns($fields[$table],$columns,$i==0,$table);
  1335. }
  1336. return $fields;
  1337. }
  1338. protected function filterFieldsByColumns($fields,$columns,$first,$table) {
  1339. if ($columns) {
  1340. $columns = explode(',',$columns);
  1341. foreach (array_keys($fields) as $key) {
  1342. $delete = true;
  1343. foreach ($columns as $column) {
  1344. if (strpos($column,'.')) {
  1345. if ($column=="$table.$key" || $column=="$table.*") {
  1346. $delete = false;
  1347. }
  1348. } elseif ($first) {
  1349. if ($column==$key || $column=="*") {
  1350. $delete = false;
  1351. }
  1352. }
  1353. }
  1354. if ($delete) unset($fields[$key]);
  1355. }
  1356. }
  1357. return $fields;
  1358. }
  1359. protected function findTableFields($table,$database) {
  1360. $fields = array();
  1361. foreach ($this->db->fetchFields($table) as $field) {
  1362. $fields[$field->name] = $field;
  1363. }
  1364. return $fields;
  1365. }
  1366. protected function filterInputByFields($input,$fields) {
  1367. if ($fields) foreach (array_keys((array)$input) as $key) {
  1368. if (!isset($fields[$key])) {
  1369. unset($input->$key);
  1370. }
  1371. }
  1372. return $input;
  1373. }
  1374. protected function convertInputs(&$input,$fields) {
  1375. foreach ($fields as $key=>$field) {
  1376. if (isset($input->$key) && $input->$key && $this->db->isBinaryType($field)) {
  1377. $value = $input->$key;
  1378. $value = str_pad(strtr($value, '-_', '+/'), ceil(strlen($value) / 4) * 4, '=', STR_PAD_RIGHT);
  1379. $input->$key = (object)array('type'=>'base64','value'=>$value);
  1380. }
  1381. if (isset($input->$key) && $input->$key && $this->db->isGeometryType($field)) {
  1382. $input->$key = (object)array('type'=>'wkt','value'=>$input->$key);
  1383. }
  1384. }
  1385. }
  1386. protected function convertOutputs(&$sql, &$params, $fields) {
  1387. $sql .= implode(',',str_split(str_repeat('!',count($fields))));
  1388. foreach ($fields as $key=>$field) {
  1389. if ($this->db->isBinaryType($field)) {
  1390. $params[] = (object)array('type'=>'base64','key'=>$key);
  1391. }
  1392. else if ($this->db->isGeometryType($field)) {
  1393. $params[] = (object)array('type'=>'wkt','key'=>$key);
  1394. }
  1395. else {
  1396. $params[] = $key;
  1397. }
  1398. }
  1399. }
  1400. protected function getParameters($settings) {
  1401. extract($settings);
  1402. $table = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_');
  1403. $key = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_,'); // auto-increment or uuid
  1404. $action = $this->mapMethodToAction($method,$key);
  1405. $include = $this->parseGetParameter($get, 'include', 'a-zA-Z0-9\-_,');
  1406. $page = $this->parseGetParameter($get, 'page', '0-9,');
  1407. $filters = $this->parseGetParameterArray($get, 'filter', false);
  1408. $satisfy = $this->parseGetParameter($get, 'satisfy', 'a-zA-Z0-9\-_,.');
  1409. $columns = $this->parseGetParameter($get, 'columns', 'a-zA-Z0-9\-_,.*');
  1410. $order = $this->parseGetParameter($get, 'order', 'a-zA-Z0-9\-_,');
  1411. $transform = $this->parseGetParameter($get, 'transform', 't1');
  1412. $tables = $this->processTableAndIncludeParameters($database,$table,$include,$action);
  1413. $key = $this->processKeyParameter($key,$tables,$database);
  1414. $satisfy = $this->processSatisfyParameter($tables,$satisfy);
  1415. $filters = $this->processFiltersParameter($tables,$satisfy,$filters);
  1416. $page = $this->processPageParameter($page);
  1417. $order = $this->processOrderParameter($order);
  1418. // reflection
  1419. list($tables,$collect,$select) = $this->findRelations($tables,$database,$auto_include);
  1420. $columns = $this->addRelationColumns($columns,$select);
  1421. $fields = $this->findFields($tables,$columns,$database);
  1422. // permissions
  1423. if ($table_authorizer) $this->applyTableAuthorizer($table_authorizer,$action,$database,$tables);
  1424. if (!isset($tables[0])) $this->exitWith404('entity');
  1425. if ($record_filter) $this->applyRecordFilter($record_filter,$action,$database,$tables,$filters);
  1426. if ($tenancy_function) $this->applyTenancyFunction($tenancy_function,$action,$database,$fields,$filters);
  1427. if ($column_authorizer) $this->applyColumnAuthorizer($column_authorizer,$action,$database,$fields);
  1428. $multi = strpos($key[0],',')!==false;
  1429. if (strlen($post)) {
  1430. // input
  1431. $multi = $post[0]=='[';
  1432. $contexts = $this->retrieveInputs($post);
  1433. $inputs = array();
  1434. foreach ($contexts as $context) {
  1435. $input = $this->filterInputByFields($context,$fields[$tables[0]]);
  1436. if ($tenancy_function) $this->applyInputTenancy($tenancy_function,$action,$database,$tables[0],$input,$fields[$tables[0]]);
  1437. if ($input_sanitizer) $this->applyInputSanitizer($input_sanitizer,$action,$database,$tables[0],$input,$fields[$tables[0]]);
  1438. if ($input_validator) $this->applyInputValidator($input_validator,$action,$database,$tables[0],$input,$fields[$tables[0]],$context);
  1439. $this->convertInputs($input,$fields[$tables[0]]);
  1440. $inputs[] = $input;
  1441. }
  1442. }
  1443. return compact('action','database','tables','key','page','filters','fields','order','transform','multi','inputs','collect','select');
  1444. }
  1445. protected function addWhereFromFilters($filters,&$sql,&$params) {
  1446. $first = true;
  1447. if (isset($filters['or'])) {
  1448. $first = false;
  1449. $sql .= ' WHERE (';
  1450. foreach ($filters['or'] as $i=>$filter) {
  1451. $sql .= $i==0?'':' OR ';
  1452. $sql .= $filter[0];
  1453. for ($i=1;$i<count($filter);$i++) {
  1454. $params[] = $filter[$i];
  1455. }
  1456. }
  1457. $sql .= ')';
  1458. }
  1459. if (isset($filters['and'])) {
  1460. foreach ($filters['and'] as $i=>$filter) {
  1461. $sql .= $first?' WHERE ':' AND ';
  1462. $sql .= $filter[0];
  1463. for ($i=1;$i<count($filter);$i++) {
  1464. $params[] = $filter[$i];
  1465. }
  1466. $first = false;
  1467. }
  1468. }
  1469. }
  1470. protected function listCommandInternal($parameters) {
  1471. extract($parameters);
  1472. echo '{';
  1473. $table = array_shift($tables);
  1474. // first table
  1475. $count = false;
  1476. echo '"'.$table.'":{';
  1477. if (is_array($order) && is_array($page)) {
  1478. $params = array();
  1479. $sql = 'SELECT COUNT(*) FROM !';
  1480. $params[] = $table;
  1481. if (isset($filters[$table])) {
  1482. $this->addWhereFromFilters($filters[$table],$sql,$params);
  1483. }
  1484. if ($result = $this->db->query($sql,$params)) {
  1485. while ($pages = $this->db->fetchRow($result)) {
  1486. $count = $pages[0];
  1487. }
  1488. }
  1489. }
  1490. $params = array();
  1491. $sql = 'SELECT ';
  1492. $this->convertOutputs($sql,$params,$fields[$table]);
  1493. $sql .= ' FROM !';
  1494. $params[] = $table;
  1495. if (isset($filters[$table])) {
  1496. $this->addWhereFromFilters($filters[$table],$sql,$params);
  1497. }
  1498. if (is_array($order)) {
  1499. $sql .= ' ORDER BY ! '.$order[1];
  1500. $params[] = $order[0];
  1501. }
  1502. if (is_array($order) && is_array($page)) {
  1503. $sql = $this->db->addLimitToSql($sql,$page[1],$page[0]);
  1504. }
  1505. if ($result = $this->db->query($sql,$params)) {
  1506. echo '"columns":';
  1507. $keys = array_keys($fields[$table]);
  1508. echo json_encode($keys);
  1509. $keys = array_flip($keys);
  1510. echo ',"records":[';
  1511. $first_row = true;
  1512. while ($row = $this->db->fetchRow($result,$fields[$table])) {
  1513. if ($first_row) $first_row = false;
  1514. else echo ',';
  1515. if (isset($collect[$table])) {
  1516. foreach (array_keys($collect[$table]) as $field) {
  1517. $collect[$table][$field][] = $row[$keys[$field]];
  1518. }
  1519. }
  1520. echo json_encode($row);
  1521. }
  1522. $this->db->close($result);
  1523. echo ']';
  1524. if ($count) echo ',';
  1525. }
  1526. if ($count) echo '"results":'.$count;
  1527. echo '}';
  1528. // other tables
  1529. foreach ($tables as $t=>$table) {
  1530. echo ',';
  1531. echo '"'.$table.'":{';
  1532. $params = array();
  1533. $sql = 'SELECT ';
  1534. $this->convertOutputs($sql,$params,$fields[$table]);
  1535. $sql .= ' FROM !';
  1536. $params[] = $table;
  1537. if (isset($select[$table])) {
  1538. echo '"relations":{';
  1539. $first_row = true;
  1540. foreach ($select[$table] as $field => $path) {
  1541. $values = $collect[$path[0]][$path[1]];
  1542. if ($values) {
  1543. $this->addFilter($filters,$table,'and',$field,'in',implode(',',$values));
  1544. }
  1545. if ($first_row) $first_row = false;
  1546. else echo ',';
  1547. echo '"'.$field.'":"'.implode('.',$path).'"';
  1548. }
  1549. echo '}';
  1550. }
  1551. if (isset($filters[$table])) {
  1552. $this->addWhereFromFilters($filters[$table],$sql,$params);
  1553. }
  1554. if ($result = $this->db->query($sql,$params)) {
  1555. if (isset($select[$table])) echo ',';
  1556. echo '"columns":';
  1557. $keys = array_keys($fields[$table]);
  1558. echo json_encode($keys);
  1559. $keys = array_flip($keys);
  1560. echo ',"records":[';
  1561. $first_row = true;
  1562. while ($row = $this->db->fetchRow($result,$fields[$table])) {
  1563. if ($first_row) $first_row = false;
  1564. else echo ',';
  1565. if (isset($collect[$table])) {
  1566. foreach (array_keys($collect[$table]) as $field) {
  1567. $collect[$table][$field][]=$row[$keys[$field]];
  1568. }
  1569. }
  1570. echo json_encode($row);
  1571. }
  1572. $this->db->close($result);
  1573. echo ']';
  1574. }
  1575. echo '}';
  1576. }
  1577. echo '}';
  1578. }
  1579. protected function readCommand($parameters) {
  1580. extract($parameters);
  1581. if ($multi) $object = $this->retrieveObjects($key,$fields,$filters,$tables);
  1582. else $object = $this->retrieveObject($key,$fields,$filters,$tables);
  1583. if (!$object) $this->exitWith404('object');
  1584. $this->startOutput();
  1585. echo json_encode($object);
  1586. }
  1587. protected function createCommand($parameters) {
  1588. extract($parameters);
  1589. if (!$inputs || !$inputs[0]) $this->exitWith404('input');
  1590. $this->startOutput();
  1591. if ($multi) echo json_encode($this->createObjects($inputs,$tables));
  1592. else echo json_encode($this->createObject($inputs[0],$tables));
  1593. }
  1594. protected function updateCommand($parameters) {
  1595. extract($parameters);
  1596. if (!$inputs || !$inputs[0]) $this->exitWith404('subject');
  1597. $this->startOutput();
  1598. if ($multi) echo json_encode($this->updateObjects($key,$inputs,$filters,$tables));
  1599. else echo json_encode($this->updateObject($key,$inputs[0],$filters,$tables));
  1600. }
  1601. protected function deleteCommand($parameters) {
  1602. extract($parameters);
  1603. $this->startOutput();
  1604. if ($multi) echo json_encode($this->deleteObjects($key,$filters,$tables));
  1605. else echo json_encode($this->deleteObject($key,$filters,$tables));
  1606. }
  1607. protected function listCommand($parameters) {
  1608. extract($parameters);
  1609. $this->startOutput();
  1610. if ($transform) {
  1611. ob_start();
  1612. }
  1613. $this->listCommandInternal($parameters);
  1614. if ($transform) {
  1615. $content = ob_get_contents();
  1616. ob_end_clean();
  1617. $data = json_decode($content,true);
  1618. echo json_encode(self::php_crud_api_transform($data));
  1619. }
  1620. }
  1621. protected function retrievePostData() {
  1622. if ($_FILES) {
  1623. $files = array();
  1624. foreach ($_FILES as $name => $file) {
  1625. foreach ($file as $key => $value) {
  1626. switch ($key) {
  1627. case 'tmp_name': $files[$name] = $value?base64_encode(file_get_contents($value)):''; break;
  1628. default: $files[$name.'_'.$key] = $value;
  1629. }
  1630. }
  1631. }
  1632. return http_build_query(array_merge($files,$_POST));
  1633. }
  1634. return file_get_contents('php://input');
  1635. }
  1636. public function __construct($config) {
  1637. extract($config);
  1638. // initialize
  1639. $dbengine = isset($dbengine)?$dbengine:null;
  1640. $hostname = isset($hostname)?$hostname:null;
  1641. $username = isset($username)?$username:null;
  1642. $password = isset($password)?$password:null;
  1643. $database = isset($database)?$database:null;
  1644. $port = isset($port)?$port:null;
  1645. $socket = isset($socket)?$socket:null;
  1646. $charset = isset($charset)?$charset:null;
  1647. $table_authorizer = isset($table_authorizer)?$table_authorizer:null;
  1648. $record_filter = isset($record_filter)?$record_filter:null;
  1649. $column_authorizer = isset($column_authorizer)?$column_authorizer:null;
  1650. $tenancy_function = isset($tenancy_function)?$tenancy_function:null;
  1651. $input_sanitizer = isset($input_sanitizer)?$input_sanitizer:null;
  1652. $input_validator = isset($input_validator)?$input_validator:null;
  1653. $extensions = isset($extensions)?$extensions:null;
  1654. $auto_include = isset($auto_include)?$auto_include:null;
  1655. $allow_origin = isset($allow_origin)?$allow_origin:null;
  1656. $db = isset($db)?$db:null;
  1657. $method = isset($method)?$method:null;
  1658. $request = isset($request)?$request:null;
  1659. $get = isset($get)?$get:null;
  1660. $post = isset($post)?$post:null;
  1661. $origin = isset($origin)?$origin:null;
  1662. // defaults
  1663. if (!$dbengine) {
  1664. $dbengine = 'MySQL';
  1665. }
  1666. if (!$method) {
  1667. $method = $_SERVER['REQUEST_METHOD'];
  1668. }
  1669. if (!$request) {
  1670. $request = isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:'';
  1671. if (!$request) {
  1672. $request = isset($_SERVER['ORIG_PATH_INFO'])?$_SERVER['ORIG_PATH_INFO']:'';
  1673. }
  1674. }
  1675. if (!$get) {
  1676. $get = $_GET;
  1677. }
  1678. if (!$post) {
  1679. $post = $this->retrievePostData();
  1680. }
  1681. if (!$origin) {
  1682. $origin = isset($_SERVER['HTTP_ORIGIN'])?$_SERVER['HTTP_ORIGIN']:'';
  1683. }
  1684. // connect
  1685. $request = trim($request,'/');
  1686. if (!$database) {
  1687. $database = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_');
  1688. }
  1689. if (!$db) {
  1690. $db = new $dbengine();
  1691. if (!$charset) {
  1692. $charset = $db->getDefaultCharset();
  1693. }
  1694. $db->connect($hostname,$username,$password,$database,$port,$socket,$charset);
  1695. }
  1696. if ($extensions===null) {
  1697. $extensions = true;
  1698. }
  1699. if ($auto_include===null) {
  1700. $auto_include = true;
  1701. }
  1702. if ($allow_origin===null) {
  1703. $allow_origin = '*';
  1704. }
  1705. $this->db = $db;
  1706. $this->settings = compact('method', 'request', 'get', 'post', 'origin', 'database', 'table_authorizer', 'record_filter', 'column_authorizer', 'tenancy_function', 'input_sanitizer', 'input_validator', 'extensions', 'auto_include', 'allow_origin');
  1707. }
  1708. public static function php_crud_api_transform(&$tables) {
  1709. $get_objects = function (&$tables,$table_name,$where_index=false,$match_value=false) use (&$get_objects) {
  1710. $objects = array();
  1711. if (isset($tables[$table_name]['records'])) {
  1712. foreach ($tables[$table_name]['records'] as $record) {
  1713. if ($where_index===false || $record[$where_index]==$match_value) {
  1714. $object = array();
  1715. foreach ($tables[$table_name]['columns'] as $index=>$column) {
  1716. $object[$column] = $record[$index];
  1717. foreach ($tables as $relation=>$reltable) {
  1718. if (isset($reltable['relations'])) {
  1719. foreach ($reltable['relations'] as $key=>$target) {
  1720. if ($target == "$table_name.$column") {
  1721. $column_indices = array_flip($reltable['columns']);
  1722. $object[$relation] = $get_objects($tables,$relation,$column_indices[$key],$record[$index]);
  1723. }
  1724. }
  1725. }
  1726. }
  1727. }
  1728. $objects[] = $object;
  1729. }
  1730. }
  1731. }
  1732. return $objects;
  1733. };
  1734. $tree = array();
  1735. foreach ($tables as $name=>$table) {
  1736. if (!isset($table['relations'])) {
  1737. $tree[$name] = $get_objects($tables,$name);
  1738. if (isset($table['results'])) {
  1739. $tree['_results'] = $table['results'];
  1740. }
  1741. }
  1742. }
  1743. return $tree;
  1744. }
  1745. protected function swagger($settings) {
  1746. extract($settings);
  1747. $tables = array();
  1748. if ($result = $this->db->query($this->db->getSql('list_tables'),array($database))) {
  1749. while ($row = $this->db->fetchRow($result)) {
  1750. $table = array(
  1751. 'name'=>$row[0],
  1752. 'comments'=>$row[1],
  1753. 'root_actions'=>array(
  1754. array('name'=>'list','method'=>'get'),
  1755. array('name'=>'create','method'=>'post'),
  1756. ),
  1757. 'id_actions'=>array(
  1758. array('name'=>'read','method'=>'get'),
  1759. array('name'=>'update','method'=>'put'),
  1760. array('name'=>'delete','method'=>'delete'),
  1761. ),
  1762. );
  1763. $tables[] = $table;
  1764. }
  1765. $this->db->close($result);
  1766. }
  1767. foreach ($tables as $t=>$table) {
  1768. $table_list = array($table['name']);
  1769. $table_fields = $this->findFields($table_list,false,$database);
  1770. $table_names = array_map(function($v){ return $v['name'];},$tables);
  1771. if ($extensions) {
  1772. $result = $this->db->query($this->db->getSql('reflect_belongs_to'),array($table_list[0],$table_names,$database,$database));
  1773. while ($row = $this->db->fetchRow($result)) {
  1774. $table_fields[$table['name']][$row[1]]->references=array($row[2],$row[3]);
  1775. }
  1776. $result = $this->db->query($this->db->getSql('reflect_has_many'),array($table_names,$table_list[0],$database,$database));
  1777. while ($row = $this->db->fetchRow($result)) {
  1778. $table_fields[$table['name']][$row[3]]->referenced[]=array($row[0],$row[1]);
  1779. }
  1780. $primaryKeys = $this->findPrimaryKeys($table_list[0],$database);
  1781. foreach ($primaryKeys as $primaryKey) {
  1782. $table_fields[$table['name']][$primaryKey]->primaryKey = true;
  1783. }
  1784. }
  1785. foreach (array('root_actions','id_actions') as $path) {
  1786. foreach ($table[$path] as $i=>$action) {
  1787. $table_list = array($table['name']);
  1788. $fields = $table_fields;
  1789. if ($table_authorizer) $this->applyTableAuthorizer($table_authorizer,$action['name'],$database,$table_list);
  1790. if ($column_authorizer) $this->applyColumnAuthorizer($column_authorizer,$action['name'],$database,$fields);
  1791. if (!$table_list || !$fields[$table['name']]) $tables[$t][$path][$i] = false;
  1792. else $tables[$t][$path][$i]['fields'] = $fields[$table['name']];
  1793. }
  1794. // remove unauthorized tables and tables without fields
  1795. $tables[$t][$path] = array_values(array_filter($tables[$t][$path]));
  1796. }
  1797. if (!$tables[$t]['root_actions']&&!$tables[$t]['id_actions']) $tables[$t] = false;
  1798. }
  1799. $tables = array_merge(array_filter($tables));
  1800. //var_dump($tables);die();
  1801. header('Content-Type: application/json; charset=utf-8');
  1802. echo '{"swagger":"2.0",';
  1803. echo '"info":{';
  1804. echo '"title":"'.$database.'",';
  1805. echo '"description":"API generated with [PHP-CRUD-API](https://github.com/mevdschee/php-crud-api)",';
  1806. echo '"version":"1.0.0"';
  1807. echo '},';
  1808. echo '"host":"'.$_SERVER['HTTP_HOST'].'",';
  1809. echo '"basePath":"'.$_SERVER['SCRIPT_NAME'].'",';
  1810. echo '"schemes":["http'.((!empty($_SERVER['HTTPS'])&&$_SERVER['HTTPS']!=='off')?'s':'').'"],';
  1811. echo '"consumes":["application/json"],';
  1812. echo '"produces":["application/json"],';
  1813. echo '"tags":[';
  1814. foreach ($tables as $i=>$table) {
  1815. if ($i>0) echo ',';
  1816. echo '{';
  1817. echo '"name":"'.$table['name'].'",';
  1818. echo '"description":"'.$table['comments'].'"';
  1819. echo '}';
  1820. }
  1821. echo '],';
  1822. echo '"paths":{';
  1823. foreach ($tables as $i=>$table) {
  1824. if ($table['root_actions']) {
  1825. if ($i>0) echo ',';
  1826. echo '"/'.$table['name'].'":{';
  1827. foreach ($table['root_actions'] as $j=>$action) {
  1828. if ($j>0) echo ',';
  1829. echo '"'.$action['method'].'":{';
  1830. echo '"tags":["'.$table['name'].'"],';
  1831. echo '"summary":"'.ucfirst($action['name']).'",';
  1832. if ($action['name']=='list') {
  1833. echo '"parameters":[';
  1834. echo '{';
  1835. echo '"name":"include",';
  1836. echo '"in":"query",';
  1837. echo '"description":"One or more related entities (comma separated).",';
  1838. echo '"required":false,';
  1839. echo '"type":"string"';
  1840. echo '},';
  1841. echo '{';
  1842. echo '"name":"order",';
  1843. echo '"in":"query",';
  1844. echo '"description":"Column you want to sort on and the sort direction (comma separated). Example: id,desc",';
  1845. echo '"required":false,';
  1846. echo '"type":"string"';
  1847. echo '},';
  1848. echo '{';
  1849. echo '"name":"page",';
  1850. echo '"in":"query",';
  1851. echo '"description":"Page number and page size (comma separated). NB: You cannot use \"page\" without \"order\"! Example: 1,10",';
  1852. echo '"required":false,';
  1853. echo '"type":"string"';
  1854. echo '},';
  1855. echo '{';
  1856. echo '"name":"transform",';
  1857. echo '"in":"query",';
  1858. echo '"description":"Transform the records to object format. NB: This can also be done client-side in JavaScript!",';
  1859. echo '"required":false,';
  1860. echo '"type":"boolean"';
  1861. echo '},';
  1862. echo '{';
  1863. echo '"name":"columns",';
  1864. echo '"in":"query",';
  1865. echo '"description":"The table columns you want to retrieve (comma separated). Example: posts.*,categories.name",';
  1866. echo '"required":false,';
  1867. echo '"type":"string"';
  1868. echo '},';
  1869. echo '{';
  1870. echo '"name":"filter[]",';
  1871. echo '"in":"query",';
  1872. echo '"description":"Filters to be applied. Each filter consists of a column, an operator and a value (comma separated). Example: id,eq,1",';
  1873. echo '"required":false,';
  1874. echo '"type":"array",';
  1875. echo '"collectionFormat":"multi",';
  1876. echo '"items":{"type":"string"}';
  1877. echo '},';
  1878. echo '{';
  1879. echo '"name":"satisfy",';
  1880. echo '"in":"query",';
  1881. echo '"description":"Should all filters match (default)? Or any?",';
  1882. echo '"required":false,';
  1883. echo '"type":"string",';
  1884. echo '"enum":["any"]';
  1885. echo '}';
  1886. echo '],';
  1887. echo '"responses":{';
  1888. echo '"200":{';
  1889. echo '"description":"An array of '.$table['name'].'",';
  1890. echo '"schema":{';
  1891. echo '"type":"array",';
  1892. echo '"items":{';
  1893. echo '"type": "object",';
  1894. echo '"properties": {';
  1895. foreach (array_keys($action['fields']) as $k=>$field) {
  1896. if ($k>0) echo ',';
  1897. echo '"'.$field.'": {';
  1898. echo '"type": "string"';
  1899. if (isset($action['fields'][$field]->referenced)) {
  1900. echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
  1901. }
  1902. if (isset($action['fields'][$field]->references)) {
  1903. echo ',"x-references": '.json_encode($action['fields'][$field]->references);
  1904. }
  1905. if (isset($action['fields'][$field]->primaryKey)) {
  1906. echo ',"x-primary-key": true';
  1907. }
  1908. echo '}';
  1909. }
  1910. echo '}'; //properties
  1911. echo '}'; //items
  1912. echo '}'; //schema
  1913. echo '}'; //200
  1914. echo '}'; //responses
  1915. }
  1916. if ($action['name']=='create') {
  1917. echo '"parameters":[{';
  1918. echo '"name":"item",';
  1919. echo '"in":"body",';
  1920. echo '"description":"Item to create.",';
  1921. echo '"required":false,';
  1922. echo '"schema":{';
  1923. echo '"type": "object",';
  1924. echo '"properties": {';
  1925. foreach (array_keys($action['fields']) as $k=>$field) {
  1926. if ($k>0) echo ',';
  1927. echo '"'.$field.'": {';
  1928. echo '"type": "string"';
  1929. if (isset($action['fields'][$field]->referenced)) {
  1930. echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
  1931. }
  1932. if (isset($action['fields'][$field]->references)) {
  1933. echo ',"x-references": '.json_encode($action['fields'][$field]->references);
  1934. }
  1935. if (isset($action['fields'][$field]->primaryKey)) {
  1936. echo ',"x-primary-key": true';
  1937. }
  1938. echo '}';
  1939. }
  1940. echo '}'; //properties
  1941. echo '}'; //schema
  1942. echo '}],';
  1943. echo '"responses":{';
  1944. echo '"200":{';
  1945. echo '"description":"Identifier of created item.",';
  1946. echo '"schema":{';
  1947. echo '"type":"integer"';
  1948. echo '}';//schema
  1949. echo '}';//200
  1950. echo '}';//responses
  1951. }
  1952. echo '}';//method
  1953. }
  1954. echo '}';
  1955. }
  1956. if ($table['id_actions']) {
  1957. if ($i>0 || $table['root_actions']) echo ',';
  1958. echo '"/'.$table['name'].'/{id}":{';
  1959. foreach ($table['id_actions'] as $j=>$action) {
  1960. if ($j>0) echo ',';
  1961. echo '"'.$action['method'].'":{';
  1962. echo '"tags":["'.$table['name'].'"],';
  1963. echo '"summary":"'.ucfirst($action['name']).'",';
  1964. echo '"parameters":[';
  1965. echo '{';
  1966. echo '"name":"id",';
  1967. echo '"in":"path",';
  1968. echo '"description":"Identifier for item.",';
  1969. echo '"required":true,';
  1970. echo '"type":"string"';
  1971. echo '}';
  1972. if ($action['name']=='update') {
  1973. echo ',{';
  1974. echo '"name":"item",';
  1975. echo '"in":"body",';
  1976. echo '"description":"Properties of item to update.",';
  1977. echo '"required":false,';
  1978. echo '"schema":{';
  1979. echo '"type": "object",';
  1980. echo '"properties": {';
  1981. foreach (array_keys($action['fields']) as $k=>$field) {
  1982. if ($k>0) echo ',';
  1983. echo '"'.$field.'": {';
  1984. echo '"type": "string"';
  1985. if (isset($action['fields'][$field]->referenced)) {
  1986. echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
  1987. }
  1988. if (isset($action['fields'][$field]->references)) {
  1989. echo ',"x-references": '.json_encode($action['fields'][$field]->references);
  1990. }
  1991. if (isset($action['fields'][$field]->primaryKey)) {
  1992. echo ',"x-primary-key": true';
  1993. }
  1994. echo '}';
  1995. }
  1996. echo '}'; //properties
  1997. echo '}'; //schema
  1998. echo '}';
  1999. }
  2000. echo '],';
  2001. if ($action['name']=='read') {
  2002. echo '"responses":{';
  2003. echo '"200":{';
  2004. echo '"description":"The requested item.",';
  2005. echo '"schema":{';
  2006. echo '"type": "object",';
  2007. echo '"properties": {';
  2008. foreach (array_keys($action['fields']) as $k=>$field) {
  2009. if ($k>0) echo ',';
  2010. echo '"'.$field.'": {';
  2011. echo '"type": "string"';
  2012. if (isset($action['fields'][$field]->referenced)) {
  2013. echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
  2014. }
  2015. if (isset($action['fields'][$field]->references)) {
  2016. echo ',"x-references": '.json_encode($action['fields'][$field]->references);
  2017. }
  2018. if (isset($action['fields'][$field]->primaryKey)) {
  2019. echo ',"x-primary-key": true';
  2020. }
  2021. echo '}';
  2022. }
  2023. echo '}'; //properties
  2024. echo '}'; //schema
  2025. echo '}';
  2026. echo '}';
  2027. } else {
  2028. echo '"responses":{';
  2029. echo '"200":{';
  2030. echo '"description":"Number of affected rows.",';
  2031. echo '"schema":{';
  2032. echo '"type":"integer"';
  2033. echo '}';
  2034. echo '}';
  2035. echo '}';
  2036. }
  2037. echo '}';
  2038. }
  2039. echo '}';
  2040. }
  2041. }
  2042. echo '}';
  2043. echo '}';
  2044. }
  2045. protected function allowOrigin($origin,$allowOrigins) {
  2046. if ($allowOrigins=='*') {
  2047. header('Access-Control-Allow-Origin: *');
  2048. } else {
  2049. if ($origin) foreach (explode(',',$allowOrigins) as $o) {
  2050. if (preg_match('/^'.str_replace('\*','.*',preg_quote(strtolower(trim($o)))).'$/',$origin)) {
  2051. header('Access-Control-Allow-Origin: '.$origin);
  2052. break;
  2053. }
  2054. }
  2055. }
  2056. }
  2057. public function executeCommand() {
  2058. if (isset($_SERVER['REQUEST_METHOD'])) {
  2059. $this->allowOrigin($this->settings['origin'],$this->settings['allow_origin']);
  2060. }
  2061. if (!$this->settings['request']) {
  2062. $this->swagger($this->settings);
  2063. } else {
  2064. $parameters = $this->getParameters($this->settings);
  2065. switch($parameters['action']){
  2066. case 'list': $this->listCommand($parameters); break;
  2067. case 'read': $this->readCommand($parameters); break;
  2068. case 'create': $this->createCommand($parameters); break;
  2069. case 'update': $this->updateCommand($parameters); break;
  2070. case 'delete': $this->deleteCommand($parameters); break;
  2071. case 'headers': $this->headersCommand($parameters); break;
  2072. }
  2073. }
  2074. }
  2075. }
  2076. // require 'auth.php'; // from the PHP-API-AUTH project, see: https://github.com/mevdschee/php-api-auth
  2077. // uncomment the lines below for token+session based authentication (see "login_token.html" + "login_token.php"):
  2078. // $auth = new PHP_API_AUTH(array(
  2079. // 'secret'=>'someVeryLongPassPhraseChangeMe',
  2080. // ));
  2081. // if ($auth->executeCommand()) exit(0);
  2082. // if (empty($_SESSION['user']) || $_GET['csrf']!=$_SESSION['csrf']) {
  2083. // header('HTTP/1.0 401 Unauthorized');
  2084. // exit(0);
  2085. // }
  2086. // uncomment the lines below for form+session based authentication (see "login.html"):
  2087. // $auth = new PHP_API_AUTH(array(
  2088. // 'authenticator'=>function($user,$pass){ $_SESSION['user']=($user=='admin' && $pass=='admin'); }
  2089. // ));
  2090. // if ($auth->executeCommand()) exit(0);
  2091. // if (empty($_SESSION['user']) || $_GET['csrf']!=$_SESSION['csrf']) {
  2092. // header('HTTP/1.0 401 Unauthorized');
  2093. // exit(0);
  2094. // }
  2095. // uncomment the lines below when running in stand-alone mode:
  2096. // $api = new PHP_CRUD_API(array(
  2097. // 'dbengine'=>'MySQL',
  2098. // 'hostname'=>'localhost',
  2099. // 'username'=>'',
  2100. // 'password'=>'',
  2101. // 'database'=>'',
  2102. // 'charset'=>'utf8'
  2103. // ));
  2104. // $api->executeCommand();
  2105. // For Microsoft SQL Server 2012 use:
  2106. // $api = new PHP_CRUD_API(array(
  2107. // 'dbengine'=>'SQLServer',
  2108. // 'hostname'=>'(local)',
  2109. // 'username'=>'',
  2110. // 'password'=>'',
  2111. // 'database'=>'xxx',
  2112. // 'charset'=>'UTF-8'
  2113. // ));
  2114. // $api->executeCommand();
  2115. // For PostgreSQL 9 use:
  2116. // $api = new PHP_CRUD_API(array(
  2117. // 'dbengine'=>'PostgreSQL',
  2118. // 'hostname'=>'localhost',
  2119. // 'username'=>'xxx',
  2120. // 'password'=>'xxx',
  2121. // 'database'=>'xxx',
  2122. // 'charset'=>'UTF8'
  2123. // ));
  2124. // $api->executeCommand();
  2125. // For SQLite 3 use:
  2126. // $api = new PHP_CRUD_API(array(
  2127. // 'dbengine'=>'SQLite',
  2128. // 'database'=>'data/blog.db',
  2129. // ));
  2130. // $api->executeCommand();