引言:
在原文站的 HandBooks 裡面,並沒有 API 這個項目,是一個獨立的"站"。有自己的搜尋引擎,包函了很多很多的函數。
不過有時候看這些的說明的時候,發現看完了還是不知道這個函數在作什麼,或是這個 hook 要怎麼用。和一堆 source code 大眼瞪小眼。
因為工作任務的需要,我必須要去研究 Drupal 的 hook 和函數。把一些我搞懂的函數分享在這裡。也希望其它懂程式,看得懂英文的人加入分享的行列。
因為我能作得不多,也不可能把所有的函數看完。不過我會儘量在翻譯的過程中,也加上一些我所知道可能的用法或範例。
對原文的 API 網站有興趣的話,可以到這裡看看。
這裡的函數主要以 5.0 為主。
drupal_foo的意思,就是drupal很多函數裡面以「drupal_」為開頭名稱的,會以drupal開頭的函數,表示很常被模組的開發者使用,也特別的重要。
幾乎所有的drupal_foo函數,都會在下面這些位置找到。
include/bootstrap.inc
include/common.inc這個單元是許多熱心的貢獻者,將許多drupal_foo函數寫上中文說明,每個drupal_foo的文件說明裡裡通常會包含:定義、描述、參數、傳回值、範例、程式碼...等等。若您也知道一些drupal_foo的用途,或是看到有錯誤的地方,歡迎直接共筆編輯,幫忙加上說明和解釋。
drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE)
includes/common.inc, 第 1600 行之後
加入一個 JavaScript 的檔案、設定、或是程式碼到頁面中
根據不同的參數,會有不同的結果產生。一般,它會加入 JavaScript 的"檔案連結"或是"直接貼入程式碼"到網頁裡。以下列出不同的結果:
drupal_addjs('js1.js', 'module');
drupal_addjs('js2.js', 'core');$data (選擇性) 如果用到這個參數,它的內容的和後面的 $type 參數有關:
$type (選擇性) 一個表示要放到網頁裡的 JavaScript 是哪一種類型。可以是 'core', 'module', 'theme', 'inline' 和 'setting'。你也可以自訂一個名稱,這時 $data 的內容會被視為一個 JavaScript 檔案(*.js)路徑。預設值是 'module'
排列順序: 'core' -> 'module' -> 'theme' -> 'setting' -> 'inline' -> [自訂的名稱]
$scope (選擇性) JavaScript 程式要放置的位置,預設可以是 'header'(開頭) and 'footer'(節尾). 如果你所用的版型有定義其它的位置,也可以使用它們放到這個參數中。
$defer (選擇性) 如果為 TRUE,在 標籤中會加入 defer 屬性,預設為 FALSE。這個參數在 $type 是 'setting' 沒有作用。
$cache (選擇性) 如果設定為 FALSE, JavaScript 檔案會在每一次呼叫頁面的時候重新讀取,預設值是 TRUE,這個參數只對 $type 表示引入的是一個 JavaScript 檔案有用
如果第一個參數為 NULL,就會傳回和 $scope 同樣參數 JavaScript 陣列回來
1. 引入檔案
drupal_add_js('test1.js', 'module');
drupal_add_js('test2.js', 'theme');
drupal_add_js('test3.js', 'core');<script type="text/javascript" src="/drupal/test3.js"></script>
<script type="text/javascript" src="/drupal/test1.js"></script>
<script type="text/javascript" src="/drupal/test2.js"></script>2. 加入程式碼 & 加入設定
drupal_add_js('alert("var1 = "+Drupal.settings.var1);', 'inline');
drupal_add_js(array('var1' => 123, 'var2' => 456), 'setting');<?php
function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE) {
if (!is_null($data)) {
_drupal_add_js('misc/jquery.js', 'core', 'header', FALSE, $cache);
_drupal_add_js('misc/drupal.js', 'core', 'header', FALSE, $cache);
}
return _drupal_add_js($data, $type, $scope, $defer, $cache);
}
?>drupal_get_path($type, $name)
includes/common.inc
可用此函數取得模組、版型或版型引擎(theme engine)的路徑
$type: 要尋找的種類 (如: theme, theme_engine, module)
$name: 尋找東西的名字
要找東西的路徑(網站相對路徑,不是伺服器的系統路徑)
當你開發的模組裡面有圖片,或是由其它需要引入的 JavaScript 的時候,那就很有用了。因為你不能確定你的模組會被放在哪裡。
有可能在 modules/ 下,也可能在 sites/all/modules/ 下面。這時候就需要用它來幫你找出模組的路徑,好引入它資料夾裡面的檔案。
如:
$my_module_path = druapl_get_path('module', 'my_module');
$my_theme_path = druapl_get_path('theme', 'my_theme');
$phptemplate_engine_path = druapl_get_path('theme_engine', 'phptemplate');$my_module_path = base_path() . drupal_get_path("module", "my_module");
// 如果網站是在 drupal 資料夾下, base_path() 會傳回 "/drupal"不過如果使用 drupal_add_js() 函數,因為它本身就會加上網站本身的路徑,所以就不需要加上 base_path()了。
<?php
function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
}
?>drupal_set_message($message = NULL, $type = 'status')
includes/bootstrap.inc
定義一組訊息,以反映剛執行命令的狀態
$message: 要顯示的訊息
$type: 類別,可以是
所有已經定義的訊息
例如要告訴使用者”你己經成功註冊了”:
<?php
drupal_set_message(t('Created a new user account. No e-mail has been sent.'));
?>又例如要顯示一個錯誤:
<?php
drupal_set_message(t('Invalid password.') , 'error' );
?>註:第一個參數可以為html
註:t() 函數指可翻譯的
<?php
function drupal_set_message($message = NULL, $type = 'status') {
if ($message) {
if (!isset($_SESSION['messages'])) {
$_SESSION['messages'] = array();
}
if (!isset($_SESSION['messages'][$type])) {
$_SESSION['messages'][$type] = array();
}
$_SESSION['messages'][$type][] = $message;
}
//如果發生資料庫連接錯誤,則訊息不會被記錄
return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
}
?>drupal_set_title($title = NULL)
includes/path.inc, 第 187 行之後
設定頁面的標題文字(不是網站標題)
$title 選擇性參數,如果為 NULL 則不會改變概有的 標題內容。
沒有傳回值,但會改變當下頁面的標題。
drupal_set_title("新標題");<?php
function drupal_set_title($title = NULL) {
static $stored_title;
if (isset($title)) {
$stored_title = $title;
}
return $stored_title;
}
?>看名字就知道這些函數是用來處理和模組有關的事情。
如果今天開發的模組需要和其它的模組互動,或是"相依"在其它的模組之上,或是要觸發其它模組的 hook 。
那麼這個部分的函數就要花時間去研究。
module_exists($module)
includes/module.inc
判斷某個模組是否存在(並已啟用)
$module: 模組的名字(不要帶上 .module 的副檔名).
如果該名稱的模組已安裝且正被啟用,則傳回 TRUE 。
<?php
function module_exists($module) {
$list = module_list();
return array_key_exists($module, $list);
}
?>user 也是模組之一,管理使用者的登入登出,或是權限的認證。
總之想作好一個有管理使用者的模組,這個部分的函數就必須有所了解。
這些函數不需要特別去引用,直接呼叫函數的名稱就可以了。
user_access($string, $account = NULL)
modules/user/user.module, 第 351 行開始
判斷使用者是否具有某種權限。
所有的權限判斷,都應該要例用這個函數。一來是讓所有的程式有一致性,而且可以保證SuperUser能擁有所有的權限。
$string: 要判定的權限名字,如"administer nodes"
$account (預設): 一個 User 物件。在要判斷的使用者並不是「目前登入的那個人」時使用。
如果判定是「有這個權限」,則傳回布林值 TRUE。
檢查目前使用者是否有「存取管理頁面(administer comments)」的權限
$has_permission = user_access("administer comments"); // 第二個參數省略<?php
function user_access($string, $account = NULL) {
global $user;
static $perm = array();
if (is_null($account)) {
$account = $user;
}
// 超級使用者 SuperUser 擁有所有的權限
if ($account->uid == 1) {
return TRUE;
}
// 為了滅少到資料庫作查詢連線數,會把作查過的資料放入靜態變數中
if (!isset($perm[$account->uid])) {
$result = db_query("SELECT DISTINCT(p.perm) FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (%s)", implode(',', array_keys($account->roles)));
$perm[$account->uid] = '';
while ($row = db_fetch_object($result)) {
$perm[$account->uid] .= "$row->perm, ";
}
}
if (isset($perm[$account->uid])) {
return strpos($perm[$account->uid], "$string, ") !== FALSE;
}
return FALSE;
}
?>user_load($array = array())
modules/user/user.module
取得一個 user 物件
$array 一個關連式陣列,讓這個函數可以找得到你想找的使用者資料,可能是帳號名稱或是e-mail位址。
如果找到的話,傳回一個"完整"的 user 物件,否則傳回 FALSE
其實Drupal 的登入就是靠它來檢查,輸入帳號密碼,來作搜尋有沒有符合的條件。
$account = user_load(array('name' => '納格髓', 'pass' => 'very secret', 'status' => 1))如果有找到,就表示帳號密碼正確,$account 就會傳回一個 user 物件。再下兩行
global $user;
$user = $account; // 把找到的 user 物件"存"起來附帶一提,登出的語法也很簡單。
global $user;
$user = drupal_anonymous_user(); <?php
function user_load($array = array()) {
// Dynamically compose a SQL query:
$query = array();
$params = array();
foreach ($array as $key => $value) {
if ($key == 'uid' || $key == 'status') {
$query[] = "$key = %d";
$params[] = $value;
}
else if ($key == 'pass') {
$query[] = "pass = '%s'";
$params[] = md5($value);
}
else {
$query[]= "LOWER($key) = LOWER('%s')";
$params[] = $value;
}
}
$result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);
if (db_num_rows($result)) {
$user = db_fetch_object($result);
$user = drupal_unpack($user);
$user->roles = array();
if ($user->uid) {
$user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
}
else {
$user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
}
$result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d', $user->uid);
while ($role = db_fetch_object($result)) {
$user->roles[$role->rid] = $role->name;
}
user_module_invoke('load', $array, $user);
}
else {
$user = FALSE;
}
return $user;
}
?>user_roles($membersonly = 0, $permission = 0)
modules/user/user.module, 第 1762 行開始
傳回符合條件的「群組(身分)」陣列
(在 Drupal 裡,群組(身分)被叫作"role")
$membersonly: 如果設為 TRUE ,則傳回的時候會排除「訪客群組(身分)」
$permission: 一個字串,如果加到它,則只傳回包含這個權限的群組(身分)回來
一個關連陣列,鍵是該群組在資料庫的ID,值是它的名字。
列出現在所有的群組: (假設我事前建立一個叫作 power user 的群組)
print_r(user_roles());Array
(
[1] => anonymous user
[2] => authenticated user
[3] => power user
)<?php
function user_roles($membersonly = 0, $permission = 0) {
$roles = array();
if ($permission) {
$result = db_query("SELECT r.* FROM {role} r INNER JOIN {permission} p ON r.rid = p.rid WHERE p.perm LIKE '%%%s%%' ORDER BY r.name", $permission);
}
else {
$result = db_query('SELECT * FROM {role} ORDER BY name');
}
while ($role = db_fetch_object($result)) {
if (!$membersonly || ($membersonly && $role->rid != DRUPAL_ANONYMOUS_RID)) {
$roles[$role->rid] = $role->name;
}
}
return $roles;
}
?>user_save($account, $array = array(), $category = 'account')
modules/user/user.module, 第 106 行開始
更新一個使用者帳號內容,或是建立一個新的帳號
$account 一個 user 物件,如果 $user->uid(帳號 ID)為空,則建立新的帳號,如果有東西的話,就更新這個 uid 的帳號資料
$array 一個帶有帳號資訊的陣列。例如: array('name' => 'My name'); 值為 NULL 的話,就表示把這個欄位清空。
(註: 在更新中,沒有列在這個陣列裡的欄位會保持不變)
$category (選擇性參數) 用來作註記的參數。在這個函數中並沒有用到,主要是用來當參數傳給相關的 hook 函數。
第一個參數要求的是一個 user 物件,但是其實只用到裡面的 uid 屬性。
所以要新增一個帳號的時候,並不用費心去建立一個"空的 user 物件",給它一個 '' (空字串)就好了。
如:
$roles = array(
'3' => '角色名稱',
'4' => '角色名稱',
'5' => '角色名稱',
);
user_save('', array("name" => '納格髓', "pass" => 'unknow', "status" => 1, 'roles' => $roles , 'profile_tel' => '0800 123456'));;註:
在 Drupal 的群組(roles)中,1 表示訪客,2 表示已註冊帳號。這兩個資訊在記錄的時候會"自動跳過"。因為有沒有帳號就已經足以作為這兩者的區分。所以就算你在roles放入 1 或 2 也不會被記入資料庫中。
(原來文章的例子是直接打 array(3, 4, 5) 來加入編號 3, 4, 5的角色,這種寫法是錯誤的)
<?php
function user_save($account, $array = array(), $category = 'account') {
// Dynamically compose a SQL query:
$user_fields = user_fields();
if ($account->uid) {
user_module_invoke('update', $array, $account, $category);
$data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
foreach ($array as $key => $value) {
if ($key == 'pass' && !empty($value)) {
$query .= "$key = '%s', ";
$v[] = md5($value);
}
else if ((substr($key, 0, 4) !== 'auth') && ($key != 'pass')) {
if (in_array($key, $user_fields)) {
// Save standard fields
$query .= "$key = '%s', ";
$v[] = $value;
}
else if ($key != 'roles') {
// Roles is a special case: it used below.
if ($value === NULL) {
unset($data[$key]);
}
else {
$data[$key] = $value;
}
}
}
}
$query .= "data = '%s' ";
$v[] = serialize($data);
db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid)));
// Reload user roles if provided
if (is_array($array['roles'])) {
db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
foreach (array_keys($array['roles']) as $rid) {
if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
}
}
}
// Delete a blocked user's sessions to kick them if they are online.
if (isset($array['status']) && $array['status'] == 0) {
sess_destroy_uid($account->uid);
}
// If the password changed, delete all open sessions and recreate
// the current one.
if (isset($array['pass'])) {
sess_destroy_uid($account->uid);
sess_regenerate();
}
// Refresh user object
$user = user_load(array('uid' => $account->uid));
user_module_invoke('after_update', $array, $user, $category);
}
else {
$array['uid'] = db_next_id('{users}_uid');
if (!isset($array['created'])) { // Allow 'created' to be set by hook_auth
$array['created'] = time();
}
// Note, we wait with saving the data column to prevent module-handled
// fields from being saved there. We cannot invoke hook_user('insert') here
// because we don't have a fully initialized user object yet.
foreach ($array as $key => $value) {
switch ($key) {
case 'pass':
$fields[] = $key;
$values[] = md5($value);
$s[] = "'%s'";
break;
case 'uid': case 'mode': case 'sort':
case 'threshold': case 'created': case 'access':
case 'login': case 'status':
$fields[] = $key;
$values[] = $value;
$s[] = "%d";
break;
default:
if (substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) {
$fields[] = $key;
$values[] = $value;
$s[] = "'%s'";
}
break;
}
}
db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values);
// Build the initial user object.
$user = user_load(array('uid' => $array['uid']));
user_module_invoke('insert', $array, $user, $category);
// Build and save the serialized data field now
$data = array();
foreach ($array as $key => $value) {
if ((substr($key, 0, 4) !== 'auth') && ($key != 'roles') && (!in_array($key, $user_fields)) && ($value !== NULL)) {
$data[$key] = $value;
}
}
db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", serialize($data), $user->uid);
// Save user roles (delete just to be safe).
if (is_array($array['roles'])) {
db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
foreach (array_keys($array['roles']) as $rid) {
if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
}
}
}
// Build the finished user object.
$user = user_load(array('uid' => $array['uid']));
}
// Save distributed authentication mappings
$authmaps = array();
foreach ($array as $key => $value) {
if (substr($key, 0, 4) == 'auth') {
$authmaps[$key] = $value;
}
}
if (sizeof($authmaps) > 0) {
user_set_authmaps($user, $authmaps);
}
return $user;
}
?>在寫模組的同時,發現有些資訊是記在全域變數裡面,在遍尋的函數找不到之後才發現。
所以把一些常用的全域變數整理到這裡,提供給人查詢。
所有用變數使用之前,必須要先「global $變數;」才能使用...
使用者物件
既使沒有人登入,也會有一個"訪客"況態的物件。要知道這個人有那些資訊,就必須要使用這個物件。
網站根目錄網址
當使用「clean_url(簡絜網址)」的時候,有時候會需要用到絕對路徑來作網頁連結。
不確定網址的開頭是什麼的時候,像是不確定自己的模組會被裝到哪個站裡面,可以用它來查詢網站根目錄的網址。
如果我用把drupal裝在"drupal_test"的目錄下,在本機測試時,這個變數可能會是「http://localhost/drupal_test」。
網站根目錄網址(相對路徑)
當使用「clean_url(簡絜網址)」的時候,有時候會需要用到絕對路徑來作網頁連結。
不確定網址的開頭是什麼的時候,像是不確定自己的模組會被裝到哪個站裡面,可以用它來查詢網站根目錄的網址。
如果我用把drupal裝在"drupal_test"的目錄下,在本機測試時,這個變數會是「drupal_test」。
慢慢增加中....