Для начала я хочу поблагодарить m007, который подал идею, как можно упростить Programmer-friendly интерфейс взаимодействия Клиент(JS)-Сервер(PHP), что и поспособствовало написанию этой статьи.

Данная статья является заключительной, в которой я объединю JS-Шаблоны, PHP Model-controller, и добавлю Динамическое создание UI методов на JS (некоторые идеи которого были взяты из статьи m007). Тем самым мы получим на выходе неплохой и удобный фундамент для создания своих AJAX приложений.

Динамическое создание методов на JavaScript

Для начала хочу обратить ваше внимание, что все наши методы лежат в PHP-библиотеке. Каждый метод можно вызвать, передав определенные параметры в стиле JSON. И чтобы вызвать PHP-метод из JS, я предлагал в прошлой статье использовать функцию server_exec. Но можно это сделать более удобно и
информативно для программиста.
m007 предложил вызывать функции прямо из JS, то есть вместо server_exec(«database_func(...);»,function(){...}) использовать в JS database.func(...).

В PHP, для не объявленных функций, есть метод __call. В JS такого метода нет, но можно предварительно подгрузить методы из PHP-библиотеки и динамически добавить их в JS-класс.

Для этого в PHP-класс UserInterface (см. прошлую статью) был добавлен метод, который при ‘пустом’ запросе выводил все зарегистрированные методы.
На Javascript написан класс PHPUIclass, который является интерфейсом для UserInterface. В этом классе описан метод обработки `пустого` запроса (спасибо DmitryBaranovskiy за приведение кода в порядок):
this.add=function(res){
      var methods=eval('{' + res.responseText + '}');
      for (var index in methods)
        that[methods[index]] = (function (name) {
          return function () {
            request.push('"' + name + '":' + that.methods(Array.prototype.slice.call(arguments)));
            if (!timer)
              timer=window.setTimeout(function(){
                ajax_load(url, «method={»+that.getrequest(request)+"}", that.ajax_run, true);
              },0);
          };
        })(methods[index]);
    }
* This source code was highlighted with <font size='1'
color='gray'>Source Code Highlighter
.
Эта функция вызывается, когда PHP-библиотека возвращает названия зарегистрированных методов UI (ответ на ‘пустой’ запрос). Исходя из этих данных, этот метод создаёт внутри своего класса новые методы, которые используют для вызова public-методов PHP-библиотеки.

При запуске метода, происходит кодирование входящих параметров (request.push('"'+methods[index]+'":'+that.methods(Array.prototype.slice.call(arguments)))) и установка нулевого таймера на функцию для отсылки запроса. Думаю, что некоторых читателей этот момент удивил. По этому я остановлюсь на нём поподробнее.

Javascript не имеет потоков и весь код выполняет последовательно. Состояния у него всего два – выполнение кода и ожидание прерывания. Прерывания могут прийти, например, от таймера (по таймауту), от AJAX запроса (при смене статуса) и т д. При этом запускается состояния выполнение кода, который не прерывается, пока не дойдёт до конца.
Именно для этого я использую нулевой таймер, который запустит выполнение функции отсылающей запрос на сервер, после окончания выполнения основного кода. Сделано это для того, чтобы отсылать всё одним инкрементир��ванным запросом, состоящим из всех вызываеммых функций и обрабатывать результаты последовательно.

Теперь можно перейти к обработке результатов запроса. Так как обработчик будет вызван не сразу – то это будет функция или несколько функций, которые необходимо вызвать, когда придет ответ.
Этим занимаются методы run и ajax_run. Первый добавляет функции на запуск после получения ответа. Функции должны иметь вид function(data). А функция ajax_run занимается их запуском.
this.run=function(eee){
  func.push(eee);
}

this.ajax_run=function(req){
  var ret=eval('('+req.responseText+')');
  for (var i in func)
   if (func[i])
     func[i](ret);
  request=[];
  func=[];
  timer=false;
}
* This source code was highlighted with <font size='1'
color='gray'>Source Code Highlighter
.
В результате мы получаем модифицируйщийся класс, в зависимости от установленных методов в PHP. И взаимодействие с ним выглядит очень удобно:
//Инициализация класса удаленной работы с файлами.
/* Синхронный запрос на вызов FileManager.php.
Получение методов dir,save,read.
Динамическое создание функций. */
var File=new PHPUIclass('FileManager.php');

//Вызов удалённых функций
/* Установка таймера и формирования запроса */
File.save(«edited.txt»,«Отредактированная часть файла»);
File.read(«save.log»);

File.dir();

//Установка функций для получения результатов запроса.
/* Добавление функции для обработки запроса read */
File.run(show_file);

/* Добавление функции для обработки запроса dir */
File.run(show_dir);

//Конец выполнения скрипта, а значит выполнение POST-запроса по Timeout(0) и возвращение результатов в show_file и show_dir
* This source code was highlighted with Source Code Highlighter.
Ещё один пример, как воспользоваться результатом функции для запуска другой:
function show_file(file){
  //…
}

function show_error(error){
  // ...
}

function check_file(result){
  if (result.ERROR && result.ERROR.checkfile)
    show_error(result.ERROR.checkfile);
  else{
    // Всё прошло нормально?
    // Тогда ещё один запрос, уже на чтение.
    File.read(result[«filename»]);
    File.run(show_file);
  }
}

//Вызов удалённых функций
File.checkfile(«edited.txt»);

File.run(check_file);
* This source code was highlighted with Source Code Highlighter.
Но в этом случае будет происходить два запроса. Этого избежать можно несколькими способами. Один из способов — это реализовать вызов необходимой функции с возвратом пряма в PHP.
function checkfile($filename){
  global $UImethod;
  if (file_exists($filename))
   return $UImethod->read($filename);
  else
   return array(«ERROR» => array(«checkfile»=>$filename));
}
Теряется модульность, но в данной реализации Javascript библиотеки, пока это единственный выход. Возможно в будующем я усовершенствую её, при необходимости.

Пример

А вот — простой пример. File-viewer с возможностью редактировать файлы.

PHP:
UI.php основной класс UserInterface — подключается в библиотеку.
mod.fileview.php библиотека для чтения-записи файлов — функции read и save.
mod.dirview.php библиотека для чтения дириктории — функция dir.
mod.backup.php библиотека для формирования save.log — функции read,save,dir.
FileManager.php основной файл, который включает в себя все модули и запускает обработчик запроса.

Javascript:
PHPUI.js основной класс, который занимается подключением к интерфейсу UserInterface в php.
skin.js — класс работы с шаблонами.
index.html — загрузка шаблонов, вывод файлов.

HTML:
manager.skin — Шаблоны
manager.css — CSS
rus.xml — русский язык.

Рабочий пример

Послесловие

Плюсы:
— Удобство программирования и создания новых методов для JS-интерфейса
— Ясность и прозрачность кода
— Универсальность методов
— Возможен вызов функций с неопределенным количеством параметров
Минусы:
— Нет проверки на параметры методов. Возможны простейшие ошибки при создании кода. Но для этого можно в PHP-функциях проверять пришедшие параметры (например для DEBUG-MODE). В случае не верных параметров, выводить alert(«ERROR»).

p.s. кстати, модель этого проекта является MVC.