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
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

api.php 74KB

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