разработка поддерживаемой системы RPC - PullRequest
13 голосов
/ 22 ноября 2010

Я работаю над веб-приложением, которое будет широко использовать методы AJAX для связи клиент / сервер ... JSON-RPC, в частности.Zend Framework используется на стороне сервера и предлагает хороший JSON-RPC-сервер, который я хотел бы использовать.

Моя цель - создать обслуживаемую систему, которая предоставляет подмножество большой серверной функциональности на стороне клиента (javascript), без ненужного дублирования кода.Я видел многочисленные публикации в блогах и учебные пособия о том, как использовать JSON-RPC-сервер ZF (см. здесь и здесь ), но все они, похоже, были направлены на предоставление небольшого общедоступного API,Дублирование кода является распространенным явлением, например, в одном сообщении в блоге представлен следующий метод:

public static function setTitle($bookId, $title) {
    $book = new Nickel_Model_Book($bookId);
    $book->setTitle($title);
    $book->update();
    return true;
}

Мне не нравится тот факт, что существует два метода setTitle.Если сигнатура метода для одного изменяется, другой должен синхронизироваться ... кажется, кошмаром в обслуживании, если у вас обширный API.Мне кажется, что должен быть один Book класс с одним setTitle методом.

Моя первоначальная мысль - добавить аннотацию docblock @export к методам / классам, которые я хочу раскрыть.Когда я решаю раскрыть метод setTitle, я просто добавляю аннотацию, а не новый метод.

Одна потенциальная проблема, которую я вижу, связана с постоянством объекта.На стороне сервера имеет смысл setTitle установить свойство title объекта ... но не сохранять его в базе данных до тех пор, пока не будет вызван update().На стороне клиента вызов setTitle должен немедленно повлиять на базу данных.Одним из возможных решений является изменение всех методов доступа таким образом, чтобы они принимали необязательный второй параметр, означающий, что изменение должно немедленно обновить базу данных:

function setTitle($title, $persist = false) {
    $this->title = $title;

    if ($persist) $this->update();
}

Какой-то прокси-класс может гарантировать, что для $persist установлен флагвсе вызовы RPC на стороне клиента.

Другая проблема - сериализация объектов PHP.На стороне сервера имеет смысл выполнить вызов $book->setTitle("foo") в стиле OO, но на стороне клиента book.setTitle(1234, "foo") имеет смысл (где 1234 - это идентификатор книги) из-за отсутствия состояния.Мое решение для этого было бы сделать так, чтобы вышеупомянутый прокси-класс отвечал за то, чтобы как-то превратить book.setTitle(1234, "foo") в:

$book = new Book();
$book->load(1234);
return $book->setTitle($title);

Я чувствую, что эта проблема должна быть решена или обсуждена ранее ... но яне найти много ресурсов в Интернете.Это похоже на вменяемое решение?

Ответы [ 3 ]

3 голосов
/ 09 сентября 2011

То, что вы ищете, называется Уровень обслуживания .

Их сущности должны быть чисто контейнерами данных (если вы не используете Active Record), вы должны только предоставлять свой уровень обслуживания, а это в свою очередь обращается к их сущностям и их соответствующим методам.

Ваш класс обслуживания будет примерно таким:

class BookService extends Service {

    //...

    public function changeBookTitle( $title, Book $book )
    {
        //verify if the $title is correct and valid
        $book->setTitle( $title );
        $this->methodToGetThePersistenceManager()->save( $book );

        //fire events, create a result object, etc...
    }
}
1 голос
/ 20 августа 2011

Ну,

То, что вы хотите сделать, звучит действительно хорошо на бумаге, и вы хотите, чтобы код был удобен в обслуживании, и, в зависимости от вашей реальной системы, то, что вы просите, может даже быть несколько безопасным, однако оно получаетблизок к тому, чтобы быть опасным и подверженным ошибкам.

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

class Book {
  public $title = '';
  public $id = 0;
  public function setTitle($string) {
    $this->title = $string;
    echo "title called with $string\n";
  }
  public function twoArgs($arg1,$arg2) {
    echo "Multi-call: $arg1,$arg2\n";
  }
}
class Api {
  public function parse($json) {
    $jsonobj = json_decode($json,true);
    foreach ($jsonobj as $key=>$value) {
      $class_name = $key;
      $obj = new $class_name();
      foreach ($value as $vkey=>$vvalue) {
        if (method_exists($obj,$vkey)) {
          call_user_func_array(array($obj,$vkey),$vvalue);
        } else if (isset($obj->$vkey)) {
          echo "Setting $vkey\n";
          $obj->$vkey = $vvalue;
        }
      }
    }
  }
}

$json = json_encode(array('Book' => array('id' => 1234, 'setTitle' => array('The new title'))));
$api = new Api();
$api->parse($json);
$json = json_encode(array('Book' => array('id' => 1234, 'twoArgs' => array('arg1 :) ', 'arg2 :]'))));
$api->parse($json);

Очевидно, что вы захотите добавить логику для обработки постоянных пометок и загрузки или позволить им передавать в конструктор: [args] и обрабатывать это.и т. д. Когда дело доходит до раскрытия функций, вы выставляете их в документации, они все доступны, если с этого момента они являются публичными функциями и т. д.

1 голос
/ 10 июня 2011

Я обдумывал твой вопрос несколько минут. Если вы хотите попробовать, и это на PHP, вы можете сделать несколько магических методов вида

множество {Свойство} из {Object}.

Вы можете сделать это с помощью магического метода __call.

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

Магический метод определил бы, какой тип объекта ему нужно создать и какое свойство установить на него, а затем сохранить.

Вам нужно будет найти какой-то способ различения между удаленными вызовами и локальными вызовами, чтобы вы не ошиблись, если выполняете вызовы из локальной библиотеки и сохраняетесь при удаленных вызовах.

Мне кажется, что я как-то неправильно понял ваш вопрос, но я пытался.

...