Управляемая событиями архитектура и хуки в PHP

Я планирую работать над игрой, в которой есть серверная часть PHP для связи с репозиторием данных. Я подумал об этом и пришел к выводу, что лучшая парадигма дизайна для нашей игры будет основана на событиях. Я ищу систему достижений (похожую на систему значков на этом веб-сайте), и в основном я хотел бы иметь возможность привязать эти «проверки достижений» к ряду различных событий, происходящих в игре. то есть:

Когда пользователь выполняет действие X, запускается крючок Y и вызываются все присоединенные функции для проверки соответствия требованиям достижения.

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

Я не уверен, что это хорошее объяснение того, что я собираюсь делать, но в любом случае я ищу следующее:

  1. Хороший справочный материал о том, как кодировать приложение, управляемое событиями
  2. Фрагменты кода, показывающие, как добавить "ловушку" в функцию в PHP.
  3. Фрагменты кода, показывающие, как прикрепить функцию к «ловушке», упомянутой в пункте 2.

У меня есть несколько идей относительно того, как выполнить 2) и 3), но я надеялся, что кто-нибудь, хорошо разбирающийся в этом вопросе, сможет пролить свет на передовой опыт.

Заранее спасибо!


person MoarCodePlz    schedule 27.07.2011    source источник
comment
Я знаю, что wordpress использует хуки для своих плагинов. Вы можете посмотреть здесь   -  person Maxime Fafard    schedule 27.07.2011
comment
Ссылка на API хуков WordPress - не совсем то, о чем я прошу.   -  person MoarCodePlz    schedule 27.07.2011
comment
Что ж, вы действительно можете проверить, как wordpress использует хуки. Хорошая документация и понятный исходный код.   -  person Maxime Fafard    schedule 27.07.2011
comment
Что ж, реализация ловушки wordpress на самом деле не очень хороший пример, у нее нет четкого интерфейса, реализация содержит ошибки, а код плохо документирован. Однако он показывает, что вы можете назначать обратные вызовы переменным, включая. массивы, в которых вы можете сортировать эти массивы и что вы можете сделать неправильно при вызове обратных вызовов и отмене регистрации обратного вызова. Однако эта информация не доступна в доступном формате.   -  person hakre    schedule 27.07.2011


Ответы (3)


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

Вы можете сделать это либо с помощью "тупых" обратных вызовов (Демо):

class Hooks
{
    private $hooks;
    public function __construct()
    {
        $this->hooks = array();
    }
    public function add($name, $callback) {
        // callback parameters must be at least syntactically
        // correct when added.
        if (!is_callable($callback, true))
        {
            throw new InvalidArgumentException(sprintf('Invalid callback: %s.', print_r($callback, true)));
        }
        $this->hooks[$name][] = $callback;
    }
    public function getCallbacks($name)
    {
        return isset($this->hooks[$name]) ? $this->hooks[$name] : array();
    }
    public function fire($name)
    {
        foreach($this->getCallbacks($name) as $callback)
        {
            // prevent fatal errors, do your own warning or
            // exception here as you need it.
            if (!is_callable($callback))
                continue;

            call_user_func($callback);
        }
    }
}

$hooks = new Hooks;
$hooks->add('event', function() {echo 'morally disputed.';});
$hooks->add('event', function() {echo 'explicitly called.';});
$hooks->fire('event');

Или реализовать шаблон, часто используемый в приложениях, управляемых событиями: Шаблон наблюдателя.

Фрагменты кода, показывающие, как добавить "ловушку" в функцию в PHP.

Ссылка на руководство выше (обратные вызовы могут быть сохранены в переменной) и некоторые примеры кода PHP для шаблона наблюдателя.

person hakre    schedule 27.07.2011
comment
Потрясающие. Это более или менее то, о чем я думал относительно реализации хуков, но хотел убедиться, что я чего-то не упускаю. Также очень крутой сайт для демонстрации PHP. - person MoarCodePlz; 27.07.2011
comment
@MoarCodePlz: в этой реализации отсутствуют контексты событий / ловушек. Вы можете, например, инкапсулировать события в собственные классы, чтобы их можно было запускать (сопоставлять с ними). Однако это добавило бы еще один слой. В этом контексте еще одна полезная функция - is_callable. - person hakre; 27.07.2011
comment
@MoarCodePlz: добавлена ​​некоторая дезинфекция в класс, чтобы показать это. Возможно, вам понадобится какое-то расширение, чтобы снова удалить обратные вызовы (отмена регистрации). - person hakre; 27.07.2011
comment
Каков хороший подход, чтобы остановить запуск следующего события, скажем, если я выведу ответ или что-то в этом роде? - person John; 21.03.2014
comment
@John: Определите для хуков, чтобы они не открывали побочный канал, такой как вывод. В качестве альтернативы PHP-стиль отбрасывает весь вывод, захватывая его, и превращает его в фатальную ошибку. Хуки должны возвращаться через определенные структуры, а не через побочный канал, иначе вы столкнетесь с общими проблемами глобального статического состояния, которые обычно пытаетесь предотвратить. - person hakre; 24.03.2014
comment
Как мне внедрить в событие такие вещи, как объекты? - person John; 02.04.2014
comment
@John: взгляните на call_user_func_array() вместо call_user_func() - он позволяет передавать массив из нуля или более параметров . Также есть func_get_args() - person hakre; 03.04.2014

Для PHP я регулярно интегрировал компонент событий Symfony: http://components.symfony-project.org/event-dispatcher/.

Вот небольшой пример ниже, который вы можете найти в развернутом виде в разделе рецептов Symfony..

<?php

class Foo
{
  protected $dispatcher = null;

    // Inject the dispatcher via the constructor
  public function __construct(sfEventDispatcher $dispatcher)
  {
    $this->dispatcher = $dispatcher;
  }

  public function sendEvent($foo, $bar)
  {
    // Send an event
    $event = new sfEvent($this, 'foo.eventName', array('foo' => $foo, 'bar' => $bar));
    $this->dispatcher->notify($event);
  }
}


class Bar
{
  public function addBarMethodToFoo(sfEvent $event)
  {
    // respond to event here.
  }
}


// Somewhere, wire up the Foo event to the Bar listener
$dispatcher->connect('foo.eventName', array($bar, 'addBarMethodToFoo'));

?>

Это система, которую мы интегрировали в корзину для покупок, чтобы создать игровой процесс покупки, привязывая действия пользователя к игровым событиям. Когда пользователь выполнял определенные действия, php запускал события, вызывающие выдачу вознаграждений.

Пример 1: если пользователь нажал определенную кнопку 10 раз, он получил звезду.

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

person bejonbee    schedule 27.07.2011

Ознакомьтесь с CodeIgniter, так как в нем есть встроенные хуки.

Просто включите хуки:

$config['enable_hooks'] = TRUE;

А затем определите свой хук:

 $hook['post_controller_constructor'] = array(
                                'class'    => 'Hooks',
                                'function' => 'session_check',
                                'filename' => 'hooks.php',
                                'filepath' => 'hooks',
                                'params'   => array()
                                ); 

Затем используйте его в своем классе:

<?php

    class Hooks {
        var $CI;

        function Hooks() {
            $this->CI =& get_instance();
        }

        function session_check() {
            if(!$this->CI->session->userdata("logged_in") && $this->CI->uri->uri_string != "/user/login")
                redirect('user/login', 'location');
        }
    }

?> 
person AlienWebguy    schedule 27.07.2011
comment
Хотя я ценю отзывы и обязательно должен проверить CodeIgniter, я больше ищу объяснения того, как писать такие вещи с нуля. Я также ищу хороший справочник по разработке приложений, управляемых событиями, чтобы я мог разработать собственное приложение, управляемое событиями, с моими собственными пользовательскими хуками. - person MoarCodePlz; 27.07.2011
comment
Тратьте свое время на кодирование своей игровой логики, а не на изобретение заново проверенных, настоящих и проверенных людьми колес. Обновил мой ответ примером использования. - person AlienWebguy; 27.07.2011
comment
Хотя я полностью согласен с аргументом не изобретать колесо, я также думаю, что если все, что мне нужно, это очень простая система ловушек, управляемая событиями, то использование внешней библиотеки, которая имеет ряд наворотов, будет худшим подходом, чем написание единственного класс как показал хакре. ЕСЛИ я искал что-то более сложное и надежное, я думаю, что использование внешней библиотеки было бы лучше. - person MoarCodePlz; 27.07.2011
comment
Вы создаете игру и не думаете, что вам понадобится надежный фреймворк с наворотами? OK :) - person AlienWebguy; 27.07.2011
comment
Справедливо. Большая часть фреймворка будет сосредоточена на других вещах, но отчасти я испытываю волнение по поводу этого, работая над моим собственным фреймворком. Хотя это может работать не так хорошо, как проверенные и проверенные фреймворки, я надеюсь получить хороший опыт и понимание. Мне не часто нравится не знать, как работает то, что я использую. - person MoarCodePlz; 27.07.2011
comment
Верно дат. Я знаю это чувство. Следует учитывать, что фреймворки с открытым исходным кодом, управляемые сообществом, всегда будут лучше, чем индивидуальные усилия. Научиться что-то делать так же просто, как взглянуть на исходный код и выполнить его обратное проектирование. Таким образом, вы сможете заложить основу для текущих передовых практик и двигаться дальше. - person AlienWebguy; 27.07.2011
comment
CodeIgniter далек от событийного дизайна. - person sepehr; 12.10.2012