<?php
/**
* Модуль реализации работы с Пользователями
* Реализация UserList0
*/
/**
* Класс модели работы с БД
*/
class DB
{
private $host = "localhost";
private $dbname = "users";
private $user = "";
private $pass = "";
private static $DBH;
private function __construct () {
// MySQL через PDO_MYSQL
try {
self::$DBH = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->dbname, $this->user, $this->pass);
self::$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
self::$DBH->query('SET NAMES utf8');
} catch(Exception $e) {
throw new Exception ("[DB PDO] Ошибка БД - " . $e->getMessage());
}
}
public static function GetDBH () {
if ( empty ( self::$DBH )) { new DB();
}
return self::$DBH;
}
}
/**
* Класс модели работы с пользователями
*/
class UserModel
{
private $dbInctance;
private $dbStruct = array ( 'table' => 'users', 'columns' => array ('id' => 'key', 'login' => 'txt',
'nick' => 'txt',
'email' => 'txt'
) ,
'id' => 'id',
'unic' => array ('login', 'email'), 'nochange' => array ('id', 'login'), 'require' => array ('login') );
public function __construct(PDO $dbInctance)
{
$this->dbInctance = $dbInctance;
}
/**
* получает пользователя по ID, алиас к selectUsers с параметром.
* Возвращает массив данных пользователя
*/
public function selectUserByID($id)
{
$retArray = $this->selectUsers( array($this->dbStruct['id'] => $id) ); return $retArray[0];
}
/**
* получает список пользователей с учетом $filter, с условием LIKE % если в поле указана *.
* если указано orCond = true, ищет по условию OR
*/
public function selectUsers
(array $filter = array(), $orCond = false) {
$sqlStr = "SELECT * FROM " . $this->dbStruct['table'];
$condStr = "";
// экранируем - необходимо, т.к. запрос сборный и мы не сможем использовать paramValue PDO
$filter = $this->addslashesIt($filter);
foreach ($filter as $name => $val) {
if (strpos($val, '*') !== false) { // найдены условия, нужен LIKE $val = $name . ' LIKE ' . "'" . $val . "'";
} else { // * нет, нужно сравнение
$val = $name . ' = ' . "'" . $val . "'";
}
if (!strlen($condStr)) { // условия пустые $condStr = $val;
} else { // есть условия
$condStr .= $orCond?' OR ':' AND ';
$condStr .= $val;
}
}
if (strlen($condStr)) { // есть условия для WHERE $sqlStr .= ' WHERE ' . $condStr;
}
$sth = $this->dbInctance->query($sqlStr);
$sth->setFetchMode(PDO::FETCH_ASSOC);
if ($rows = $sth->fetchAll()) {
$retArray = $rows;
}
return $retArray;
}
/**
* создает пользователя на основании данных полей, указанных в $data.
* возвращает id созданного пользователя
*/
public function addUser
(array $data) {
$sqlStr = "INSERT INTO " . $this->dbStruct['table'] . " (:col) VALUES (':val')";
$requiredCol = $this->dbStruct['require'];
$mustBeUnic = $this->dbStruct['unic'];
// экранируем - необходимо, т.к. запрос сборный и мы не сможем использовать paramValue PDO
$data = $this->addslashesIt($data);
foreach ($requiredCol as $column) {
if ( !array_key_exists($column, $data) || !strlen($data[$column])) { // не найдено обязательное поле или оно пустое throw new Exception ( "[" . __CLASS__ . "] Не указано обязательное поле " . $column . "!");
}
}
// проверяем уникальность данных, если задано
if (count($mustBeUnic)) {
foreach ($mustBeUnic as $column) {
$filter[$column] = $data[$column];
}
}
if ( count($filter) && count($this->selectUsers($filter, true)) ) { throw new Exception ("[" . __CLASS__ . "] Пользователь с такими данными уже существует!");
}
}
$sth = $this->dbInctance->query($sqlStr);
return $this->dbInctance->lastInsertId();
}
/**
* изменяет данные полей, указанных в $data пользователей по ID.
*/
public function updateUser
($id, array $data) {
$sqlStr = "UPDATE " . $this->dbStruct['table'] . " SET ";
$condStr = "WHERE " . $this->dbStruct['id'] . " = " . $this->addslashesIt($id);
$mustBeUnic = $this->dbStruct['unic'];
$cantBeChange = $this->dbStruct['nochange'];
// экранируем - необходимо, т.к. запрос сборный и мы не сможем использовать paramValue PDO
$data = $this->addslashesIt($data);
$setLine = "";
foreach ($data as $key => $val) {
if (strlen($setLine)) { // еще одно поле $setLine .= ", ";
}
$setLine .= $key . ' = ' . "'" . $val . "'";
}
$sqlStr .= $setLine . $condStr;
if (count($cantBeChange)) { foreach ($cantBeChange as $column) {
throw new Exception ("[" . __CLASS__ . "] Нельзя изменить поле " . $column . "!");
}
}
}
// проверяем уникальность данных, если задано
if (count($mustBeUnic)) {
foreach ($mustBeUnic as $column) {
if (array_key_exists($column, $data) && strlen($data[$column])) { // существует и не пустой (пустое поле мб не уникальным) $filter[$column] = $data[$column];
}
}
// проверяем наличие пользователя, у которого есть такие данные, кроме собственно изменяемой записи
$searchedUsers = $this->selectUsers($filter, true);
if (count ($searchedUsers) > 1) { throw new Exception ("[" . __CLASS__ . "] Пользователь с такими данными уже существует - в базе есть несколько пользователей с не уникальными данными!");
}
if (count ($searchedUsers) && $searchedUsers[0][ $this->dbStruct['id'] ] !== $id) { throw new Exception ("[" . __CLASS__ . "] Пользователь с такими данными уже существует!");
}
}
}
$sth = $this->dbInctance->query($sqlStr);
if (!$sth->rowCount()) {
throw new Exception ("[" . __CLASS__ . "] Не удалось обновить данные!");
}
return true;
}
/**
* удаляет пользователя по id.
* возвращает id созданного пользователя
*/
public function deleteUser($id)
{
$sqlStr = "DELETE FROM " . $this->dbStruct['table'] . " WHERE " . $this->dbStruct['id'] . " = " . $this->addslashesIt($id);
$sth = $this->dbInctance->query($sqlStr);
if (!$sth->rowCount()) {
throw new Exception ("[" . __CLASS__ . "] Не удалось удалить запись!");
}
return true;
}
/**
* Экранирует данные. Может принимать массив и строку
* возвращает массив \ строку с экранированными данными
*/
private function addslashesIt($data)
{
foreach ($data as $key => $val) {
}
} else { // строка
}
return $data;
}
/**
* Получает структуру таблицы для дальнейшей обработки \ построения запроса
* возвращает массив данных
*/
public function getTableStructure()
{
return $this->dbStruct;
}
}
/**
* Форматирует полученные данные в соответствии с форматом выдачи
* возвращает данные в запрошенном формате
*/
class FormatUtil
{
public static
function toXML
(array $data) {
header('Content-Type: application/xml; charset=utf8'); $xml = new SimpleXMLElement('<root/>');
self::to_xml($xml, $data);
return $xml->asXML();
}
public static
function toJSON
(array $data) {
header('Content-Type: application/json; charset=utf8'); }
private static
function to_xml
(SimpleXMLElement
$object, array $data) {
foreach ($data as $key => $value)
{
{
//$new_object = $object->addChild( is_numeric($key) ? 'user'.$key : $key);
$new_object = $object->addChild( is_numeric($key) ?
'user' : $key); self::to_xml($new_object, $value);
}
else
{
$object->addChild( is_numeric($key) ?
'user'.$key : $key , $value); }
}
}
}
/**
* Класс авторизации, проверяет наличие куки, производит аутентификацию
* возвращает true \ false
*/
class Authorization {
private static $OBJ;
private $cookieName = 'auth';
private $authName = 'authorization';
private $paramArray;
private $paramType;
private $login = 'test';
private $pass = 'test';
private $isAuth = false;
private function __construct ()
{
$this->paramType = $_SERVER["REQUEST_METHOD"];
if($this->paramType == 'GET') {
$this->paramArray = $_GET;
} else {
$this->paramArray = $_POST;
}
if (!isset($_COOKIE[$this->cookieName])) { {
$login = $this->paramArray[$this->authName]['login'];
$pass = $this->paramArray[$this->authName]['pass'];
if ($login == $this->login && $pass == $this->pass)
{
$this->isAuth = true;
}
}
} else {
if ($_COOKIE[$this->cookieName] == md5($this->pass)) { $this->isAuth = true;
}
}
}
public static function init()
{
if ( empty ( self::$OBJ )) { self::$OBJ = new Authorization();
}
return self::$OBJ;
}
public function isAuth ()
{
return $this->isAuth;
}
}
/**
* Класс-роутер, получает параметры и вызывает модель
* возвращает данные в json или запрошенном формате
*/
class Route {
private static $OBJ;
private $paramArray;
private $paramType;
private $format = 'json';
private $actionName = 'action';
private $api = array ( 'get' => array( 'metod' => 'selectUsers', 'param' => array ('id', 'login', 'nick', 'email') ),
'getbyid'=> array( 'metod' => 'selectUserByID', ),
'add' => array( 'metod' => 'addUser', 'param' => array ('data') ),
'update' => array( 'metod' => 'updateUser', 'param' => array ('id', 'data') ),
'delete' => array( 'metod' => 'deleteUser', )
);
private function __construct ()
{
$this->paramType = $_SERVER["REQUEST_METHOD"];
if($this->paramType == 'GET') {
$this->paramArray = $_GET;
} else {
$this->paramArray = $_POST;
}
}
public static function init()
{
if ( empty ( self::$OBJ )) { self::$OBJ = new Route();
}
return self::$OBJ;
}
public function run()
{
// проверяем авторизацию
if (!Authorization::init()->isAuth()) {
$this->makeError("[" . __CLASS__ . "] API доступно только авторизованным пользователям!");
}
$action = $this->getParam($this->actionName);
$this->makeError("[" . __CLASS__ . "] не найдена комманда " . $action . '!');
}
$metod = $this->api[$action]['metod'];
switch ($action) { // дополнительная проверка \ обработка параметров для комманд
case 'get':
foreach ($this->api[$action]['param'] as $val) {
$paramElem = $this->getParam($val, false);
if ($paramElem !== false) {
$filter[$val] = $paramElem;
}
}
$param[] = $filter;
break;
default:
foreach ($this->api[$action]['param'] as $val) {
$param[] = $this->getParam($val);
}
break;
}
$model = new UserModel(DB::GetDBH());
try {
} catch(Exception $e) {
$this->makeError( $e->getMessage() );
}
echo $this->outputFormated(array ('error'=>'0', 'data' => $returnData) ); return;
}
private function getParam ($name, $required = true)
{
if ($required) {
$this->makeError("[" . __CLASS__ . "] Нет обязательного элемента " . $name . '!');
} else {
return false;
}
}
return $this->paramArray[$name];
}
private function outputFormated($data)
{
$format = $this->getParam('format', false);
if ($format === false) {
$format = $this->format;
}
die("[" . __CLASS__ . "] Неверный формат вывода - " . $format . '!'); }
$this->format = $format;
return FormatUtil::{'to' . $this->format}($data);
}
private function makeError($errMess)
{
die ( $this->outputFormated( array('error'=>'1', 'message'=>$errMess) ) ); }
}
Header("Content-Type: text/html;charset=UTF-8");
$_GET['format'] = 'xml';
$_GET['action'] = 'get';
$_GET['authorization'] = array('login'=>'test', 'pass'=>'test');
try {
Route::init()->run();
} catch(Exception $e) {
echo $e->getMessage() . '<br>';
}