Electronic CHATS
Зарегистрируй себе бесплатно многофункциональный, профессиональный, быстрый чат!

Главная страница | Форум тех. поддержки Впервые у нас? Тогда читай FAQ и Правила! Сделать стартовой | Добавить в избранное

АДМИНКА

Логин:
создать чат
Пароль:
вспомнить


СВЯЗЬ С НАМИ

Администрация

Autodesk admin [показать ICQ]

Помощники

этим людям можно задавать вопросы по настройке и блокировки чата:

Pavel 5006165 helper [?]
Alex 630868614 helper [?]

РАДИО


РЕКЛАМА


«Пишем PHP фреймворк для создания серверных частей приложений в социальных сетях»

Вступление



Когда я только начинал разрабатывать приложения для социальных сетей, хватало 2 файлов — config.php со всеми настройками и простым классом работы с базой, и index.php который отвечал за получение параметров от клиента, обработку и отображение результатов. Естественно долго так продолжаться не могло — сотни строк кода не способствовали быстрой разработке и рефакторингу, обычные sql запросы в базу заставляли делать кучу лишних движений по обработке результатов, критические изменения — изменение источника данных, отображения, префикса таблицы или вообще типа базы или же социальной сети — грозили переписыванием множества строк кода. В сложившейся ситуации встала задача разработки компактной системы, которая позволит максимально сосредоточиться на бизнес логике и абстрагироваться от локальных параметров — типа базы, хостинга, социальной сети.

Наиболее подходящей оказалась модель mvc. Ее использование позволило менять через настройки отображение (json/html/xml/что то свое) и контроллер авторизации (для смены соц сети) без редактирования кода модели. Для работы с базой написана прослойка, которая позволяет обращаться к таблицам как к переменным класса модели (например $this->user, $this->article обращаются к таблицам user и article соответственно) с заданными методами (create,load,loads,save,count) которые возвращают ожидаемые результаты, и использовать для запросов значения по умолчанию (если во всем методе используется userId=123 то можно указать это 1 раз, а не в каждом запросе). В сегодняшнем примере я покажу пример реализации с использованием базы Mysql и соц сети Facebook.

Итоговая структура проекта получилась следующей:
/index.php — «точка входа» для запросов от flash или ajax
/system/settings.ini — все настройки базы и приложения в удобном ini формате
/system/classes/core/ — папка с классами «ядра» — проверка авторизации, отображение, абстрактные методы бд и модели, специфичные для соц сети классы (например для facebook это библиотека facebook php-sdk)
/system/classes/model/ — папка с моделями, которые хранятся в виде /modelName/modelName.php и реализуют класс modelName

Обращение извне к системе имеет следующий вид:
example.com/?act=user.getInfo (для некоторых соц сетей необходимо так же передавать user_id и auth_key)
Теперь перейдем к реализации кода


Точка входа



В начале файла index.php задаются все необходимые для работы define и реализуется метод __autoload для автоматической подгрузки классов. Так же нужно заметить, что для корректной работы необходим PHP 5.2+.

image

Основной класс назван Dispatcher, служит для загрузки файлов конфигурации, проверки авторизации и входящих данных, вызова модели и передачи в отображение результатов ее работы. Класс наследован от Checker, его я разберу чуть позже.
В классе всего всего 2 метода — конструктор, в котором реализованы все проверки и вызов роутера, и сам роутер. После класса вызывается собственно создание его экземпляра для начала работы.

image

Немного о формате выходных данных. Опытным путем составили структуру наиболее удобную для флеша — result отвечает за статус обработки (1-все ок, меньше 0 — ошибка) а response содержит результат. Пример такого ответа в формете json: {«response»:{«avatar»:«1»},«result»:«1»}.
Метод _error это метод класса Checker, и он как раз возвращает на view result<0. Для использования settings и _error в конструктор модели передается ссылка на диспатчер.

Настройки



Фаил settings.ini прост по своей структуре.

[db]
host     = "localhost"
login    = "root"
password = ""
database = "devBase"
charset  = "utf8"
 
[api]
;vk&mm
api_id     = ""
api_secret = ""
api_pay    = ""
;fb
appid      = "123"
apikey     = "apikey"
apisecret  = "apisecret"
domain     = "domain"
 
[global]
data_type = get
view_type = json


Блок [db] отвечает за подключение к базе, блок [api] отвечает за настройки в социальной сети, [global] определяет источник входящих данных и формат отображения.

Теперь перейдем к классам из папки /system/classes/core/. Их в данном случае 7: AbstractDb, AbstractModel, Checker, Database, Registry, View + указанный выше класс Facebook.

Checker



Этот класс реализует следующие методы: конструктор, проверка авторизации, проверка input переменной, проверка input переменной-числа, метод ошибки.

Констуктор просто проверяет откуда брать данные, записывает их в массив input и вызывает метод проверки авторизации:

function __construct(){
        switch($this->settings['global']['data_type']){
            case "get"        :$this->input=$_GET;break;
            case "post"        :$this->input=$_POST;break;
            //case "session"    :$this->input=$_SESSION;break;
            case "cookie"    :$this->input=$_COOKIE;break;
        }
        $this->check_auth();
        Registry::set('input', $this->input);
    }


Метод check_auth самый негибкий в системе. По причине того, что в 1 папке на хостинге всегда лежит версия только для 1 социальной сети, создавать лишний раз нагрузку через if смысла нет и быстрее-закомментировать неиспользуемые варианты авторизации.

Вот вариант для контакта:

    $viewer_id = $this->_check_num('viewer_id',-1);
    $auth_key = $this->_check('auth_key',-2);
    $md5 = md5($this->settings['api']['api_id'].'_'.$viewer_id.'_'.$this->settings['api']['api_pay']);
    if($md5 != $auth_key) $this->_error(-3);
    $this->input["viewer_id"] = $viewer_id;
    $this->me = array("user_id" => $viewer_id);


По причине того что для авторизации в фейсбуке нужно использовать curl запрос, который занимает десятки мс, я предпочитаю кэшировать в куках переменную пользователя. Вот вариант для фейсбука с кэшированием:

image

Функция _setup_me_cook для записи значений в куку:

image

В итоге если мы работаем с фейсбуком, то на момент авторизации может быть только ошибка -1 (чаще всего устаревший\отсутствующий access token) для контакта — -1 -2 -3 и -4.

Завершаем check_auth проверкой переданного act, который в случае ошибки возвращает -5
if(!isset($this->input['act'])||$this->input['act']==""||substr($this->input['act'],0,1)=="_") $this->_error(-5);


Функции _check _check_num и _error интуитивно понятны, поэтому лишь приведу их код. Стоит лишь заметить что функция получения числа так же имеет возможность проверить число на вхождение в массив-для этого его надо передать 3 необязательным аргументом. Так как View мы создали еще раньше в конструкторе Dispatcher, то можно его использовать в методе _error.

    function _check($text,$err){
        if(!isset($this->input[$text])) $this->_error($err);
        return $this->input[$text];
    }
    function _check_num($text,$err,$arr=0){
        if(!isset($this->input[$text])||!is_numeric($this->input[$text])) $this->_error($err);
        if($arr!=0) if(!in_array($this->input[$text],$arr)) $this->_error($err);
        return $this->input[$text];
    }
    function _error($key){
        $this->view->show_error($key);
        exit();
    }


Registry



Данный класс в принципе стандартен, служит лишь для хранения параметров, и никаких изысков от него не нужно

image

View



Данный класс содержит конструктор (для указания типа отображения), общий метод show в который передается отображение, и реализации отображений для каждого типа (т.е. функции для json,xml,html).

<?php
class View{
    var $type;
    function View($val){
        $this->type=$val;
    }
    //второй параметр указывает, проверять ли "жестко"
    //входящие данные. по умолчанию-да (для ошибок-нет)
    function show($data,$check=1){
        $type=$this->type;
        $this->$type($data,$check);
    }


Так же приложу мою вариацию функции json. Собственно по причине того, что utf и кириллицу нужно прогонять по своему, и был введен для метода show параметр check

    function json($data,$check){
        if($check)
            $this->_json_encode($data);
        else 
            json_encode($data);
    }


Реализация метода _json_encode взята отсюда.

AbstractModel



Данный класс наследуется всеми моделями по умолчанию. Он содержит конструктор (для сохранения ссылки на Dispatcher) и реализацию метода __get для «красивой» работы с базой. Так же в этом классе очень удобно писать общие для всех моделей в данной игре методы — например какие то подсчеты очков, которые могут быть вызваны во всех методах.

<?php
class AbstractModel{
    function __construct($link){
        //теперь что бы вызывать _error достаточно
        //вызвать $this->system->_error
        $this->system=$link;
    }
    //реализация "магического" метода
    function __get($a){    
        //если копия AbstractDb для таблицы $a существует
        //возвращаем ее
        if(method_exists($this,$a)) return $this->$a;
        //в ином случае создаем новую
        $this->$a=new AbstractDb($a);
        return $this->$a;
    }


Database



Для каждой базы нужно писать свой класс Database и скорее всего AbstractDb (по этой причине были попытки их объединить, но пока безуспешные). Я приведу пример для варианта mysql, основанный на mysqli.
Конструктор класса ничего не принимает, и считывает параметры из Registry. Это очень удобно если нужно «автономно» работать с базой — например в cron, достаточно подключить 2 класса и записать в registry настройки. Так же не надо эти настройки передавать в AbstractModel и из нее в AbstractDb. Хотя никто не мешает переделать под передачу параметров.
Основные параметры это хост, логин, пароль, название базы и кодировка. Параметр active нужен что бы не создавать сразу подключение к базе а лишь по мере необходимости (могут возникнуть ситуации, когда копия базы создана, а подключение не нужно).

    function Database() {
        $settings = Registry::get('settings');
        $settings = $settings['db'];
        $this->host = $settings['host'];
        $this->login = $settings['login'];
        $this->password = $settings['password'];
        $this->database = $settings['database'];
        $this->charset = $settings['charset'];
        $this->active = 0;         
    }


Функция connect отвечает за установку соединения при active=0

function connect(){
$this->link = new mysqli($this->host,$this->login,$this->password,$this->database); 
   if (mysqli_connect_errno()) { 
printf("SQL connection error: %s\n", mysqli_connect_error()); 
exit; 
} 
$this->link->set_charset($this->charset);
$this->active=1;
}


При желании можно вызывать connect при создании базы, а любое упоминание о active из кода убрать.

Далее идут реализации методов самой работы с базой. select возвращает массив ассоциативных массивов значений, create возвращает id созданной записи, count возвращает просто число, update возвращает число измененных строк, а query ничего не возвращает и служит для прямого обращения к базе и методов delete. Вспомогательные методы insert_id и affected_rows созданы на случай если нужны будут соответствующие данные.

image

Соответственно можно реализовать любую бд, главное что бы она реализовывала данные методы и возвращала соответствующие значения.

Остался самый большой и главный класс для работы с базой — AbstractDb. Он позволяет преобразовывать вызов модели вида $this->user->loads(«name»,«w=2»); в запросы select name from prefix_user where w=2 limit 0,30.

AbstractDb



Конструктор данного класса принимает от магического метода __get имя таблицы, обнуляет значение по умолчанию и получает соединение с базой от Registry

<?php
class AbstractDb{
    function AbstractDb($name){
        $this->name=$name;
        $this->default_where="";
        if(is_null(Registry::get('db'))){
            $this->mydb=new Database();
            Registry::set('db',$this->mydb);
        }
        else $this->mydb=Registry::get('db');
    }


Если необходимо использовать префиксы, то можно или жестко задать
$this->name="prefix_".$name;

или же брать из настроек.

Работа со значениями по умолчанию реализуется в 3 методах — del_default, set_default и get_default.
del_default удаляет указанный ключ и пересчитывает в строку default_where если осталась одна запись (удаляем 3 первых символа («or » или «and»)).

    function del_default($name){
        unset($this->def[$name]);
        if(count($this->def)==1)
            foreach($this->def as $a)
                $this->default_where=substr($a,3);
    }


set_default записывает в массив по ключу «имя параметра» запрос where. Например «name like '%s'» будет сохранено как array(«name»=>«name like '%s'»). Это сделано что бы сразу при необходимости удалять по ключу «name» из массива. 2 параметр этой функции указывает, как относится к другим значениям дефаулт 0 как AND, 1 как OR.

image

get_default принимает запрос пользователя (в виде строки) и возвращает пустую строку или строку запроса (для ускорения в простой ситуации и служит default_where — нам не нужен перебор).

image

Поскольку возможна ситуация когда пользователь указал where но не указал по умолчанию, сделал наоборот или указал и то и другое\не указал ничего — where надо «клеить» в каждой ситуации. Для этого сделана универсальная функция get_where. Она принимает where пользователя, флаг «использовать значения по умолчанию»(1/0) и возвращает нормальное where.

image

Теперь можно перейти к самим методам работы с базой.

save



Использует update mysql метод. Принимает массив параметров обновления, where пользователя и флаг where по умолчанию. Возвращает 0 или число обновленных записей.

    function save($values,$where="",$use_def=1){
        $where=$this->get_where($where,$use_def);
        if(!$data=$this->mydb->update("update ".$this->name." set ".implode(", ",$values).$where")) return 0;
        else return $data;
    }


remove



Использует delete mysql метод. Принимает where пользователя и флаг where по умолчанию.

    function remove($where="",$use_def=1){
        $where=$this->get_where($where,$use_def);
        $this->mydb->query("delete from ".$this->name.$where");
    }


count



Использует select (*) mysql метод. Принимает where пользователя и флаг where по умолчанию.

    function count($where="",$use_def=1){
        $where=$this->get_where($where,$use_def);
        if(!($data=$this->mydb->count("select count(*) from ".$this->name.$where"))) return 0;
        return $data;
    }


load



Функция для загрузки 1 единственной записи (limit 0,1). В случае если запросили одно поле — возвращает сразу его значение, если несколько или все — возвращает ассоциативный массив значений. Базируется на функции loads. Принимает список полей в виде строки, where пользователя и флаг where по умолчанию, параметры группирования и сортировки.

    function load($expression,$where="",$use_def=1,$group_by="",$order_by=""){
        $data=$this->loads($expression,$where,$use_def,"0,1",$group_by,$order_by);
        if(count($data)==0) return array();
        if($expression!="*"&&!strpos($expression,",")) return $data[0][$expression];
        else return $data[0];
    }


loads



Использует select mysql функции. Принимает все возможные select параметры и возвращает массив — результат работы select метода класса БД. Закомментированая строчка служит для отладки запросов.

image

create



Использует insert into. Может принять как массив параметров (вставка 1 строчки), так и массив массивов для нескольких строк. Id по умолчанию задается как '0'

image

Единственный минус этого метода — нельзя нормально использовать insert into select * from. При необходимости решается 1 флагом метода, но пока у меня такой необходимости не было.

Зачем это все нужно, или пример использования



Для того что бы показать возможности системы — приведу код класса User. Он реализует вызовы
example.com/?act=user.getInfo (проверка существования пользователя)
example.com/?act=user.register (регистрация пользователя с получением из GET числа avatar)
example.com/?act=user.getMe (возвращает всю информацию о пользователе)
example.com/?act=user.getPoints (возвращает очки пользователя)

image

На мой взгляд, такой код гораздо удобнее для понимания и редактирования, чем множество прямых mysql запросов в базу и помогает быстро писать код логики, при необходимости быстро внося изменения. И не смотря на такой подход — скорость выполнения совершенно не страдает, самые «сложные» методы стабильно выполняются за 2-4 мс.
Дата публикации: 2011-04-01


СЧЁТЧИКИ

создать чат бесплатно - - - - Топ100 - Хостинг
Design by Autodesk
Копирование любых материалов с сайта, без установки ссылки на echats.ru - ЗАПРЕЩЕНО!
Electronic CHATS™ Company | Copyright © 2009-2024 | All rights reserved