鈎子系統


簡介

Hooks 是一項開發功能,允許開發人員將自定義代碼添加到Dolibarr的標準頁面,而無需修改Dolibarr的核心文件。與鏈接到Dolibarr事件的 觸發系統(與Dolibarr代碼交互的另一種方式)不同,Hooks可以預埋在Dolibarr核心代碼的任何時間、任何地點運行。這些是程序中的插入點(被視為代碼執行從當前頁面「轉移」並運行自定義代碼,然後返回的點)。

每個Hook都有一個名字,例如,代碼:$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action)中的「doActions」;然而,Hook的名稱通常不是唯一的(可能在各種代碼模塊中重複使用),因此為了準確識別位置,代碼模塊具有一個「上下文」名稱,該名稱將被該代碼模塊位置中的所有Hooks引用。因此,要使用Hooks,您需要知道它的上下文名稱(位置)和Hooks名稱。

  • Hook是否處於活動狀態取決於上下文(通常每個模塊有一個上下文:例如,產品模塊的「productcard」,發票模塊的「invoicecard」等等)。要查找現有的Hooks,請在php文件中搜索「initHooks(」。

這將找到諸如「$hookmanager->initHooks(array('thirdpartycomm','globalcard'));」的結果,其中「thirdpartycomm」是上下文名稱

  • Hooks是用於插入或替換標準代碼的函數。要查找可以覆蓋的代碼,請搜索「executeHooks(」。找不到就可能沒有預埋Hook,所以你可以添加自己的Hook(並在Github中提交相應的拉取請求,以便將其包含在未來的版本中)。

這將找到諸如「$reshook = $hookmanager->executeHooks('addMoreBoxStatsCustomer', $parameters, $object, $action);」的結果,其中「addMoreBoxStatsCoustomer」是Hook名稱。

添加/預埋hook點以允許插入代碼

要在您自己的模塊中預埋hook(以便您的模塊可以被其他模塊「掛接」),您需要執行兩個步驟。

這些步驟必須為模塊中要預埋hook的每個PHP腳本複製。當然,這也是hook在每個核心Dolibarr模塊中實現的方式。

1 - 初始化HookManager對象

對於一個頁面,將這段代碼放在PHP腳本的開頭(在 main 的 include 之後):

// Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array
include_once(DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php');
$hookmanager=new HookManager($db);
$hookmanager->initHooks(array('context'));

$hookmanager->initHooks() 接受1個參數(上下文數組),並啟用此腳本的鈎子支持。

- 'context' 是包含執行上下文的字符串。這是一個簡單的指示符,鈎子函數可以使用它來檢測何時調用它們(多個頁面/模塊可以在不同的位置調用同一個鈎子,而一個鈎子函數可能只想為一個給定的上下文運行,而不想為其他上下文運行)。

注意:您可以同時放置多個上下文(例如,如果您需要多個頁面共享一個上下文,但也需要特定於給定頁面的上下文)。

對於方法或函數,可以通過以下方式獲取鈎子管理器:

global $hookmanager;

2 - 然後將

$parameters=array();
$reshook=$hookmanager->executeHooks('hookname',$parameters,$object,$action); // See description below
// Note that $action and $object may have been modified by hook
if (empty($reshook))
{
   ... // standard code that can be disabled/replaced by hook if return code > 0.
}

調用放置在允許添加代碼的位置:

$parameters=array();
$reshook=$hookmanager->executeHooks('hookname',$parameters,$object,$action); // See description below
// Note that $action and $object may have been modified by hook
if (empty($reshook))
{
   ... // standard code that can be disabled/replaced by hook if return code > 0.
}

$hookmanager->executeHooks() 接受4個參數並添加鈎子(這是腳本和模塊外部函數的腳本入口點):

- 'hookname' 是將被調用的hook的名稱(可以是您想要的任何內容,也可以遵循 Dolibarr 的命名法,查看下面的鈎子列表)。例如:「formObjectOptions」

- $parameters 是一個自定義數組,用於向鈎子傳遞更多自定義數據(鈎子中的函數可以處理這些數據)。把你想要的放在這裡,它可以是一個文件、一個字符串數組、或任何東西...

例如:

$parameters=array('file'=>'my/path/to/a/file', 'customnames'=>array('henry','david','john'));

- $object 是您要傳遞給鈎子函數的對象,通常是當前模塊的數據(例如,如果您在發票模塊中,則是發票對象等)。它可以是您想要的任何東西,但請記住,它將是鈎子函數使用的主要組件。

- $action 是一個指示當前操作的字符串(可以是null或類似於「create」或「edit」的內容)。

注意:如果要在腳本的不同位置添加多個鈎子,則需要多次重複此步驟。

現在您的模塊應該可以被掛接了,您可以按照下面的實現鈎子中的過程來實現一個鈎子函數,該函數將利用您剛才添加的hook(還可以測試它是否有效)。

實現Hook

要使用鈎子(即添加或覆蓋部分代碼),必須首先定義模塊描述符(請參閱模塊開發#創建模塊描述符(必做))。然後,您必須執行以下步驟:

1 - 將模塊添加到鈎子應該運行的上下文中。這意味着,在給定的上下文中,將調用您的代碼。為此,請編輯模塊的描述符(/htdocs/yourmodulename/core/modules/modYourModuleName.class.php),並輸入變量$this->module_parts,如示例所示:

$this->module_parts = array(
'hooks' => array('hookcontext1','hookcontext2')  // Set here all hooks context you want to support
);

不要忘記將 YourModuleName 更改為您自己的模塊名稱!

注意:可以通過添加以下內容來查找模塊的上下文:

print('Module context: '.$object->context);

(將這段代碼添加到鈎子調用所在的PHP文件中,並在檢索到上下文值後將其刪除)。

  警告:不要忘記在模塊管理界面中禁用和重新激活模塊,以便使更改生效。因為當您將新的上下文名稱添加到

$this->module_parts = array(
'hooks' => array('hookcontext1','hookcontext2')  // Set here all hooks context you want to support
);

時,此上下文列表必須存儲在數據庫中。僅當啟用自定義模塊時,才會發生這種情況。 因此,當您添加/刪除/修改/重命名任何上下文名稱時,您必須禁用並啟用自定義模塊才能使更改生效。

2 - 用您的函數替換現有函數(重載)

在模塊中創建 /htdocs/yourmodulename/class/actions_yourmodulename.class.php,代碼包含鈎子調用的方法(該方法的名稱在調用executeHooks時可見)。下面是一個例子:

class ActionsYourModuleName
{ 
	/**
	 * Overloading the doActions function : replacing the parent's function with the one below
	 *
	 * @param   array()         $parameters     Hook metadatas (context, etc...)
	 * @param   CommonObject    &$object        The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...)
	 * @param   string          &$action        Current action (if set). Generally create or edit or null
	 * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook
	 * @return  int                             < 0 on error, 0 on success, 1 to replace standard code
	 */
	function doActions($parameters, &$object, &$action, $hookmanager)
	{
		$error = 0; // Error counter
		$myvalue = 'test'; // A result value

		print_r($parameters);
		echo "action: " . $action;
		print_r($object);

		if (in_array('somecontext', explode(':', $parameters['context'])))
		{
		  // do something only for the context 'somecontext'
		}

		if (! $error)
		{
			$this->results = array('myreturn' => $myvalue);
			$this->resprints = 'A text to show';
			return 0; // or return 1 to replace standard code
		}
		else
		{
			$this->errors[] = 'Error message';
			return -1;
		}
	}
}

然後,在調用包含executeHooks的代碼時,將自動調用該方法,executeHooks為您的代碼提供參數 $parameters、$Object和$action。

參數:

  • $parameters 是一個集成鈎子數據的元數據數組(其上下文可通過 $parameters['context'] 訪問,但根據具體情況,其他信息可能可用)。
  • $object 是您要處理的對象(例如,productcard上下文中的product)
  • $action 指定要執行的操作(例如 "create", "edit" or "view")。
  • $hookmanager 傳遞它只是為了讓您的鈎子可以調用其他鈎子。

返回值:

  • 執行成功時掛鈎的返回代碼必須為0或1;錯誤時為負數。

返回0。如果後續的核心模塊代碼被包裹於 if (empty($reshook)) {...},它將正常執行,但數據由自定義函數修改。

返回1。如果後續的核心模塊代碼被包裹於 if (empty($reshook)) {...},它根本不會執行。這意味着你的鈎子所做的完全取代了Dolibarr在調用鈎子後所做的。

返回負數。則可以通過設置 $this->errors[]='Error message to report to user' 向用戶提供錯誤消息。

  • 如果該方法將一個數組賦給屬性 $this->results,則數組 $hookManager->resArray 將自動加載該數組的內容,以便以後重用。
  • 如果該方法將一個字符串賦給屬性 $this->resprints ,則該字符串將在方法退出時由掛接處理程序(executeHook)打印出來。
  • 您的鈎子還可以更改$object和$action的值。

Dolibarr中可用的Hooks清單

要查找Dolibarr中可用的掛鈎點,只需在源代碼中搜索「executeHooks(」即可,您將很容易找到所有已預埋的掛鈎點。

以下是一個可用的掛鈎點清單(不完整):Category:Hooks...

請注意:此清單會隨着版本升級而不斷增長,因此如果您真的想知道是否存在特定的鈎子或上下文,請使用上面列出的方法直接搜索源代碼。

Dolibarr中可用的上下文清單

要查找Dolibarr中可用的上下文,查找過程類似於查找掛鈎點。

在源代碼中搜索「initHooks(」,您將很容易找到所有已實現的上下文。

這是其中的一小部分(不完整):

adherents\card.php(111): membercard
adherents\type.php(73): membertypecard
categories\categorie.php(96): categorycard
comm\card.php(72): commcard
comm\propal.php(99): propalcard
comm\action\card.php(85): actioncard
comm\action\index.php(112): agenda
comm\mailing\card.php(55): mailingcard
commande\card.php(93): ordercard
compta\facture.php(105): invoicecard
compta\paiement.php(70): paiementcard
compta\deplacement\card.php(50): tripsandexpensescard
compta\dons\card.php(53): doncard
compta\localtax\clients.php(172): externalbalance
compta\salaries\card.php(47): salarycard
compta\tva\card.php(45): taxvatcard
contact\card.php(77): contactcard
contrat\card.php(70): contractcard
expedition\card.php(85): expeditioncard
fichinter\card.php(80): interventioncard
fourn\card.php(54): suppliercard
fourn\commande\card.php(80): ordersuppliercard
fourn\commande\orderstoinvoice.php(88): orderstoinvoicesupplier
fourn\facture\card.php(72): invoicesuppliercard
fourn\facture\paiement.php(71): paymentsupplier
livraison\card.php(68): deliverycard
product\card.php(91): productcard
product\composition\card.php(55): productcompositioncard
product\fournisseurs.php(62): pricesuppliercard
product\stats\commande.php(45): productstatsorder
product\stats\commande_fournisseur.php(45): productstatssupplyorder
product\stats\contrat.php(45): productstatscontract
product\stats\facture.php(48): productstatsinvoice
product\stats\facture_fournisseur.php(47): productstatssupplyinvoice
product\stats\propal.php(45): productstatspropal
product\stock\card.php(54): warehousecard
projet\card.php(48): projectcard
projet\tasks.php(67): projecttaskcard
resource\card.php(60): resource_card
resource\element_resource.php(58): element_resource
societe\agenda.php(41): agendathirdparty
societe\commerciaux.php(40): salesrepresentativescard
societe\consumption.php(80): consumptionthirdparty
societe\info.php(41): infothirdparty
societe\soc.php(80): thirdpartycard
user\card.php(93): usercard
user\list.php(72): userlist
user\passwordforgotten.php(56): passwordforgottenpage
...

請注意:此清單會隨着版本升級而不斷增長,因此如果您真的想知道是否存在特定的鈎子或上下文,請使用上面列出的方法直接搜索源代碼。

參考