Shared rss feeds:

Servare Mentem - blog

Мой предыдущий блог

пятница, 30 мая 2008 г.

CADBiS часть I : JRadius и с чем его едят

Итак, я обещал продолжить повествование о своём проекте на "соискание степени магистра" :)... Ээ ну, в общем, тупо дипломном проекте.
Итак, что такое JRadius и почему я пишу про него, ведь в предыдущем посте, я, кажется, писал про Proxy-сервер? Так вот, к нему я ещё вернусь. А сейчас, считаю очень важным написать именно про JRadius.
Начну издалека. Самой большой проблемой при написании программ под UNIX становится отладка, особенно если большую часть рабочего времени проводишь в среде Windows. Конечно, можно возразить, что для Linux надо писать проги, сидя под Linux. Но тут я не могу согласиться. Я давно хочу перейти на Linux полностью - и дома и на работе, но не могу этого сделать по одной простой причине: на работе мне приходится писать софт в Ms Visual Studio под .NET, а под Linux это делать не представляется сколько-нибудь возможным...
Теперь многие могут возразить, что раз уж я сижу под Windows, зачем мне разрабатывать софт под *nix? Могу сказать только, что я почти уверен, что по надёжности и производительности Windows-сервера не сравнятся с никсами. Утверждаю сейчас голословно, не хочу развязывать холивары. Если у кого-то возникли сомнения - Google вам в помощь и различные форумы типа "Windows Server vs Linux server". Для меня сейчас этот вопрос не стоит. Я знаю из личного опыта, что хорошо настроенный сервер FreeBSD надёжнее и производительнее хорошо настроенного сервера Windows Server 2003 и имеет гораздо больше удобных, полезных, а главное открытых и бесплатных серверных решений. Такой сервер способен работать буквально годами без сбоев и перезагрузок.
Я немного отвлёкся от темы. Вернёмся к защите моего бакалаврского проекта. Передо мной стояла задача аналогичная той, которая встаёт перед небольшими Интернет-провайдерами или же перед системными администраторами небольших компаний где необходим доступ в Интернет и возможность отслеживать и ограничивать использование этого доступа. А именно: нужно было поднять Интернет-шлюз с аутентификацией и организацией Интернет-доступа через VPN-канал. Каждый пользователь имеет собственный аккаунт с паролем. Каждому аккаунту сопоставлен тариф (группа), которая обеспечивает ему определённый уровень привилегий (например, разрешение/запрет на доступ в определённое время суток, ограничение максимального объёма Интернет-трафика и т.д.).
Решение, ставшее уже классическим в подобной ситуации - это: FreeBSD-сервер с установленным MPD (Multilink PPTP Daemon) и FreeRADIUS. Ну и различные прочие сервисы типа radacct, squid, freenibs, mabill etc. В общем-то и моё решение в рамках бакалаврского проекта заключалось в выборе связки FreeRADIUS+FreeNIBS. Единственное - мне пришлось немного модифицировать сам FreeNIBS, написать к нему свой интерфейс (веб-морду) и выбросить ненужную функциональность. Система отлично себя зарекомендовала и в течении 2-х лет успешно работала практически без сбоев. Попутно росла функциональность системы введением и совершенствованием прокси-сервера, который отслеживал посещаемые сайты, а заодно запрещал или разрешал к ним доступ. Но в определённый момент захотелось получить от системы большую гибкость, чем она обеспечивала. К тому же к тому времени уже был готов прототип прокси-сервера, реализованного на Java, который показал, что программирование на Java полностью избавляет от проблемы кроссплатформенности и сложности отладки приложения на другой платформе. Это даёт очень высокую степень гибкости и расширяемости системы. При этом можно писать, отлаживать и профилировать приложение сидя под любой ОС. Потом просто нужно будет скопировать собранную версию на выбранную платформу, и вуаля - она работает точно так же!
И вот тогда я занялся поисками возможности реализовать управление подключениями тоже на Java, чтобы полностью переписать функционал, который предоставляет FreeNIBS на свой лад.
После довольно продолжительных поисков решение было найдено. JRadius - это Java-framework с открытым исходным кодом, реализующий клиентский и серверный функционал протокола RADIUS. JRadius-клиент позволяет вам реализовать аутентификацию и аккаунтинг по протоколу RADIUS в собственном Java-приложении. Cервер JRadius - это движок обработки RADIUS-протокола, который работает в связке с модулем rlm_jradius для FreeRADIUS. Изначально он разработан Дэвидом Бёрдом (David Bird) во время работы над PicoPoint, B.V. В последствии разработка движка продолжилась и проект поддерживается по сей день.

JRadius - это не отдельный RADIUS-сервер, это Java-сервер, который вызывается rlm_jradius модулем, встроенным во FreeRADIUS. Модуль использует пул соединений с сервером JRadius и передаёт ему пакеты RADIUS-запроса и ответа для любого из событий которое в состоянии обработать любой из модулей FreeRADIUS. Это означает, что вы можете реализовать обработку событий аутентификации, авторизации и аккаунтинга через JRadius. Сам же сервер JRadius -это лёгкий выделенный сервер, конфигурирующийся через XML. Он позволяет использовать специфичный словарь JRadius/FreeRADIUS и любое число ваших собственных обработчиков составляющих единую цепочку (chained handlers).
Итак, я проникся идеей JRadius и загорелся желанием реализовать необходимую функциональность на его базе. Всё что мне пришлось для этого сделать - это скачать JRadius, включить его в проект, написать собственный обработчик (CADBiSHandler) и переопределить метод handle().
Работать с RADIUS-пакетами в JRadius - одно удовольствие. Например, чтобы получить какой-либо атрибут, нужно всего-лишь вызвать:
req.getAttributes().get("Client-IP-Address")
А если необходимо определить тип запроса и в зависимости от этого принять какое-то решение, то можно это реализовать так:
public boolean handle(JRadiusRequest jRequest)
{
  int type = jRequest.getType();
  switch (type)
  {
    case JRadiusServer.JRADIUS_accounting:
    {
      //...
    }
    break;
    case JRadiusServer.JRADIUS_authorize:
    {
      //...
    }
    break;
  }
}
В общем, повторить функциональность FreeNIBS труда не составило. Поэтому было решено реализовать что-нибудь поинтереснее.
Изначально, когда CADBiS только зарождалась, ситуация была следующей. Имелся некоторый ограниченный объём трафика, который было разрешено скачать в месяц(например, 20 гб). Необходимо было так поделить этот трафик между пользователями, чтобы во-первых каждая группа получила то количество, которое соответствует её статусу и числу человек, а во-вторых чтобы существовала некоторая дневная норма, превышение которой запрещено, для того чтобы как можно более рационально поделить месячный Интернет-трафик между пользователями. FreeNIBS этого сделать не позволяет, а реализовать это на нём было слишком сложно и лениво. Теперь же, с использованием JRadius эта идея должна была воплотиться в жизнь.
Идея в следующем. Задаётся месячный предел трафика, превышение которого невозможно. Каждой группе пользователей (будь то администраторы или же студенты) назначается определённое число баллов. В соответствии с этим числом пересчитывается максимальное количество трафика, которое они могут использовать в месяц. Зная текущее число месяца и количество дней в месяце, можно определить сколько дней с начала месяца прошло и сколько ещё остаётся. А значит, можно вычислить дневную норму трафика для каждой группы. Однако, разрешить пользователям определённой группы скачать фиксированное количество трафика в день недостаточно, т.к. кто-то может хотеть выкачать свою долю сегодня, а завтра не придёт вообще. Поэтому было решено разрешить превышения на несколько дней вперёд. То есть, к примеру, норма для студентов - 300 мегабайт за день. Система должна разрешить скачать им 300 мегабайт за текущий день, потом ещё 300 мегабайт за следующий день, и только потом отключить.
Подобная организация позволяет гибко управлять системой и лимитами трафика. Если какой-то группе не хватает их дневного лимита, а у другой группы лимит не расходуется и наполовину, всегда можно подменить баллы так, что каждый получит трафик в соответствии со своими потребностями, и при этом общий объём останется фиксированным.
Далее я расскажу про систему категоризации и фильтрации контента, и соовтетсвенно про прокси-сервер на Java.

Ярлыки: , , , , ,

вторник, 20 мая 2008 г.

PHP vs C#

Когда со мной начали спорить что PHP - вообще никакой язык, я молчать не смог.
Утверждалось, что в PHP вообще нет никаких преимуществ перед великим и могучим C#. Ну как тут было не поспорить? Лично я считаю, что PHP в любом случае более гибкий язык (в силу того что он интерпретируем) и всё что можно реализовать на C# - можно несложно реализовать и на PHP, а вот обратное во множестве случаев практически невозможно!
Мне говорили "но ведь PHP - не объектно-ориентированный язык! в C# - есть полноценная объектная модель, наследование, интерфейсы, абстрактные классы, виртуальные методы, свойства, и большая коллекция паттернов!", "Позвольте, позвольте, но ведь всё это так же есть и в PHP!" - отвечал я. В PHP5 есть нормальная объектная модель со всеми этими необходимыми фичами.
"А есть там делегаты?" - спросили меня. "Они там попросту не нужны" - отвечал я. - "Язык скриптовый, динамический и нестрого типизированный, поэтому нечего говорить про generics, delegates и прочие приблуды - зачем они здесь? Я больше чем уверен, что если мне потребуется делегат, я смогу его реализовать на PHP в 2 счёта!". Вообще, я быстро устаю от подобных нападок. Они, как правило, абсолютно беспочвенны. Ну почему мало кто воспринимает скриптовые языки всерьёз? Один лишь Ruby как-то выделяется на фоне остальных. Sun даже собирается включить его в следующую версию JDK. А PHP, мол, какашка - вчерашний день, не для серьёзных проектов... Ну что за бред, извините меня? Так считают люди которые с ним либо плохо знакомы, либо вообще не имеют представления о том, что такое PHP. Спросите у Oracle и IBM - почему они уделяют столько внимания именно PHP.
У Ruby очень грамотная реклама и хороший подход к обучению - "как писать правильно". И пожалуйста - Ruby On Rails лучший фреймворк в Интернете. Может быть, так оно и есть, я спорить не буду. Мне нравится идеология Rails, но то что Ruby - намного продвинутее PHP как язык, я сказать не могу. Думаю, что они скорее сравнимы. Просто почему-то никто не учится писать на PHP правильно.
Почему-то люди чаще начинают на нём писать плохо, копируют дурацкие примеры, не обращают внимания на абстракцию и декомпозицию... В итоге - в Интернете очень много плохого PHP кода. На PHP гораздо больше фреймворков чем на Ruby, но далеко не все они сравнимы с ROR по удобству. Думаю, что по той же причине, которую я обозначил выше. PHP, видимо, располагает к тому чтобы писать на нём плохо. Он расслабляет? Позволяет не обращать внимания на мелочи? Я в замешательстве.
Что же до сравнения PHP и C# давайте пройдём по наиболее существенным различиям, сравнивая лишь сам язык. Я ни в коей мере не собираюсь сравнивать скажем .NET и PHP - говорю только за язык. Итак:
- ООП? Что сказать - необходимый минимум в PHP есть. Интерфейсы, абстрактные классы, наследование, паттерны - это всё легко реализуемо. Ну да, нет в нём парциальных и вложенных классов. Но, по-моему, это не так существенно. Вот то что нет пространств имён - уже минус, здесь спорить не буду. Хотя, при грамотном подходе можно обойтись и без них. В конце концов их можно реализовать самостоятельно так же как они реализуются в JavaScript(есть прекрасные примеры - ZendFramework, PRADO... etc). Ну а кого это не устраивает - можно подождать выхода 5.3, где namespaces уже будут включены в язык.
- Строгая типизация? Такой же плюс как и минус, объяснять, надеюсь, смысла нет.
- Generics? В PHP от них никакого толка поскольку опять же типизация нестрогая...
- Delegates? - Ну не нужны они здесь! В крайнем случае я могу вызвать create_function(). Это похоже на создание анонимного делегата в C#. Например, сортировка массива:
// PHP 5.x code:
usort($arr, create_function('$a,$b','return ($a==$b)?0:($a<$b)? 1 : -1;'));
- Closures? - Вот это уже интересно. В PHP их нет. Думаю, что это довольно существенный недостаток. Однако, нет ничего невозможного. Можно реализовать и такую приблуду, если захотеть. Один из вариантов реализации - в SMPHPToolkit. Для сравнения:
C# 3.0 code:
var ary = { 1, 2, 3 };
var x = 2;
ary.Select(elem => elem * x;);

То же самое на PHP:
$arr = arr( 1, 2, 3 );
$x = 2;
$arr->select(eval(${new closure('$el')}->{'$el*=$x'}));
В принципе получилось не сильно больше кода. А по сути - почти то же самое. То что это можно реализовать средствами самого языка а не встроенной поддержкой - уже говорит о большей гибкости языка.
Возможно, это не лучшая эмуляция closure на PHP. Очень надеюсь что полноценные замыкания войдут в 6-ю версию.
Но, признаться, я не считаю это таким уж существенным преимуществом. В конце концов в Java тоже нет нормальных замыканий, но самые лучшие фреймворки и паттерны написаны как раз на нём.
Всегда можно воспользоваться классическими интерфейсами.
А теперь давайте рассмотрим преимущества PHP, которых уж точно нет в C# и которые так просто не реализовать.
- Буферизованный вывод. Считаю что это существенное преимущество PHP перед конкурентами. Например, для создания простейшего темплейтного движка:
 function apply_template($filename, $context)
 {
  ob_start();
  extract($context);
  require($filename);
  return ob_get_clean();
 }

 $content = apply_template("template.tpl.php", array('title'=>'Мой заголовок'));
Что делает эта фунцкия? Элементарно - включает файл шаблона в текст текущей страницы и при этом извлекает переменные из массива $context в текущий контекст. Таким образом, в шаблоне будет доступна переменная $title со значением 'Мой заголовок'. А весь текст шаблона (с любым PHP/HTML кодом) будет интерпретирован с текущим контекстом и результат будет возвращён функцией в виде строки.
- Eval. Элементарно - я могу в любой строке написать php/html код и сделать eval($code). Этот код будет интерпретирован с текущим контекстом. Таким образом, можно сделать так чтобы код генерировал код. Очень удобная возможность, отсутствующая в C#.
- Autoload. В PHP есть прекрасный механизм - можно подключать файлы с классами лишь тогда, когда они запрашиваются в первый раз. Например:
function __autoload($class_name) {
    require_once $class_name . '.inc.php';
}
$obj  = new MyClass1();
В данном примере при создании объекта класса MyClass1 будет подключён файл MyClass1.inc.php.
- "Magic methods". Суть "магических" методов в том, что можно реализовать динамически изменяемые классы меняющие своё поведение при обращении к определённым свойствам, методам или при преобразовании их к строке. С помощью __get и __set можно реализовать добавление новых свойств и методов для объекта при первом обращении к ним:
class Setter {
  private $_props = array();
  public function __get($prop) {
    if (isset($this->_props[$prop]))
   return $this->_props[$prop]
    return null;
  }
  function __set($prop, $val) {
      $this->_props[$prop] = $val;
  }
}
$foo = new Setter();
$foo->MyProperty1 = 1;
$foo->MyProperty2 = new MyClass();
Такое поведение похоже на поведение объекта в Ruby, где каждый класс является объектом с изменяемой в процессе работы сигнатурой.
- ${generate_id()}. Под этим я имею ввиду возможность обращаться к переменным или методам с именем, которое является просто строкой. Мне необязательно писать $var. Я могу написать ${'var'} или ${gen_var()}. Это всё будет обращением к переменной $var. На мой взгляд очень полезная возможность.

Conclusion Я перечислил далеко не всё, что, по-моему, легко реализуется в PHP, а в C# почти нереализуемо. Но и этих примеров должно хватить, чтобы понять что PHP всё-таки динамический и гибкий язык и не стоит к нему обращаться как к вчерашнему дню. У PHP свои преимущества и недостатки. Он не хуже Ruby или C#, но и не лучше их. Каждый язык предназначен для своей цели. PHP изначально затачивался под WEB. И он является в настоящее время лидирующей технологией, которая используется в Интернете. Не думаю, что это простая случайность и недоразумение. Да, в нём нет того язящества которое есть в Ruby. Да, его не сравнить по мощи с .NET или Java. Он делает лишь то, для чего создан и справляется с этим на ура. Чтобы найти на нём хороший фреймворк для веб-разработки, долго искать не нужно - их сотни, что так же касается и CMS. Примеров крупномасштабных проектов так же предостаточно. Это заблуждение, что PHP не для них, а только для персональных сайтов. Так было когда-то давно, когда он только появился. Сейчас же всё совсем по-другому.
Так что всё зависит от подхода и от программиста. То, что PHP расслабляет при программировании вовсе не значит, что нужно отказаться от написания хорошего кода и писать ad hoc. Нужно просто выбрать тот framework, который не позволяет этого делать. Вот Ruby On Rails во многом ограничивает. И это правильный подход, поскольку свобода всегда оставляет возможность для написания хардкода. А такой возможности программиста нужно безжалостно лишать.

Ссылки по теме:

Ярлыки: , , , ,

четверг, 15 мая 2008 г.

SM PHP Toolkit : part2

Сегодня я поведаю о втором довольно удобном приспособлении, вошедшем в SMPHPToolkit. Это грид вообще (и ajax-грид в частности). К примеру, необходимо реализовать на странице грид с пэйджингом, сортировками и без перегрузки страницы. Для начала, предположим что у нас есть класс уровня доступа к данным:
// Данные для отображения
class mydata{
 private static $mydata = array(
     array(0,'Название 1'),
     array(1,'Название 2'),
     array(2,'Название 3'),
     array(3,'Название 4'),
     array(4,'Название 5'),
     array(5,'Название 6'),
     array(6,'Название 7'),
     array(7,'Название 8'),
     array(8,'Название 9'),
     );
 // функция получения общего числа записей
 public static function get_total(){return count(self::$mydata);}
 // функция получения определённой страницы отсортированных данных
 // cортировка указывается третьим параметром (true=ASC,false=DESC)
 public static function get_page($pagesize, $page, $asc = true){
  return array_slice(
    ($asc)?self::$mydata:array_reverse(self::$mydata),
    ($page-1)*$pagesize, $pagesize);
 }
 // так же допустим есть функция удаления строки
 public static function delete($num){unset(self::$mydata[$num]);}
}
Начнём создавать грид. Сперва инициализируем AJAX-буфер, а так же AJAX-EntitiesManager - класс, который позволит упростить работу с действиями над данными в гриде. Для буфера зададим отображение прогресса (будет отображаться модальное окно с надписью "wait please"). В качестве второго аргумента для $emanager зададим созданный буфер.
// Инициализируем буфер и менеджер сущностей
$ajaxbuffer = new ajax_buffer("ajax_buffer");
$ajaxbuffer->show_progress(true);
$emanager = new ajax_entities_manager('entities_manager', $ajaxbuffer);
Теперь посмотрим на код инициализации грида:
$grid = new ajax_grid('my_grid',$datasource,$ajaxbuffer,$grid_pager);
В качестве аргументов он принимает datasource (объект данных для отрисовки), ajax-буфер и используемый пейджер. Буфер и пейджер - аргументы необязательные. При этом если не задать первый - он будет создан внутри грида автоматически. Пейджер представляет собой набор кнопок для переключения страниц а так же алгоритм пересчёта общего числа страниц и вычисления текущей страницы. В качестве аргументов он принимает общее число записей которые будут отображаться в гриде и размер страницы.
$grid_pager = new ajax_grid_pager('my_grid_pager',mydata::get_total(),5);
Объект-источник данных (DataSource) для грида описывает его заголовок и задаёт данные. Однако, при создании грида достаточно того, чтобы источник данных содержал только описание колонок. Каждая колонка принимает несколько параметров: имя поля, отображаемое имя поля, тип поля и флаг указывающий на то возможна ли сортировка по данному полю. Дополнительным параметром может служить объект расширяющий объект grid_formatter. Он нужен для кастомизированного отображения данных.
$datasource = new grid_data_source(new grid_header_item_array(
  new grid_header_item('id','Id',type::STRING, true),
  new grid_header_item('title','Заголовок',type::STRING, true),
  new grid_header_item('actions','Действия',null, false, new my_grid_formatter('actions', $emanager->client_id()))
  ));
А вот так можно перегрузить форматтер для того чтобы отрисовать кастом-поле "Действия":
class my_grid_formatter extends grid_formatter {
 protected $_field = '';
 protected $_client_id = '';
 public function __construct($field, $client_id){
  $this->_field = $field;
  $this->_client_id = $client_id;
 }
 public function format($data, $type, $number = 0, $columns = null)
 {
  switch($this->_field){
  case 'actions':
   return '<a href="javascript:'.$this->_client_id.
   '.deleteItem('.$data[0].');">Удалить</a>';
  default:
   return parent::format($data,$type); 
  }
 }
};
Прежде чем выбирать данные и передавать их источнику, нужно их изменить (ведь, возможно данная страница уже запрошена по ajax и пользователь нажал, к примеру "удалить" какую-то запись. Проверим.
// Если нужно выполнить какие-то действия над данными по постбэку
if($ajaxbuffer->is_post_back() &&  $emanager->isAnyAction())
{
 switch($emanager->getAction())
 {
  // необходимо удалить строку
  case $emanager->action->DEL:
   mydata::delete($emanager->getItem());
  break;
 }
// сбрасываем действие (в противном случае оно будет активно при каждом ajax запросе)
 $emanager->eraseAction();
}
Вот и всё. Все основные инициализации сделаны. Теперь нужно выбрать данные и передать их источнику.
// выбираем текущую страницу отсортированных данных
$mydata = mydata::get_page($grid_pager->get_pagesize(),$grid_pager->get_curpage(), $grid->get_sort_direction() != sorting::SORT_DIR_DESC);
// добавляем данные в DataSource
foreach($mydata as $data)
{
 $datasource->add_row(array($data[0],$data[1],$data)); 
}
Остаётся только отрендерить страницу. Для этого удобно воспользоваться шаблонизатором - классом templater, так же входящим в SMPHPToolkit. Ему передаётся путь к шаблону и ассоциативный массив переменных, которые будут доступны в шаблоне.
// Выводим результат
$templater  = new templater(dirname(__FILE__).'/templates/main.tpl.php');
die($templater->render(array('grid'=>$grid,
   'ajaxbuffer'=>$ajaxbuffer,
   'title'=>'Тестовая страница',
   )));
Ниже приведён код шаблона.
<html>
<head>
 <title><?=$title ?></title>
 <link href="css/grid.css" rel="stylesheet" type="text/css"/>
 <link href="css/ajax.css" rel="stylesheet" type="text/css"/>
 <script type="text/javascript" src="js/common/prototype.js"></script>
 <script type="text/javascript" src="js/common/scriptaculous.js"></script> 
 <script type="text/javascript" src="js/common/window/window.js"></script> 
 <script type="text/javascript" src="js/smphptoolkit/ajaxbuffer.js"></script>
 <script type="text/javascript" src="js/smphptoolkit/entitymanager.js"></script>
</head>
<body>
<? $ajaxbuffer->start(); ?>
 <?=$grid->render(); ?><br/>
 Updated at: <?=date("H:i:s") ?>
<? $ajaxbuffer->end(); ?>
</body>
</html>

В итоге получили симпатичный грид с пейджингом, сортировкой и возможностью удалить строку. И всё это работает через ассинхронные запросы к серверу. Изменив mydata, к примеру, на обращение к mysql, можно элементарно поменять данные на данные из базы, а чтобы добавить колонку, достаточно добавить элемент в массивы которые передаются источнику данных. Чтобы поменять шаблон - достаточно исправить строку, передаваемую шаблонизатору. Всё строится примитивно, интуитивно понятно и в то же время достаточно гибко.
Код данного примера и последнюю версию smphptoolkit можно забрать в моём svn.
В следующем посте про SMPHPToolkit я расскажу про класс storage, который позволяет оптимально хранить данные в текстовых файлах и удобно работать с выборками данных.

Ярлыки: , , , , ,

пятница, 2 мая 2008 г.

SM PHP Toolkit : part1

Однажды, после того как я попробовал ASP.NET Ajax и UpdatePanel в частности, мне понравилась идея того как работает этот контрол - удобно и просто для понимания и использования.
Потом мне как-то пришлось снова писать на PHP и захотелось создать нечто подобное в нём - просто так, интереса ради. Ну и я достаточно легко и быстро воссоздал подобную функциональность, используя Ajax от prototypejs. Вообще же, загоревшись этой идеей я даже написал целый набор классов, который потом получил название SMPHPToolkit. В него помимо этого самого AjaxBuffer'а, вошёл контрол grid с пейджингом и сортировкой, класс быстрого и удобного управления файловым хранилищем (хранение любых кастомных объектов в файлах, оптимизированная скорость выборки, поддержка сортировок, условных выборок и т.д.)... Но об этом потом.
Пользоваться же AjaxBuffer достаточно удобно и уж точно не сложнее чем UpdatePanel в asp.net.
Сначала нужно создать объект класса ajax_buffer. Сделать это нужно до любого вывода данных (то есть примерно там же, где запускается сессия), т.к. в конструкторе запускается буферизованный вывод (это нужно при постбэке).
$ajaxbuf = new ajax_buffer("update_buffer");
Так же нужно обязательно подключить prototype и клиентский API для буфера (автоматическое подключение библиотек было решено не использовать во избежание различных конфликтов).
  
  
Затем можно в любом месте html/php кода написать приблизительно следующее:
<? $ajaxbuf->start(); ?>
  Today is <?=date("Y/m/d H:i:s")?> (updated by ajax)
<? $ajaxbuf->end(); ?>
Остаётся только вызвать update с клиента, чтобы область, заключённая между "$ajaxbuf->start()" и "$ajaxbuf->end()" обновилась через AJAX. Добавляем в конец html кода:
<script type="text/javascript">
  setInterval('<?=$ajaxbuf->client_id() ?>.update()', 1000);
</script>
Вот и всё! Посмотрим что получилось:
<?php
require_once("SMPHPToolkit/SMAjax.php");
$ajaxbuf = new ajax_buffer("update_buffer");
?>
<html>
<head>
  <script type="text/javascript" src="js/common/prototype.js"></script>
  <script type="text/javascript" src="js/ajax/engine.js"></script>
</head>
<body>
<? $ajaxbuf->start(); ?>
  Today is <?=date("Y/m/d H:i:s")?> (updated by ajax)
<? $ajaxbuf->end(); ?>

<script type="text/javascript"> 
  setInterval('<?=$ajaxbuf->client_id() ?>.update()', 1000);
</script>
</body>
</html>
В данном примере каждую секунду по ajax-запросу просто обновляется строка "Today is...", это можно легко заменить на обновление любых объектов, которые генерятся php-кодом.
На одной странице можно создать неограниченное количество буферов, каждый из которых будет перегружаться по своему событию. Нужно всего-лишь вызвать
<?=$ajaxbuf->client_id() ?>.update()
например, по клику на кнопке, и область, заключённая в start-end будет обновлена. Этот механизм очень похож по принципу на UpdatePanel.
Кроме этого простого примера, можно так же создавать переменные для обмена данными между серверной и клиентской частями приложения:
$ind_count = new ajax_var('ind_count',0);
$ajaxbuf->register_var($ind_count);
Все переменные, которые были таким образом зарегистрированы в буфере, будут доступны в javascript-коде через:
var ind_count = <?=$ajaxbuf->client_id() ?>.get_var('<?=$ind_count->client_id() ?>');
и в серверном коде:
 $ind_count->get_value();
Для буфера так же существует метод проверки того запрошена ли страница через Ajax или же она запрошена впервые пользователем: $ajaxbuf->is_post_back().
Дополнительно можно настроить метод обновления данных в области, окружённой start-end, то есть, например, можно указать метод обновления ajax_buffer_method::APPEND_AFTER и данные будут просто дописываться к уже имеющимся. По-умолчанию же область полностью переписывается обновлёнными данными.
На основе AjaxBuffer можно реализовать простые и понятные ajax-приложения, лёгкие в использовании и кастомизации.
Забрать последнюю версию SMPHPToolkit можно в моём svn-репозитории.

[To be continued...]

Ярлыки: , , , ,

-->