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
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

api.php 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. <?php
  2. class MySQL_CRUD_API extends REST_CRUD_API {
  3. protected $queries = array(
  4. 'reflect_table'=>'SELECT "TABLE_NAME" FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_NAME" LIKE ? AND "TABLE_SCHEMA" = ?',
  5. 'reflect_pk'=>'SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "COLUMN_KEY" = \'PRI\' AND "TABLE_NAME" = ? AND "TABLE_SCHEMA" = ?',
  6. 'reflect_belongs_to'=>'SELECT
  7. "TABLE_NAME","COLUMN_NAME",
  8. "REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
  9. FROM
  10. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
  11. WHERE
  12. "TABLE_NAME" = ? AND
  13. "REFERENCED_TABLE_NAME" IN ? AND
  14. "TABLE_SCHEMA" = ? AND
  15. "REFERENCED_TABLE_SCHEMA" = ?',
  16. 'reflect_has_many'=>'SELECT
  17. "TABLE_NAME","COLUMN_NAME",
  18. "REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
  19. FROM
  20. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
  21. WHERE
  22. "TABLE_NAME" IN ? AND
  23. "REFERENCED_TABLE_NAME" = ? AND
  24. "TABLE_SCHEMA" = ? AND
  25. "REFERENCED_TABLE_SCHEMA" = ?',
  26. 'reflect_habtm'=>'SELECT
  27. k1."TABLE_NAME", k1."COLUMN_NAME",
  28. k1."REFERENCED_TABLE_NAME", k1."REFERENCED_COLUMN_NAME",
  29. k2."TABLE_NAME", k2."COLUMN_NAME",
  30. k2."REFERENCED_TABLE_NAME", k2."REFERENCED_COLUMN_NAME"
  31. FROM
  32. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k1, "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k2
  33. WHERE
  34. k1."TABLE_SCHEMA" = ? AND
  35. k2."TABLE_SCHEMA" = ? AND
  36. k1."REFERENCED_TABLE_SCHEMA" = ? AND
  37. k2."REFERENCED_TABLE_SCHEMA" = ? AND
  38. k1."TABLE_NAME" = k2."TABLE_NAME" AND
  39. k1."REFERENCED_TABLE_NAME" = ? AND
  40. k2."REFERENCED_TABLE_NAME" IN ?'
  41. );
  42. protected function connectDatabase($hostname,$username,$password,$database,$port,$socket,$charset) {
  43. $db = mysqli_connect($hostname,$username,$password,$database,$port,$socket);
  44. if (mysqli_connect_errno()) {
  45. throw new \Exception('Connect failed. '.mysqli_connect_error());
  46. }
  47. if (!mysqli_set_charset($db,$charset)) {
  48. throw new \Exception('Error setting charset. '.mysqli_error($db));
  49. }
  50. if (!mysqli_query($db,'SET SESSION sql_mode = \'ANSI_QUOTES\';')) {
  51. throw new \Exception('Error setting ANSI quotes. '.mysqli_error($db));
  52. }
  53. return $db;
  54. }
  55. protected function query($db,$sql,$params) {
  56. $sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
  57. $param = array_shift($params);
  58. if ($matches[0]=='!') return preg_replace('/[^a-zA-Z0-9\-_=<>]/','',$param);
  59. if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
  60. return "'".mysqli_real_escape_string($db,$v)."'";
  61. },$param)).')';
  62. return "'".mysqli_real_escape_string($db,$param)."'";
  63. }, $sql);
  64. //echo "\n$sql\n";
  65. return mysqli_query($db,$sql);
  66. }
  67. protected function fetch_assoc($result) {
  68. return mysqli_fetch_assoc($result);
  69. }
  70. protected function fetch_row($result) {
  71. return mysqli_fetch_row($result);
  72. }
  73. protected function insert_id($db,$result) {
  74. return mysqli_insert_id($db);
  75. }
  76. protected function affected_rows($db,$result) {
  77. return mysqli_affected_rows($db);
  78. }
  79. protected function close($result) {
  80. return mysqli_free_result($result);
  81. }
  82. protected function fetch_fields($result) {
  83. return mysqli_fetch_fields($result);
  84. }
  85. protected function add_limit_to_sql($sql,$limit,$offset) {
  86. return "$sql LIMIT $limit OFFSET $offset";
  87. }
  88. protected function is_binary_type($field) {
  89. //echo "$field->name: $field->type ($field->flags)\n";
  90. return ($field->flags & 128);
  91. }
  92. }
  93. class SQLSRV_CRUD_API extends REST_CRUD_API {
  94. protected $queries = array(
  95. 'reflect_table'=>'SELECT "TABLE_NAME" FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_NAME" LIKE ? AND "TABLE_CATALOG" = ?',
  96. 'reflect_pk'=>'SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "COLUMN_KEY" = \'PRI\' AND "TABLE_NAME" = ? AND "TABLE_CATALOG" = ?',
  97. 'reflect_belongs_to'=>'SELECT
  98. "TABLE_NAME","COLUMN_NAME",
  99. "REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
  100. FROM
  101. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
  102. WHERE
  103. "TABLE_NAME" = ? AND
  104. "REFERENCED_TABLE_NAME" IN ? AND
  105. "TABLE_CATALOG" = ? AND
  106. "REFERENCED_TABLE_CATALOG" = ?',
  107. 'reflect_has_many'=>'SELECT
  108. "TABLE_NAME","COLUMN_NAME",
  109. "REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
  110. FROM
  111. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
  112. WHERE
  113. "TABLE_NAME" IN ? AND
  114. "REFERENCED_TABLE_NAME" = ? AND
  115. "TABLE_CATALOG" = ? AND
  116. "REFERENCED_TABLE_CATALOG" = ?',
  117. 'reflect_habtm'=>'SELECT
  118. k1."TABLE_NAME", k1."COLUMN_NAME",
  119. k1."REFERENCED_TABLE_NAME", k1."REFERENCED_COLUMN_NAME",
  120. k2."TABLE_NAME", k2."COLUMN_NAME",
  121. k2."REFERENCED_TABLE_NAME", k2."REFERENCED_COLUMN_NAME"
  122. FROM
  123. "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k1, "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k2
  124. WHERE
  125. k1."TABLE_CATALOG" = ? AND
  126. k2."TABLE_CATALOG" = ? AND
  127. k1."REFERENCED_TABLE_CATALOG" = ? AND
  128. k2."REFERENCED_TABLE_CATALOG" = ? AND
  129. k1."TABLE_NAME" = k2."TABLE_NAME" AND
  130. k1."REFERENCED_TABLE_NAME" = ? AND
  131. k2."REFERENCED_TABLE_NAME" IN ?'
  132. );
  133. protected function connectDatabase($hostname,$username,$password,$database,$port,$socket,$charset) {
  134. $connectionInfo = array();
  135. if ($port) $hostname.=','.$port;
  136. if ($username) $connectionInfo['UID']=$username;
  137. if ($password) $connectionInfo['PWD']=$password;
  138. if ($database) $connectionInfo['Database']=$database;
  139. if ($charset) $connectionInfo['CharacterSet']=$charset;
  140. $connectionInfo['QuotedId']=1;
  141. $db = sqlsrv_connect($hostname, $connectionInfo);
  142. if (!$db) {
  143. throw new \Exception('Connect failed. '.print_r( sqlsrv_errors(), true));
  144. }
  145. if ($socket) {
  146. throw new \Exception('Socket connection is not supported.');
  147. }
  148. return $db;
  149. }
  150. protected function query($db,$sql,$params) {
  151. $sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
  152. static $i=-1;
  153. $i++;
  154. $param = $params[$i];
  155. if ($matches[0]=='!') return preg_replace('/[^a-zA-Z0-9\-_=<>]/','',$param);
  156. if (is_array($param)) {
  157. $params = array_splice($params, $i, 1, $param);
  158. return '('.implode(',',split('',str_repeat('?',count($param)))).')';
  159. }
  160. return '?';
  161. }, $sql);
  162. return sqlsrv_query($db,$sql,$params);
  163. }
  164. protected function fetch_assoc($result) {
  165. return sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC);
  166. }
  167. protected function fetch_row($result) {
  168. return sqlsrv_fetch_array($result, SQLSRV_FETCH_NUMERIC);
  169. }
  170. protected function insert_id($db) {
  171. $result = sqlsrv_query($db, 'SELECT SCOPE_IDENTITY()');
  172. $data = sqlsrv_fetch_array($result, SQLSRV_FETCH_NUMERIC);
  173. return $data[0];
  174. }
  175. protected function affected_rows($db,$result) {
  176. return sqlsrv_rows_affected($result);
  177. }
  178. protected function close($result) {
  179. return sqlsrv_free_stmt($result);
  180. }
  181. protected function fetch_fields($result) {
  182. //var_dump(sqlsrv_field_metadata($result));
  183. return array_map(function($a){
  184. $p = array();
  185. foreach ($a as $k=>$v) {
  186. $p[strtolower($k)] = $v;
  187. }
  188. return (object)$p;
  189. },sqlsrv_field_metadata($result));
  190. }
  191. protected function add_limit_to_sql($sql,$limit,$offset) {
  192. return "$sql OFFSET $offset ROWS FETCH NEXT $limit ROWS ONLY";
  193. }
  194. protected function is_binary_type($field) {
  195. return ($field->type>=-4 && $field->type<=-2);
  196. }
  197. }
  198. class REST_CRUD_API {
  199. protected $config;
  200. protected function mapMethodToAction($method,$key) {
  201. switch ($method) {
  202. case 'GET': return $key?'read':'list';
  203. case 'PUT': return 'update';
  204. case 'POST': return 'create';
  205. case 'DELETE': return 'delete';
  206. default: $this->exitWith404('method');
  207. }
  208. }
  209. protected function parseRequestParameter(&$request,$characters,$default) {
  210. if (!count($request)) return $default;
  211. $value = array_shift($request);
  212. return $characters?preg_replace("/[^$characters]/",'',$value):$value;
  213. }
  214. protected function parseGetParameter($get,$name,$characters,$default) {
  215. $value = isset($get[$name])?$get[$name]:$default;
  216. return $characters?preg_replace("/[^$characters]/",'',$value):$value;
  217. }
  218. protected function parseGetParameterArray($get,$name,$characters,$default) {
  219. $values = isset($get[$name])?$get[$name]:$default;
  220. if (!is_array($values)) $values = array($values);
  221. if ($characters) {
  222. foreach ($values as &$value) {
  223. $value = preg_replace("/[^$characters]/",'',$value);
  224. }
  225. }
  226. return $values;
  227. }
  228. protected function applyPermissions($database, $tables, $action, $permissions, $multidb) {
  229. if (in_array(strtolower($database), array('information_schema','mysql','sys'))) return array();
  230. $results = array();
  231. $permissions = array_change_key_case($permissions,CASE_LOWER);
  232. foreach ($tables as $table) {
  233. $result = false;
  234. $options = $multidb?array("*.*","$database.*","$database.$table"):array("*","$table");
  235. $options = array_map('strtolower', $options);
  236. foreach ($options as $option) {
  237. if (isset($permissions[$option])) {
  238. $result = strpos($permissions[$option],$action[0])!==false;
  239. }
  240. }
  241. if ($result) $results[] = $table;
  242. }
  243. return $results;
  244. }
  245. protected function processTableParameter($database,$table,$db) {
  246. $tablelist = explode(',',$table);
  247. $tables = array();
  248. foreach ($tablelist as $table) {
  249. $table = str_replace('*','%',$table);
  250. if ($result = $this->query($db,$this->queries['reflect_table'],array($table,$database))) {
  251. while ($row = $this->fetch_row($result)) $tables[] = $row[0];
  252. $this->close($result);
  253. }
  254. }
  255. return $tables;
  256. }
  257. protected function findSinglePrimaryKey($table,$database,$db) {
  258. $keys = array();
  259. if ($result = $this->query($db,$this->queries['reflect_pk'],array($table[0],$database))) {
  260. while ($row = $this->fetch_row($result)) $keys[] = $row[0];
  261. $this->close($result);
  262. }
  263. return count($keys)==1?$keys[0]:false;
  264. }
  265. protected function exitWith404($type) {
  266. if (isset($_SERVER['REQUEST_METHOD'])) {
  267. header('Content-Type:',true,404);
  268. die("Not found ($type)");
  269. } else {
  270. throw new \Exception("Not found ($type)");
  271. }
  272. }
  273. protected function startOutput($callback) {
  274. if (isset($_SERVER['REQUEST_METHOD'])) {
  275. if ($callback) {
  276. header('Content-Type: application/javascript');
  277. echo $callback.'(';
  278. } else {
  279. header('Content-Type: application/json');
  280. }
  281. }
  282. }
  283. protected function endOutput($callback) {
  284. if ($callback) {
  285. echo ');';
  286. }
  287. }
  288. protected function processKeyParameter($key,$table,$database,$db) {
  289. if ($key) {
  290. $key = array($key,$this->findSinglePrimaryKey($table,$database,$db));
  291. if ($key[1]===false) $this->exitWith404('1pk');
  292. }
  293. return $key;
  294. }
  295. protected function processOrderParameter($order,$table,$database,$db) {
  296. if ($order) {
  297. $order = explode(',',$order,2);
  298. if (count($order)<2) $order[1]='ASC';
  299. $order[1] = strtoupper($order[1])=='DESC'?'DESC':'ASC';
  300. }
  301. return $order;
  302. }
  303. protected function processFilterParameter($filter,$db) {
  304. if ($filter) {
  305. $filter = explode(',',$filter,3);
  306. if (count($filter)==3) {
  307. $match = $filter[1];
  308. $filter[1] = 'LIKE';
  309. if ($match=='cs') $filter[2] = '%'.addcslashes($filter[2], '%_').'%';
  310. if ($match=='sw') $filter[2] = addcslashes($filter[2], '%_').'%';
  311. if ($match=='ew') $filter[2] = '%'.addcslashes($filter[2], '%_');
  312. if ($match=='eq') $filter[1] = '=';
  313. if ($match=='ne') $filter[1] = '!=';
  314. if ($match=='lt') $filter[1] = '<';
  315. if ($match=='le') $filter[1] = '<=';
  316. if ($match=='ge') $filter[1] = '>=';
  317. if ($match=='gt') $filter[1] = '>';
  318. if ($match=='in') {
  319. $filter[1] = 'IN';
  320. $filter[2] = explode(',',$filter[2]);
  321. }
  322. } else {
  323. $filter = false;
  324. }
  325. }
  326. return $filter;
  327. }
  328. protected function processPageParameter($page) {
  329. if ($page) {
  330. $page = explode(',',$page,2);
  331. if (count($page)<2) $page[1]=20;
  332. $page[0] = ($page[0]-1)*$page[1];
  333. }
  334. return $page;
  335. }
  336. protected function retrieveObject($key,$table,$db) {
  337. if (!$key) return false;
  338. if ($result = $this->query($db,'SELECT * FROM "!" WHERE "!" = ?',array($table[0],$key[1],$key[0]))) {
  339. $object = $this->fetch_assoc($result);
  340. $this->close($result);
  341. }
  342. return $object;
  343. }
  344. protected function createObject($input,$table,$db) {
  345. if (!$input) return false;
  346. $keys = implode('","',split('', str_repeat('!', count($input))));
  347. $values = implode(',',split('', str_repeat('?', count($input))));
  348. $params = array_merge(array_keys((array)$input),array_values((array)$input));
  349. array_unshift($params, $table[0]);
  350. $result = $this->query($db,'INSERT INTO "!" ("'.$keys.'") VALUES ('.$values.')',$params);
  351. return $this->insert_id($db,$result);
  352. }
  353. protected function updateObject($key,$input,$table,$db) {
  354. if (!$input) return false;
  355. $params = array();
  356. $sql = 'UPDATE "!" SET ';
  357. $params[] = $database;
  358. $params[] = $table[0];
  359. foreach (array_keys((array)$input) as $i=>$k) {
  360. if ($i) $sql .= ',';
  361. $v = $input->$k;
  362. $sql .= '"!"=?';
  363. $params[] = $k;
  364. $params[] = $v;
  365. }
  366. $sql .= ' WHERE "!"=?';
  367. $params[] = $key[1];
  368. $params[] = $key[0];
  369. $result = $this->query($db,$sql,$params);
  370. return $this->affected_rows($db, $result);
  371. }
  372. protected function deleteObject($key,$table,$db) {
  373. $result = $this->query($db,'DELETE FROM "!" WHERE "!" = ?',array($table[0],$key[1],$key[0]));
  374. return $this->affected_rows($db, $result);
  375. }
  376. protected function findRelations($tables,$database,$db) {
  377. $collect = array();
  378. $select = array();
  379. if (count($tables)>1) {
  380. $table0 = array_shift($tables);
  381. $result = $this->query($db,$this->queries['reflect_belongs_to'],array($table0,$tables,$database,$database));
  382. while ($row = $this->fetch_row($result)) {
  383. $collect[$row[0]][$row[1]]=array();
  384. $select[$row[2]][$row[3]]=array($row[0],$row[1]);
  385. }
  386. $result = $this->query($db,$this->queries['reflect_has_many'],array($tables,$table0,$database,$database));
  387. while ($row = $this->fetch_row($result)) {
  388. $collect[$row[2]][$row[3]]=array();
  389. $select[$row[0]][$row[1]]=array($row[2],$row[3]);
  390. }
  391. $result = $this->query($db,$this->queries['reflect_habtm'],array($database,$database,$database,$database,$table0,$tables));
  392. while ($row = $this->fetch_row($result)) {
  393. $collect[$row[2]][$row[3]]=array();
  394. $select[$row[0]][$row[1]]=array($row[2],$row[3]);
  395. $collect[$row[4]][$row[5]]=array();
  396. $select[$row[6]][$row[7]]=array($row[4],$row[5]);
  397. }
  398. }
  399. return array($collect,$select);
  400. }
  401. protected function getParameters($config) {
  402. extract($config);
  403. $table = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_*,', false);
  404. $key = $this->parseRequestParameter($request, 'a-zA-Z0-9\-,', false); // auto-increment or uuid
  405. $action = $this->mapMethodToAction($method,$key);
  406. $callback = $this->parseGetParameter($get, 'callback', 'a-zA-Z0-9\-_', false);
  407. $page = $this->parseGetParameter($get, 'page', '0-9,', false);
  408. $filters = $this->parseGetParameterArray($get, 'filter', false, false);
  409. $order = $this->parseGetParameter($get, 'order', 'a-zA-Z0-9\-_*,', false);
  410. $transform = $this->parseGetParameter($get, 'transform', '1', false);
  411. $table = $this->processTableParameter($database,$table,$db);
  412. $key = $this->processKeyParameter($key,$table,$database,$db);
  413. foreach ($filters as &$filter) {
  414. $filter = $this->processFilterParameter($filter,$db);
  415. }
  416. $page = $this->processPageParameter($page);
  417. $order = $this->processOrderParameter($order,$table,$database,$db);
  418. $table = $this->applyPermissions($database,$table,$action,$permissions,$multidb);
  419. if (empty($table)) $this->exitWith404('entity');
  420. $object = $this->retrieveObject($key,$table,$database,$db);
  421. $input = json_decode(file_get_contents($post));
  422. list($collect,$select) = $this->findRelations($table,$database,$db);
  423. return compact('action','database','table','key','callback','page','filters','order','transform','db','object','input','collect','select');
  424. }
  425. protected function listCommand($parameters) {
  426. extract($parameters);
  427. $this->startOutput($callback);
  428. echo '{';
  429. $tables = $table;
  430. $table = array_shift($tables);
  431. // first table
  432. $count = false;
  433. echo '"'.$table.'":{';
  434. if (is_array($order) && is_array($page)) {
  435. $params = array();
  436. $sql = 'SELECT COUNT(*) FROM "!"';
  437. $params[] = $table;
  438. foreach ($filters as $i=>$filter) {
  439. if (is_array($filter)) {
  440. $sql .= $i==0?' WHERE ':' AND ';
  441. $sql .= '"!" ! ?';
  442. $params[] = $filter[0];
  443. $params[] = $filter[1];
  444. $params[] = $filter[2];
  445. }
  446. }
  447. if ($result = $this->query($db,$sql,$params)) {
  448. while ($pages = $this->fetch_row($result)) {
  449. $count = $pages[0];
  450. }
  451. }
  452. }
  453. $params = array();
  454. $sql = 'SELECT * FROM "!"';
  455. $params[] = $table;
  456. foreach ($filters as $i=>$filter) {
  457. if (is_array($filter)) {
  458. $sql .= $i==0?' WHERE ':' AND ';
  459. $sql .= '"!" ! ?';
  460. $params[] = $filter[0];
  461. $params[] = $filter[1];
  462. $params[] = $filter[2];
  463. }
  464. }
  465. if (is_array($order)) {
  466. $sql .= ' ORDER BY "!" !';
  467. $params[] = $order[0];
  468. $params[] = $order[1];
  469. }
  470. if (is_array($order) && is_array($page)) {
  471. $sql = $this->add_limit_to_sql($sql,$page[1],$page[0]);
  472. }
  473. if ($result = $this->query($db,$sql,$params)) {
  474. echo '"columns":';
  475. $fields = array();
  476. $base64 = array();
  477. foreach ($this->fetch_fields($result) as $field) {
  478. $base64[] = $this->is_binary_type($field);
  479. $fields[] = $field->name;
  480. }
  481. echo json_encode($fields);
  482. $fields = array_flip($fields);
  483. echo ',"records":[';
  484. $first_row = true;
  485. while ($row = $this->fetch_row($result)) {
  486. if ($first_row) $first_row = false;
  487. else echo ',';
  488. if (isset($collect[$table])) {
  489. foreach (array_keys($collect[$table]) as $field) {
  490. $collect[$table][$field][] = $row[$fields[$field]];
  491. }
  492. }
  493. foreach ($base64 as $k=>$v) {
  494. if ($v) {
  495. $row[$k] = base64_encode($row[$k]);
  496. }
  497. }
  498. echo json_encode($row);
  499. }
  500. $this->close($result);
  501. echo ']';
  502. }
  503. if ($count) echo ',"results":'.$count;
  504. echo '}';
  505. // prepare for other tables
  506. foreach (array_keys($collect) as $t) {
  507. if ($t!=$table && !in_array($t,$tables)) {
  508. array_unshift($tables,$t);
  509. }
  510. }
  511. // other tables
  512. foreach ($tables as $t=>$table) {
  513. echo ',';
  514. echo '"'.$table.'":{';
  515. $params = array();
  516. $sql = 'SELECT * FROM "!"';
  517. $params[] = $table;
  518. if (isset($select[$table])) {
  519. $first_row = true;
  520. echo '"relations":{';
  521. foreach ($select[$table] as $field => $path) {
  522. $values = $collect[$path[0]][$path[1]];
  523. $sql .= $first_row?' WHERE ':' OR ';
  524. $sql .= '"!" IN ?';
  525. $params[] = $field;
  526. $params[] = $values;
  527. if ($first_row) $first_row = false;
  528. else echo ',';
  529. echo '"'.$field.'":"'.implode('.',$path).'"';
  530. }
  531. echo '}';
  532. }
  533. if ($result = $this->query($db,$sql,$params)) {
  534. echo ',"columns":';
  535. $fields = array();
  536. $base64 = array();
  537. foreach ($this->fetch_fields($result) as $field) {
  538. $base64[] = $this->is_binary_type($field);
  539. $fields[] = $field->name;
  540. }
  541. echo json_encode($fields);
  542. $fields = array_flip($fields);
  543. echo ',"records":[';
  544. $first_row = true;
  545. while ($row = $this->fetch_row($result)) {
  546. if ($first_row) $first_row = false;
  547. else echo ',';
  548. if (isset($collect[$table])) {
  549. foreach (array_keys($collect[$table]) as $field) {
  550. $collect[$table][$field][]=$row[$fields[$field]];
  551. }
  552. }
  553. foreach ($base64 as $k=>$v) {
  554. if ($v) {
  555. $row[$k] = base64_encode($row[$k]);
  556. }
  557. }
  558. echo json_encode($row);
  559. }
  560. $this->close($result);
  561. echo ']';
  562. }
  563. echo '}';
  564. }
  565. echo '}';
  566. $this->endOutput($callback);
  567. }
  568. protected function readCommand($parameters) {
  569. extract($parameters);
  570. if (!$object) $this->exitWith404('object');
  571. $this->startOutput($callback);
  572. echo json_encode($object);
  573. $this->endOutput($callback);
  574. }
  575. protected function createCommand($parameters) {
  576. extract($parameters);
  577. if (!$input) $this->exitWith404('input');
  578. $this->startOutput($callback);
  579. echo json_encode($this->createObject($input,$table,$db));
  580. $this->endOutput($callback);
  581. }
  582. protected function updateCommand($parameters) {
  583. extract($parameters);
  584. if (!$input) $this->exitWith404('subject');
  585. $this->startOutput($callback);
  586. echo json_encode($this->updateObject($key,$input,$table,$db));
  587. $this->endOutput($callback);
  588. }
  589. protected function deleteCommand($parameters) {
  590. extract($parameters);
  591. $this->startOutput($callback);
  592. echo json_encode($this->deleteObject($key,$table,$db));
  593. $this->endOutput($callback);
  594. }
  595. protected function listCommandTransform($parameters) {
  596. if ($parameters['transform']) {
  597. ob_start();
  598. }
  599. $this->listCommand($parameters);
  600. if ($parameters['transform']) {
  601. $content = ob_get_contents();
  602. ob_end_clean();
  603. $data = json_decode($content,true);
  604. echo json_encode(self::mysql_crud_api_transform($data));
  605. }
  606. }
  607. public function __construct($config) {
  608. extract($config);
  609. $hostname = isset($hostname)?$hostname:null;
  610. $username = isset($username)?$username:'root';
  611. $password = isset($password)?$password:null;
  612. $database = isset($database)?$database:false;
  613. $port = isset($port)?$port:null;
  614. $socket = isset($socket)?$socket:null;
  615. $charset = isset($charset)?$charset:'utf8';
  616. $permissions = isset($permissions)?$permissions:array('*'=>'crudl');
  617. $db = isset($db)?$db:null;
  618. $method = isset($method)?$method:$_SERVER['REQUEST_METHOD'];
  619. $request = isset($request)?$request:(isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:'');
  620. $get = isset($get)?$get:$_GET;
  621. $post = isset($post)?$post:'php://input';
  622. $request = explode('/', trim($request,'/'));
  623. $multidb = !$database;
  624. if ($multidb) {
  625. $database = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_,', false);
  626. }
  627. if (!$db) {
  628. $db = $this->connectDatabase($hostname,$username,$password,$database,$port,$socket,$charset);
  629. }
  630. $this->config = compact('method', 'request', 'get', 'post', 'multidb', 'database', 'permissions', 'db');
  631. }
  632. public static function mysql_crud_api_transform(&$tables) {
  633. $get_objects = function (&$tables,$table_name,$where_index=false,$match_value=false) use (&$get_objects) {
  634. $objects = array();
  635. foreach ($tables[$table_name]['records'] as $record) {
  636. if ($where_index===false || $record[$where_index]==$match_value) {
  637. $object = array();
  638. foreach ($tables[$table_name]['columns'] as $index=>$column) {
  639. $object[$column] = $record[$index];
  640. foreach ($tables as $relation=>$reltable) {
  641. if (isset($reltable['relations'])) {
  642. foreach ($reltable['relations'] as $key=>$target) {
  643. if ($target == "$table_name.$column") {
  644. $column_indices = array_flip($reltable['columns']);
  645. $object[$relation] = $get_objects($tables,$relation,$column_indices[$key],$record[$index]);
  646. }
  647. }
  648. }
  649. }
  650. }
  651. $objects[] = $object;
  652. }
  653. }
  654. return $objects;
  655. };
  656. $tree = array();
  657. foreach ($tables as $name=>$table) {
  658. if (!isset($table['relations'])) {
  659. $tree[$name] = $get_objects($tables,$name);
  660. if (isset($table['results'])) {
  661. $tree['_results'] = $table['results'];
  662. }
  663. }
  664. }
  665. return $tree;
  666. }
  667. public function executeCommand() {
  668. $parameters = $this->getParameters($this->config);
  669. switch($parameters['action']){
  670. case 'list': $this->listCommandTransform($parameters); break;
  671. case 'read': $this->readCommand($parameters); break;
  672. case 'create': $this->createCommand($parameters); break;
  673. case 'update': $this->updateCommand($parameters); break;
  674. case 'delete': $this->deleteCommand($parameters); break;
  675. }
  676. }
  677. }
  678. // only execute this when running in stand-alone mode
  679. if(count(get_required_files())<2) {
  680. $api = new SQLSRV_CRUD_API(array(
  681. 'hostname'=>'(local)',
  682. 'username'=>'',
  683. 'password'=>'',
  684. 'database'=>'xxx',
  685. 'charset'=>'UTF-8'
  686. ));
  687. $api->executeCommand();
  688. }