Wczoraj pisałem o dodaniu do Wego CMS wtyczek dla systemów płatności online. Cypherq poprosił o szczegóły dotyczące implementacji. Noszę się z zamiarem przygotowania kilkuetapowego howto, kiedy tylko pojawi się darmowa wersja Wego (tak, to bardzo możliwe, po szczegóły odsyłam do nbw). Póki co, o samym sposobie ładowania wtyczek.

Moduły — wtyczki z automatyczną rejestracją

Wego ma dwa rodzaje pluginów. Pierwszy rodzaj — wtyczki z automatyczną rejestracją — używany jest przez moduły. Moduł to zamknięta część systemu, widoczna jako osobna pozycja w panelu. Przykładem niech będą Artykuły lub Kosz.

Automatyczna rejestracja oznacza tyle, że plik z wtyczką zawiera klasę o nazwie takiej samej, jak plik i nie musi właściwie robić nic więcej:

<?php

/* $Id: foo.module.php 1 2007-04-27 12:00:00Z patrys $ */

require_once(THE_ROOT . '/class/genericModule.class.php');

/**
 * Foo module
 */

class fooModule extends genericModule
{
	public function __construct(&amp; $modSys)
	{
		parent::__construct($modSys);
		$this->module = 'foo';
	}

	/**
	 * Check if module is usable (dependencies, etc.)
	 */
	protected function __public_init($data)
	{
		return array('success' => true);
	}

	/**
	 * Get module info
	 */
	public function getInfo()
	{
		return array
		(
			'title' => _('Foo'),
			'author' => 'patrys [ patrys at icenter pl ]',
			'version' => '1.0',
			'description' => _('Hello world example'),
			'modified' => filemtime(__FILE__)
		);
	}
}

?>

Do tego dochodzi klasa dla panelu administracyjnego (dla oszczędzenia pamięci, ładowana tylko w przypadku, gdy jest to aktywny moduł w panelu):

<?php

/* $Id: foo.panel.module.php 1 2007-04-27 12:00:00Z patrys $ */

class fooPanelModule extends fooModule
{
	protected function __internal_list()
	{
		return 'Hello world!';
	}
}

?>

Jest tu kilka magicznych metod — wszystkie __public_* dostępne są automatycznie z poziomu inych modułów i z poziomu szablonów Smarty; __internal_* za to służą za obsługę ekranów panelu administracyjnego, wymagana jest przynajmniej obsługa metody list, która jest domyślnym widokiem.

Inne wtyczki — ręczna rejestracja

W przypadku wtyczek pól dodatkowych i systemów płatności okazało się, że automatyczna rejestracja nie jest najlepszym wyjściem — jedna klasa może realizować kilka funkcji, a sztuczne po niej dziedziczenie wprowadza tylko chaos w plikach. Zdecydowaliśmy się, że moduły te same muszą zgłosić obsługiwane przez siebie funkcje:

<?php

/* $Id: platnosciPayment.class.php 7234 2007-04-25 10:55:39Z patrys $ */

class platnosciPaymentType extends genericPaymentType
{
	public static $paymentList = array
	(
		'platnosci_test' => array
		(
			'code' => 't',
			'name' => 'Platnosci.pl (płatność testowa)',
		),
		'platnosci_mtransfer' => array
		(
			'code' => 'm',
			'name' => 'Platnosci.pl - mBank mTransfer',
		),
		// [...]
		'platnosci_credit' => array
		(
			'code' => 'c',
			'name' => 'Platnosci.pl - Karta kredytowa',
		),
	);

	public function getConfigData()
	{
		// [...]
	}

	public function process()
	{
		// [...]
	}
}

$parent = paymentSystem::getInstance();
foreach (platnosciPaymentType::$paymentList as $key => $val)
	$parent->register($key, 'platnosciPaymentType', $val['name']);
$parent->registerSettings('platnosci', 'platnosciPaymentType', _('Platnosci.pl'));

?>

Dzięki temu możliwa jest obsługa wszystkich sposobów zapłaty za pomocą jednej klasy (konstruktor klasy genericPaymentType otrzymuje i zapisuje wybraną formę zapłaty).

Ładowanie wtyczek

Tutaj poszliśmy na łatwiznę:

public function loadPlugins()
{
	$modDir = dir(THE_ROOT . '/payment/');

	while (($file = $modDir->read()) !== false)
	{
		$fullPath = THE_ROOT . '/payment/' . $file;

		if (!is_dir($fullPath))
		{
			include($fullPath);
		}
	}
}

Ładowanie wtyczek z automatyczną rejestracją wymaga nieco więcej zachodu (po załadowaniu pliku, trzeba jeszcze sprawdzić, czy istnieje klasa o odpowiedniej nazwie i czy dziedziczy po odpowiedniej klasie bazowej), ale samodzielna implementacja nie powinna zająć więcej niż 5 minut.

Na koniec

Po notce o zmianie firmy pojawiły się niejasne spekulacje, jakobyśmy mieli zastąpić nbw butelką wody mineralnej. Spieszę z dowodem, że młodzieniec ów żyje i ma się dobrze (a jeśli już mielibyśmy kogoś zamienić na butelkę wody, to byłby to prędzej sit0).

The Crew
Ekipa, od lewej:
patrys, sit0, nbw, emes
jarv