fork download
  1. <?php
  2. /**
  3.  * Модуль реализации работы с Пользователями
  4.  * Реализация UserList0
  5. */
  6.  
  7.  
  8. /**
  9.  * Класс модели работы с БД
  10. */
  11. class DB
  12. {
  13. private $host = "localhost";
  14. private $dbname = "users";
  15. private $user = "";
  16. private $pass = "";
  17.  
  18. private static $DBH;
  19.  
  20. private function __construct () {
  21. // MySQL через PDO_MYSQL
  22. try {
  23. self::$DBH = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->dbname, $this->user, $this->pass);
  24. self::$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
  25. self::$DBH->query('SET NAMES utf8');
  26. } catch(Exception $e) {
  27. throw new Exception ("[DB PDO] Ошибка БД - " . $e->getMessage());
  28. }
  29. }
  30.  
  31. public static function GetDBH () {
  32. if ( empty ( self::$DBH )) {
  33. new DB();
  34. }
  35. return self::$DBH;
  36. }
  37. }
  38.  
  39.  
  40. /**
  41.  * Класс модели работы с пользователями
  42. */
  43. class UserModel
  44. {
  45. private $dbInctance;
  46. private $dbStruct = array ( 'table' => 'users',
  47. 'columns' => array ('id' => 'key',
  48. 'login' => 'txt',
  49. 'nick' => 'txt',
  50. 'email' => 'txt'
  51. ) ,
  52. 'id' => 'id',
  53. 'unic' => array ('login', 'email'),
  54. 'nochange' => array ('id', 'login'),
  55. 'require' => array ('login')
  56. );
  57.  
  58. public function __construct(PDO $dbInctance)
  59. {
  60. $this->dbInctance = $dbInctance;
  61. }
  62.  
  63. /**
  64. * получает пользователя по ID, алиас к selectUsers с параметром.
  65.   * Возвращает массив данных пользователя
  66. */
  67. public function selectUserByID($id)
  68. {
  69. $retArray = $this->selectUsers( array($this->dbStruct['id'] => $id) );
  70. return $retArray[0];
  71. }
  72.  
  73. /**
  74. * получает список пользователей с учетом $filter, с условием LIKE % если в поле указана *.
  75.   * если указано orCond = true, ищет по условию OR
  76. */
  77. public function selectUsers(array $filter = array(), $orCond = false)
  78. {
  79. $retArray = array();
  80. $sqlStr = "SELECT * FROM " . $this->dbStruct['table'];
  81. $condStr = "";
  82.  
  83. // экранируем - необходимо, т.к. запрос сборный и мы не сможем использовать paramValue PDO
  84. $filter = $this->addslashesIt($filter);
  85.  
  86. foreach ($filter as $name => $val) {
  87. $val = addslashes($val);
  88. if (strpos($val, '*') !== false) { // найдены условия, нужен LIKE
  89. $val = str_replace('*', '%', $val);
  90. $val = $name . ' LIKE ' . "'" . $val . "'";
  91. } else { // * нет, нужно сравнение
  92. $val = $name . ' = ' . "'" . $val . "'";
  93. }
  94.  
  95. if (!strlen($condStr)) { // условия пустые
  96. $condStr = $val;
  97. } else { // есть условия
  98. $condStr .= $orCond?' OR ':' AND ';
  99. $condStr .= $val;
  100. }
  101. }
  102.  
  103. if (strlen($condStr)) { // есть условия для WHERE
  104. $sqlStr .= ' WHERE ' . $condStr;
  105. }
  106.  
  107. $sth = $this->dbInctance->query($sqlStr);
  108. $sth->setFetchMode(PDO::FETCH_ASSOC);
  109.  
  110. if ($rows = $sth->fetchAll()) {
  111. $retArray = $rows;
  112. }
  113.  
  114. return $retArray;
  115. }
  116.  
  117. /**
  118. * создает пользователя на основании данных полей, указанных в $data.
  119.   * возвращает id созданного пользователя
  120. */
  121. public function addUser(array $data)
  122. {
  123. $retArray = array();
  124. $sqlStr = "INSERT INTO " . $this->dbStruct['table'] . " (:col) VALUES (':val')";
  125. $requiredCol = $this->dbStruct['require'];
  126. $mustBeUnic = $this->dbStruct['unic'];
  127.  
  128. // экранируем - необходимо, т.к. запрос сборный и мы не сможем использовать paramValue PDO
  129. $data = $this->addslashesIt($data);
  130.  
  131. foreach ($requiredCol as $column) {
  132. if ( !array_key_exists($column, $data) || !strlen($data[$column])) { // не найдено обязательное поле или оно пустое
  133. throw new Exception ( "[" . __CLASS__ . "] Не указано обязательное поле " . $column . "!");
  134. }
  135. }
  136.  
  137. $keys = implode(", ", array_keys($data));
  138. $values = implode("', '", array_values($data));
  139. $sqlStr = str_replace(':col', $keys, $sqlStr);
  140. $sqlStr = str_replace(':val', $values, $sqlStr);
  141.  
  142.  
  143. // проверяем уникальность данных, если задано
  144. if (count($mustBeUnic)) {
  145. $filter = array();
  146.  
  147. foreach ($mustBeUnic as $column) {
  148. if (array_key_exists($column, $data) && strlen($data[$column])) {
  149. $filter[$column] = $data[$column];
  150. }
  151. }
  152.  
  153. if ( count($filter) && count($this->selectUsers($filter, true)) ) {
  154. throw new Exception ("[" . __CLASS__ . "] Пользователь с такими данными уже существует!");
  155. }
  156. }
  157.  
  158. $sth = $this->dbInctance->query($sqlStr);
  159. return $this->dbInctance->lastInsertId();
  160. }
  161.  
  162. /**
  163. * изменяет данные полей, указанных в $data пользователей по ID.
  164. */
  165. public function updateUser($id, array $data)
  166. {
  167. $retArray = array();
  168. $sqlStr = "UPDATE " . $this->dbStruct['table'] . " SET ";
  169. $condStr = "WHERE " . $this->dbStruct['id'] . " = " . $this->addslashesIt($id);
  170.  
  171. $mustBeUnic = $this->dbStruct['unic'];
  172. $cantBeChange = $this->dbStruct['nochange'];
  173.  
  174. // экранируем - необходимо, т.к. запрос сборный и мы не сможем использовать paramValue PDO
  175. $data = $this->addslashesIt($data);
  176.  
  177. $setLine = "";
  178.  
  179. foreach ($data as $key => $val) {
  180.  
  181. if (strlen($setLine)) { // еще одно поле
  182. $setLine .= ", ";
  183. }
  184. $setLine .= $key . ' = ' . "'" . $val . "'";
  185. }
  186.  
  187. $sqlStr .= $setLine . $condStr;
  188.  
  189.  
  190. if (count($cantBeChange)) {
  191. foreach ($cantBeChange as $column) {
  192. if (array_key_exists($column, $data)) {
  193. throw new Exception ("[" . __CLASS__ . "] Нельзя изменить поле " . $column . "!");
  194. }
  195. }
  196. }
  197.  
  198. // проверяем уникальность данных, если задано
  199. if (count($mustBeUnic)) {
  200. $filter = array();
  201.  
  202. foreach ($mustBeUnic as $column) {
  203. if (array_key_exists($column, $data) && strlen($data[$column])) { // существует и не пустой (пустое поле мб не уникальным)
  204. $filter[$column] = $data[$column];
  205. }
  206. }
  207. // проверяем наличие пользователя, у которого есть такие данные, кроме собственно изменяемой записи
  208. if ( count($filter) ) {
  209.  
  210. $searchedUsers = $this->selectUsers($filter, true);
  211.  
  212. if (count ($searchedUsers) > 1) {
  213. throw new Exception ("[" . __CLASS__ . "] Пользователь с такими данными уже существует - в базе есть несколько пользователей с не уникальными данными!");
  214. }
  215.  
  216. if (count ($searchedUsers) && $searchedUsers[0][ $this->dbStruct['id'] ] !== $id) {
  217. throw new Exception ("[" . __CLASS__ . "] Пользователь с такими данными уже существует!");
  218. }
  219. }
  220. }
  221.  
  222. $sth = $this->dbInctance->query($sqlStr);
  223. if (!$sth->rowCount()) {
  224. throw new Exception ("[" . __CLASS__ . "] Не удалось обновить данные!");
  225. }
  226. return true;
  227. }
  228.  
  229. /**
  230. * удаляет пользователя по id.
  231.   * возвращает id созданного пользователя
  232. */
  233. public function deleteUser($id)
  234. {
  235. $retArray = array();
  236. $sqlStr = "DELETE FROM " . $this->dbStruct['table'] . " WHERE " . $this->dbStruct['id'] . " = " . $this->addslashesIt($id);
  237.  
  238. $sth = $this->dbInctance->query($sqlStr);
  239.  
  240. if (!$sth->rowCount()) {
  241. throw new Exception ("[" . __CLASS__ . "] Не удалось удалить запись!");
  242. }
  243. return true;
  244. }
  245.  
  246. /**
  247. * Экранирует данные. Может принимать массив и строку
  248.   * возвращает массив \ строку с экранированными данными
  249. */
  250. private function addslashesIt($data)
  251. {
  252. if (is_array($data)) { // массив
  253. foreach ($data as $key => $val) {
  254. $data[$key] = addslashes($val);
  255. }
  256. } else { // строка
  257. $data = addslashes($data);
  258. }
  259.  
  260. return $data;
  261. }
  262.  
  263. /**
  264. * Получает структуру таблицы для дальнейшей обработки \ построения запроса
  265.   * возвращает массив данных
  266. */
  267. public function getTableStructure()
  268. {
  269. return $this->dbStruct;
  270. }
  271. }
  272.  
  273. /**
  274.  * Форматирует полученные данные в соответствии с форматом выдачи
  275.  * возвращает данные в запрошенном формате
  276.  */
  277. class FormatUtil
  278. {
  279. public static function toXML(array $data)
  280. {
  281. header('Content-Type: application/xml; charset=utf8');
  282. $xml = new SimpleXMLElement('<root/>');
  283. self::to_xml($xml, $data);
  284. return $xml->asXML();
  285. }
  286.  
  287. public static function toJSON(array $data)
  288. {
  289. header('Content-Type: application/json; charset=utf8');
  290. return json_encode($data);
  291. }
  292.  
  293. private static function to_xml(SimpleXMLElement $object, array $data)
  294. {
  295. foreach ($data as $key => $value)
  296. {
  297. if (is_array($value))
  298. {
  299. //$new_object = $object->addChild( is_numeric($key) ? 'user'.$key : $key);
  300. $new_object = $object->addChild( is_numeric($key) ? 'user' : $key);
  301. self::to_xml($new_object, $value);
  302. }
  303. else
  304. {
  305. $object->addChild( is_numeric($key) ? 'user'.$key : $key , $value);
  306. }
  307. }
  308. }
  309. }
  310.  
  311. /**
  312.  * Класс авторизации, проверяет наличие куки, производит аутентификацию
  313.  * возвращает true \ false
  314.  */
  315.  
  316. class Authorization {
  317. private static $OBJ;
  318. private $cookieName = 'auth';
  319. private $authName = 'authorization';
  320. private $paramArray;
  321. private $paramType;
  322.  
  323. private $login = 'test';
  324. private $pass = 'test';
  325.  
  326. private $isAuth = false;
  327.  
  328. private function __construct ()
  329. {
  330. $this->paramType = $_SERVER["REQUEST_METHOD"];
  331. if($this->paramType == 'GET') {
  332. $this->paramArray = $_GET;
  333. } else {
  334. $this->paramArray = $_POST;
  335. }
  336.  
  337. if (!isset($_COOKIE[$this->cookieName])) {
  338. if (array_key_exists($this->authName, $this->paramArray)
  339. && array_key_exists('login', $this->paramArray[$this->authName])
  340. && array_key_exists('pass', $this->paramArray[$this->authName]))
  341. {
  342. $login = $this->paramArray[$this->authName]['login'];
  343. $pass = $this->paramArray[$this->authName]['pass'];
  344.  
  345. if ($login == $this->login && $pass == $this->pass)
  346. {
  347. setcookie ($this->cookieName, md5($pass), time() + 3600);
  348. $this->isAuth = true;
  349. }
  350. }
  351. } else {
  352. if ($_COOKIE[$this->cookieName] == md5($this->pass)) {
  353. $this->isAuth = true;
  354. }
  355. }
  356.  
  357.  
  358. }
  359.  
  360. public static function init()
  361. {
  362. if ( empty ( self::$OBJ )) {
  363. self::$OBJ = new Authorization();
  364. }
  365. return self::$OBJ;
  366. }
  367.  
  368. public function isAuth ()
  369. {
  370. return $this->isAuth;
  371. }
  372. }
  373.  
  374.  
  375. /**
  376.  * Класс-роутер, получает параметры и вызывает модель
  377.  * возвращает данные в json или запрошенном формате
  378.  */
  379. class Route {
  380.  
  381. private static $OBJ;
  382. private $paramArray;
  383. private $paramType;
  384.  
  385. private $format = 'json';
  386.  
  387. private $actionName = 'action';
  388. private $api = array ( 'get' => array( 'metod' => 'selectUsers',
  389. 'param' => array ('id', 'login', 'nick', 'email')
  390. ),
  391. 'getbyid'=> array( 'metod' => 'selectUserByID',
  392. 'param' => array ('id')
  393. ),
  394. 'add' => array( 'metod' => 'addUser',
  395. 'param' => array ('data')
  396. ),
  397. 'update' => array( 'metod' => 'updateUser',
  398. 'param' => array ('id', 'data')
  399. ),
  400. 'delete' => array( 'metod' => 'deleteUser',
  401. 'param' => array ('id')
  402. )
  403. );
  404.  
  405. private function __construct ()
  406. {
  407. $this->paramType = $_SERVER["REQUEST_METHOD"];
  408. if($this->paramType == 'GET') {
  409. $this->paramArray = $_GET;
  410. } else {
  411. $this->paramArray = $_POST;
  412. }
  413. }
  414.  
  415. public static function init()
  416. {
  417. if ( empty ( self::$OBJ )) {
  418. self::$OBJ = new Route();
  419. }
  420. return self::$OBJ;
  421. }
  422.  
  423. public function run()
  424. {
  425. // проверяем авторизацию
  426. if (!Authorization::init()->isAuth()) {
  427. $this->makeError("[" . __CLASS__ . "] API доступно только авторизованным пользователям!");
  428. }
  429.  
  430. $action = $this->getParam($this->actionName);
  431.  
  432. if (!array_key_exists($action, $this->api)) {
  433. $this->makeError("[" . __CLASS__ . "] не найдена комманда " . $action . '!');
  434. }
  435.  
  436. $metod = $this->api[$action]['metod'];
  437. $param = array();
  438.  
  439. switch ($action) { // дополнительная проверка \ обработка параметров для комманд
  440. case 'get':
  441. $filter = array();
  442. foreach ($this->api[$action]['param'] as $val) {
  443. $paramElem = $this->getParam($val, false);
  444. if ($paramElem !== false) {
  445. $filter[$val] = $paramElem;
  446. }
  447. }
  448. $param[] = $filter;
  449. break;
  450.  
  451. default:
  452. foreach ($this->api[$action]['param'] as $val) {
  453. $param[] = $this->getParam($val);
  454. }
  455. break;
  456. }
  457.  
  458. $model = new UserModel(DB::GetDBH());
  459.  
  460.  
  461. try {
  462. $returnData = call_user_func_array(array($model,$metod), $param);
  463. } catch(Exception $e) {
  464. $this->makeError( $e->getMessage() );
  465. }
  466.  
  467. echo $this->outputFormated(array ('error'=>'0', 'data' => $returnData) );
  468. return;
  469. }
  470.  
  471. private function getParam ($name, $required = true)
  472. {
  473. if ( !array_key_exists($name, $this->paramArray) ) {
  474. if ($required) {
  475. $this->makeError("[" . __CLASS__ . "] Нет обязательного элемента " . $name . '!');
  476. } else {
  477. return false;
  478. }
  479. }
  480. return $this->paramArray[$name];
  481. }
  482.  
  483. private function outputFormated($data)
  484. {
  485. $format = $this->getParam('format', false);
  486.  
  487. if ($format === false) {
  488. $format = $this->format;
  489. }
  490.  
  491. if (!method_exists('FormatUtil', 'to' . $format)) {
  492. die("[" . __CLASS__ . "] Неверный формат вывода - " . $format . '!');
  493. }
  494.  
  495. $this->format = $format;
  496.  
  497. return FormatUtil::{'to' . $this->format}($data);
  498. }
  499.  
  500. private function makeError($errMess)
  501. {
  502. die ( $this->outputFormated( array('error'=>'1', 'message'=>$errMess) ) );
  503. }
  504. }
  505.  
  506.  
  507. setlocale(LC_ALL, 'ru_RU.utf8');
  508. Header("Content-Type: text/html;charset=UTF-8");
  509.  
  510. $_GET['format'] = 'xml';
  511. $_GET['action'] = 'get';
  512. $_GET['authorization'] = array('login'=>'test', 'pass'=>'test');
  513.  
  514. try {
  515. Route::init()->run();
  516. } catch(Exception $e) {
  517. echo $e->getMessage() . '<br>';
  518. }
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
Success #stdin #stdout #stderr 0.02s 20568KB
stdin
Standard input is empty
stdout
{"error":"1","message":"[Route] API \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c!"}
stderr
PHP Notice:  Undefined index: REQUEST_METHOD in /home/3aOTTD/prog.php on line 407
PHP Notice:  Undefined index: REQUEST_METHOD in /home/3aOTTD/prog.php on line 330