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

api.php 60KB

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