Лучший способ разрешить плагин для приложения PHP

голоса
245

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

Как можно идти о написании «крючки» в свой код так, чтобы модули можно прикрепить к конкретным событиям?

Задан 01/08/2008 в 13:50
источник пользователем
На других языках...                            


8 ответов

голоса
14

Я считаю, что самый простой способ будет следовать собственному совету Джеффа и посмотреть вокруг существующего кода. Попробуйте посмотреть на Wordpress, Drupal, Joomla и другие хорошо известны PHP на основе CMS, чтобы увидеть, как их API крючки выглядеть и чувствовать себя. Таким образом, вы можете даже получить идеи, которые вы, возможно, не думали раньше, чтобы сделать вещи немного более rubust.

Более прямой ответ будет писать общие файлы, которые они «include_once» в свой файл, которые обеспечат удобство и простоту использования, они должны были бы. Это было бы разбить на категории и не предусмотрено в одном файле MASSIVE «hooks.php». Будьте осторожны, потому, что в конечном итоге происходит в том, что файлы, которые они включают в конечном итоге, все больше и больше зависимостей и функциональность улучшается. Старайтесь держать зависимостей API низким. IE меньше файлов для их включить.

Ответил 01/08/2008 в 14:44
источник пользователем

голоса
148

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

<?php

/** Plugin system **/

$listeners = array();

/* Create an entry point for plugins */
function hook() {
    global $listeners;

    $num_args = func_num_args();
    $args = func_get_args();

    if($num_args < 2)
        trigger_error("Insufficient arguments", E_USER_ERROR);

    // Hook name should always be first argument
    $hook_name = array_shift($args);

    if(!isset($listeners[$hook_name]))
        return; // No plugins have registered this hook

    foreach($listeners[$hook_name] as $func) {
        $args = $func($args); 
    }
    return $args;
}

/* Attach a function to a hook */
function add_listener($hook, $function_name) {
    global $listeners;
    $listeners[$hook][] = $function_name;
}

/////////////////////////

/** Sample Plugin **/
add_listener('a_b', 'my_plugin_func1');
add_listener('str', 'my_plugin_func2');

function my_plugin_func1($args) {
    return array(4, 5);
}

function my_plugin_func2($args) {
    return str_replace('sample', 'CRAZY', $args[0]);
}

/////////////////////////

/** Sample Application **/

$a = 1;
$b = 2;

list($a, $b) = hook('a_b', $a, $b);

$str  = "This is my sample application\n";
$str .= "$a + $b = ".($a+$b)."\n";
$str .= "$a * $b = ".($a*$b)."\n";

$str = hook('str', $str);
echo $str;
?>

Вывод:

This is my CRAZY application
4 + 5 = 9
4 * 5 = 20

Заметки:

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

Это только один способ реализации системы плагинов в PHP. Есть лучшие альтернативы, я предлагаю вам проверить документацию WordPress для получения дополнительной информации.

К сожалению, он появляется подчеркивание символов заменяются HTML сущностей с помощью Markdown? Я могу повторно разместить этот код, когда эта ошибка будет исправлена.

Edit: Nevermind, он появляется только таким образом, когда вы редактируете

Ответил 01/08/2008 в 14:46
источник пользователем

голоса
31

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

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

+1 к kdeloach от меня.

Ответил 01/08/2008 в 18:23
источник пользователем

голоса
13

Там аккуратный проект под названием Колюшка Мэтт Зандстр на Yahoo , который обрабатывает большую часть работы для обработки плагин в PHP.

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

Ответил 17/09/2008 в 20:00
источник пользователем

голоса
10

Хороший совет , чтобы посмотреть , как другие проекты сделали это. Многие называют за то, что установленные плагин и их «имя» зарегистрирован за услуги (например , WordPress делает) , поэтому у вас есть «точка» в вашем коде , где вы вызываете функцию , которая идентифицирует зарегистрированные слушатель и выполняет их. Стандартный дизайн OO скороговорка является шаблоном Observer , который был бы хорошим вариантом для реализации в истинно объектно - ориентированной системе PHP.

Zend Framework использует множество методов крюковых, и очень хорошо спроектирован. Это будет хорошая система , чтобы посмотреть.

Ответил 17/09/2008 в 20:38
источник пользователем

голоса
19

Вот подход, который я использовал, это попытка скопировать из Qt механизма сигналов / слотов, вид шаблона Observer. Объекты могут излучать сигналы. Каждый сигнал имеет идентификатор в системе - он состоит по имени идентификатор + объект отправителя Каждого сигнал может быть привязан к какому-приемникам, который просто является «вызываемым» используется класс шины для передачи сигналов на всех, кто заинтересован в получении их когда-то бывает, «послать» сигнал. Ниже и пример реализации

    <?php

class SignalsHandler {


    /**
     * hash of senders/signals to slots
     *
     * @var array
     */
    private static $connections = array();


    /**
     * current sender
     *
     * @var class|object
     */
    private static $sender;


    /**
     * connects an object/signal with a slot
     *
     * @param class|object $sender
     * @param string $signal
     * @param callable $slot
     */
    public static function connect($sender, $signal, $slot) {
        if (is_object($sender)) {
            self::$connections[spl_object_hash($sender)][$signal][] = $slot;
        }
        else {
            self::$connections[md5($sender)][$signal][] = $slot;
        }
    }


    /**
     * sends a signal, so all connected slots are called
     *
     * @param class|object $sender
     * @param string $signal
     * @param array $params
     */
    public static function signal($sender, $signal, $params = array()) {
        self::$sender = $sender;
        if (is_object($sender)) {
            if ( ! isset(self::$connections[spl_object_hash($sender)][$signal])) {
                return;
            }
            foreach (self::$connections[spl_object_hash($sender)][$signal] as $slot) {
                call_user_func_array($slot, (array)$params);
            }

        }
        else {
            if ( ! isset(self::$connections[md5($sender)][$signal])) {
                return;
            }
            foreach (self::$connections[md5($sender)][$signal] as $slot) {
                call_user_func_array($slot, (array)$params);
            }
        }

        self::$sender = null;
    }


    /**
     * returns a current signal sender
     *
     * @return class|object
     */
    public static function sender() {
        return self::$sender;
    }

}   

class User {

    public function login() {
        /**
         * try to login
         */
        if ( ! $logged ) {
            SignalsHandler::signal(this, 'loginFailed', 'login failed - username not valid' );
        }
    }

}

class App {
    public static function onFailedLogin($message) {
        print $message;
    }
}


$user = new User();
SignalsHandler::connect($user, 'loginFailed', array($Log, 'writeLog'));
SignalsHandler::connect($user, 'loginFailed', array('App', 'onFailedLogin'));

$user->login();

?>
Ответил 25/09/2008 в 22:29
источник пользователем

голоса
51

Так скажем , вы не хотите , шаблон Observer , потому что она требует , чтобы вы изменили свои методы класса , чтобы справиться с задачей слушания, и хочется чего - то общего. И предположим, что вы не хотите использовать extendsнаследование , потому что вы , возможно , уже наследующий в своем классе от некоторого другого класса. Не было бы здорово иметь общий способ сделать любой класс подключаемым без особых усилий ? Вот как:

<?php

////////////////////
// PART 1
////////////////////

class Plugin {

    private $_RefObject;
    private $_Class = '';

    public function __construct(&$RefObject) {
        $this->_Class = get_class(&$RefObject);
        $this->_RefObject = $RefObject;
    }

    public function __set($sProperty,$mixed) {
        $sPlugin = $this->_Class . '_' . $sProperty . '_setEvent';
        if (is_callable($sPlugin)) {
            $mixed = call_user_func_array($sPlugin, $mixed);
        }   
        $this->_RefObject->$sProperty = $mixed;
    }

    public function __get($sProperty) {
        $asItems = (array) $this->_RefObject;
        $mixed = $asItems[$sProperty];
        $sPlugin = $this->_Class . '_' . $sProperty . '_getEvent';
        if (is_callable($sPlugin)) {
            $mixed = call_user_func_array($sPlugin, $mixed);
        }   
        return $mixed;
    }

    public function __call($sMethod,$mixed) {
        $sPlugin = $this->_Class . '_' .  $sMethod . '_beforeEvent';
        if (is_callable($sPlugin)) {
            $mixed = call_user_func_array($sPlugin, $mixed);
        }
        if ($mixed != 'BLOCK_EVENT') {
            call_user_func_array(array(&$this->_RefObject, $sMethod), $mixed);
            $sPlugin = $this->_Class . '_' . $sMethod . '_afterEvent';
            if (is_callable($sPlugin)) {
                call_user_func_array($sPlugin, $mixed);
            }       
        } 
    }

} //end class Plugin

class Pluggable extends Plugin {
} //end class Pluggable

////////////////////
// PART 2
////////////////////

class Dog {

    public $Name = '';

    public function bark(&$sHow) {
        echo "$sHow<br />\n";
    }

    public function sayName() {
        echo "<br />\nMy Name is: " . $this->Name . "<br />\n";
    }


} //end class Dog

$Dog = new Dog();

////////////////////
// PART 3
////////////////////

$PDog = new Pluggable($Dog);

function Dog_bark_beforeEvent(&$mixed) {
    $mixed = 'Woof'; // Override saying 'meow' with 'Woof'
    //$mixed = 'BLOCK_EVENT'; // if you want to block the event
    return $mixed;
}

function Dog_bark_afterEvent(&$mixed) {
    echo $mixed; // show the override
}

function Dog_Name_setEvent(&$mixed) {
    $mixed = 'Coco'; // override 'Fido' with 'Coco'
    return $mixed;
}

function Dog_Name_getEvent(&$mixed) {
    $mixed = 'Different'; // override 'Coco' with 'Different'
    return $mixed;
}

////////////////////
// PART 4
////////////////////

$PDog->Name = 'Fido';
$PDog->Bark('meow');
$PDog->SayName();
echo 'My New Name is: ' . $PDog->Name;

В части 1, это то, что вы можете включить с require_once()заходом в верхней части вашего PHP скрипта. Он загружает классы , чтобы сделать что - то подключаемым.

В части 2, что, где мы загружаем класс. Обратите внимание, что я не должен делать ничего особенного к классу, который значительно отличается от шаблона Observer.

В части 3, что, где мы включаем наш класс вокруг в бытие «подключаемым» (то есть, поддерживает плагины , которые позволяют нам переопределить методы класса и свойства). Так, например, если у вас есть веб - приложение, вы можете иметь реестр плагин, и вы можете активировать плагин здесь. Обратите внимание также Dog_bark_beforeEvent()функцию. Если я ставлю $mixed = 'BLOCK_EVENT'перед обратным утверждением, он будет блокировать собаку от лая , а также будет блокировать Dog_bark_afterEvent , потому что не будет какое - либо событие.

В части 4, это нормальный код операции, но обратите внимание, что вы могли бы думать, работать не работает, как это вообще. Например, собака не объявляет его имя как «Fido», а «Коко». Собака не говорит «мяу», а «Гав». И если вы хотите взглянуть на имя собаки после этого, вы найдете это «Different» вместо «Коко». Все эти переопределяет были представлены в Части 3.

Так, как это работает? Ну, давайте исключаем eval()(что все говорят , что это «зло») и исключает, что это не шаблон Observer. Так, как это работает это подлый пустой класс называется вставные, который не содержит свойства и методы , используемые классом Dog. Таким образом, так как это происходит, методы магии будут участвовать для нас. Вот почему в частях 3 и 4 мы бардак с объектом , полученным из Pluggable класса, а не сам класс Dog. Вместо этого мы позволяем Plugin класса сделать «трогательный» на объекте Собака для нас. (Если это какой - то шаблон проектирования я не знаю , о - пожалуйста , дайте мне знать.)

Ответил 01/06/2009 в 06:59
источник пользователем

голоса
7

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

А что, если вы хотите плагин работать на другом - дистанционный - сервере? Лучший способ сделать это было бы предоставить форму, которая позволяет определять различные URL-адреса, которые будут вызываться, когда определенные события происходят в вашем приложении.

Различные мероприятия будут посылать различную информацию, основанную на том случае, если только произошло.

Таким образом, вы бы просто выполнить Curl вызов на URL, которая была представлена ​​в приложении (например, через протокол HTTPS), где удаленные серверы могут выполнять задачи на основе информации, которая была отправлена ​​приложением.

Это дает два преимущества:

  1. Вы не должны принять любой код на локальном сервере (безопасность)
  2. Код может быть на удаленных серверах (расширяемость) в разных языках, то PHP (портативность)
Ответил 22/04/2013 в 08:41
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more