Najlepszy sposób, aby umożliwić wtyczek dla aplikacji PHP

głosy
245

Zaczynam nową aplikację internetową w PHP i tym razem chcę stworzyć coś, co ludzie mogą przedłużyć za pomocą interfejsu wtyczki.

W jaki sposób można przejść o pisaniu „haczyki” w kodzie, dzięki czemu można podłączyć do wtyczki określonych zdarzeń?

Utwórz 01/08/2008 o 13:50
źródło użytkownik
W innych językach...                            


8 odpowiedzi

głosy
14

Wierzę, najprostszym sposobem byłoby śledzić własne porady Jeffa i spojrzeć wokół istniejącego kodu. Spróbuj patrząc na Wordpress, Drupal, Joomla i inne dobrze znane PHP CMS opartych aby zobaczyć, jak ich haki API wyglądać i czuć. W ten sposób można nawet dostać pomysłów może nie pomyśleli o uprzednio do rzeczy trochę więcej rubust.

Bardziej bezpośrednią odpowiedzią byłoby napisać ogólne pliki oni „include_once” do swojego pliku, który zapewni użyteczność oni potrzebują. To będzie podzielone na kategorie i nie zostały przekazane w jednym MASSIVE „hooks.php” pliku. Bądź jednak ostrożny, bo to, co kończy się dzieje, że pliki, które zawierają one skończyć się coraz więcej zależności i poprawia funkcjonalność. Spróbuj utrzymać zależności API niska. IE mniej pliki do nich dołączyć.

Odpowiedział 01/08/2008 o 14:44
źródło użytkownik

głosy
148

Można użyć wzoru obserwatora. Prosty sposób funkcjonalny do osiągnięcia tego celu:

<?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;
?>

Wydajność:

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

Uwagi:

W tym przykładzie kodu źródłowego, należy zadeklarować wszystkie swoje wtyczki przed właściwym kodem źródłowym, które mają być wysuwane. Podaję przykład, jak obsługiwać pojedyncze lub wielokrotne wartości są przekazywane do wtyczki. Najtrudniejszą częścią tego jest pisanie rzeczywistą dokumentację który wymienia jakie argumenty przejdzie do każdego haka.

To jest tylko jeden sposób realizacji systemu wtyczek w PHP. Istnieją lepsze alternatywy, proponuję zapoznać się z dokumentacja WordPress, aby uzyskać więcej informacji.

Niestety, wydaje się, znaków podkreślenia są zastąpione przez podmioty HTML przez Markdown? Mogę ponownie pisać ten kod kiedy ten błąd zostanie naprawiony.

Edycja: Nevermind, tylko wydaje się, że sposób, gdy edytujesz

Odpowiedział 01/08/2008 o 14:46
źródło użytkownik

głosy
31

Hak i słuchacz metoda jest najbardziej powszechnie używane, ale są jeszcze inne rzeczy, które możesz zrobić. W zależności od wielkości aplikacji, a kto swój zamiar pozwalają zobaczyć kod (czy to będzie skrypt FOSS, lub coś w domu) wpłynie znacząco jak chcesz zezwolić wtyczek.

kdeloach ma ładny przykład, ale jego realizacja i funkcja hak jest trochę niebezpieczne. Prosiłbym, aby dać więcej informacji o charakterze php app swoim piśmie, a jak widać w montażu wtyczek.

+1 do kdeloach ode mnie.

Odpowiedział 01/08/2008 o 18:23
źródło użytkownik

głosy
13

Jest schludny projekt o nazwie ciernika Matt Zandstra na Yahoo, który obsługuje wiele pracy do obsługi wtyczek w PHP.

To wymusza interfejs klasy wtyczki, obsługuje interfejs wiersza poleceń i nie jest zbyt trudne, aby wstać i działa - zwłaszcza jeśli czytasz tytułową historię o tym w czasopiśmie PHP architekta .

Odpowiedział 17/09/2008 o 20:00
źródło użytkownik

głosy
10

Dobra rada jest patrzeć, jak inne projekty to zrobić. Wiele połączeń za posiadanie zainstalowanych wtyczek i ich „nazwa” zarejestrowany dla usług (takich jak WordPress robi), więc trzeba „punkty” w kodzie gdzie można wywołać funkcję identyfikującą zarejestrowanych słuchaczy i wykonuje je. Standardowa konstrukcja OO tupot jest Wzorzec Obserwator , który byłby dobrym rozwiązaniem, aby wdrożyć w prawdziwie obiektowego systemu PHP.

Zend Framework umożliwia korzystanie z wielu metod zahaczających i jest bardzo ładnie architekturę. To byłby dobry system patrzeć.

Odpowiedział 17/09/2008 o 20:38
źródło użytkownik

głosy
19

Oto podejście używałem, to próba kopiowania z Qt sygnały / sloty mechanizmu, rodzajem wzorca Observer. Przedmioty mogą emitować sygnały. Każdy sygnał ma identyfikator w systemie - to składa się z nazwy id + dopełnienie nadawcy każdy sygnał może być zbindowanych do odbiorników, które po prostu jest „wpłacone” użyć klasy autobusów przekazać sygnały nikomu zainteresowane otrzymywaniem je Gdy coś się dzieje, „wyślij” sygnału. Poniżej jest przykład wdrożenia i

    <?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();

?>
Odpowiedział 25/09/2008 o 22:29
źródło użytkownik

głosy
51

Więc powiedzmy, że nie chcesz wzorca Observer, ponieważ wymaga, aby zmienić swoje metody klasy do obsługi zadanie słuchania, i chcesz coś rodzajowe. I powiedzmy, że nie chce korzystać z extendsdziedziczenia, ponieważ może być już w klasie dziedziczy z innej klasy. Czy nie byłoby wspaniale mieć ogólny sposób, aby każda klasa wtykowy bez większego wysiłku ? Oto jak:

<?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;

W części 1, to co można zawierać z require_once()połączeniem na górze skryptu PHP. Ładuje klas, aby coś wtykowy.

W części 2, to gdzie my załadować klasę. Zauważ, że nie trzeba robić nic specjalnego do klasy, która jest znacznie różni się od wzorca Observer.

W części 3, to gdzie możemy przełączyć naszą klasę wokół powstają „wtykowy” (czyli obsługuje wtyczki, które pozwalają nam przesłonić metody klasy i właściwości). Tak więc, na przykład, jeśli masz aplikację, użytkownik może mieć rejestru wtyczki, i można włączyć wtyczek tutaj. Zauważ również Dog_bark_beforeEvent()funkcję. Jeżeli ustawić $mixed = 'BLOCK_EVENT'przed instrukcji return, będzie blokować psa ze szczekaniem i będzie również blokować Dog_bark_afterEvent bo nie byłoby żadnego zdarzenia.

W części 4, to normalny kod operacji, ale zauważ, że to, co można by pomyśleć, byłoby to nie działa w ten sposób w ogóle. Na przykład, pies nie ogłosić jego nazwę jako „Fido”, ale „Coco”. Pies nie mówi „meow”, ale „Hau”. I kiedy chcesz spojrzeć na imię psa potem się okazać, że jest „inny” zamiast „Coco”. Wszystkie te nadpisania zostały dostarczone w części 3.

Więc jak to działa? Cóż, wykluczyć eval()(co każdy mówi, że jest „zła”) oraz wykluczyć, że nie jest to wzorzec Obserwator. Tak, tak to działa to podstępne pusty klasy o nazwie Pluggable, który nie zawiera metody i właściwości używanych przez klasę Dog. Zatem, ponieważ występuje, metody magiczne będzie angażować się dla nas. Dlatego w części 3 i 4 my bałagan z obiektem pochodzącym z podłączany klasie, klasa sama w sobie nie jest pies. Zamiast tego niech Plugin klasa wykonać „dotykania” na obiekcie psem dla nas. (Jeśli to jest jakiś rodzaj wzorca projektowego nie wiem o - proszę dać mi znać.)

Odpowiedział 01/06/2009 o 06:59
źródło użytkownik

głosy
7

Jestem zaskoczony, że większość odpowiedzi tutaj wydają się być nastawione na temat wtyczek, które są lokalne dla aplikacji internetowych, czyli wtyczek, które działają na lokalnym serwerze WWW.

A co, jeśli chciał wtyczki do uruchomienia na innym - zdalny - serwer? Najlepszym sposobem na to byłoby dostarczyć formularz, który pozwala na zdefiniowanie różnych adresów URL, które byłyby nazywane gdy poszczególne zdarzenia w aplikacji.

Różne wydarzenia będzie wysłać różne informacje w oparciu o zdarzenia, które właśnie powstały.

W ten sposób, to po prostu wykonać połączenia cURL do adresu URL, który został dostarczony do aplikacji (na przykład za pośrednictwem protokołu HTTPS), gdzie serwery zdalne mogą wykonywać zadania na podstawie informacji, które zostały wysłane przez aplikację.

Zapewnia to dwie korzyści:

  1. Nie masz gospodarzem dowolnego kodu na lokalnym serwerze (bezpieczeństwo)
  2. Kod może być na serwerach zdalnych (rozciągliwości) w różnych językach innych niż PHP (przenośność)
Odpowiedział 22/04/2013 o 08:41
źródło użytkownik

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