我在開發Zend_Ldap時遇到了同樣的問題爲Zend框架。我將嘗試解釋真正的問題,但要簡短一些:直到PHP 5.4,由於本文中的限制,無法使用Active Directory中帶有未修補的PHP版本(ext/ldap
)版本的分頁結果分機號碼。
讓我們嘗試解開整個事情... Microsoft Active Directory使用所謂的服務器控件來完成服務器端結果分頁。該控制在RFC 2696 "LDAP Control Extension for Simple Paged Results Manipulation"中描述。
ext/php
分別通過其ldap_set_option()
和LDAP_OPT_SERVER_CONTROLS
和LDAP_OPT_CLIENT_CONTROLS
選項提供對LDAP控制擴展的訪問。要設置分頁控件,您需要使用control-oid,這是1.2.840.113556.1.4.319
,我們需要知道如何對控制值進行編碼(這在RFC中進行了描述)。該值是一個字節串包裹以下序列的BER編碼的版本(從RFC複製):
realSearchControlValue ::= SEQUENCE {
size INTEGER (0..maxInt),
-- requested page size from client
-- result set size estimate from server
cookie OCTET STRING
}
因此,我們可以在適當的服務器控制設置執行LDAP查詢之前:
$pageSize = 100;
$pageControl = array(
'oid' => '1.2.840.113556.1.4.319', // the control-oid
'iscritical' => true, // the operation should fail if the server is not able to support this control
'value' => sprintf ("%c%c%c%c%c%c%c", 48, 5, 2, 1, $pageSize, 4, 0) // the required BER-encoded control-value
);
這使我們可以向LDAP/AD服務器發送分頁查詢。但是,我們如何知道是否有更多頁面需要遵循,以及如何指定我們必須發送下一個查詢的哪個控件值?
這就是我們陷入困境的地方......服務器響應包含所需分頁信息的結果集,但PHP缺少一種方法從結果集中準確檢索此信息。 PHP爲LDAP API函數ldap_parse_result()
提供了一個包裝器,但所需的最後一個參數serverctrlsp
未公開給PHP函數,因此無法檢索所需的信息。一個bug report已經提交了這個問題,但一直存在自2005年以來沒有反應如果ldap_parse_result()
功能提供所需的參數,使用分頁的結果會工作像
$l = ldap_connect('somehost.mydomain.com');
$pageSize = 100;
$pageControl = array(
'oid' => '1.2.840.113556.1.4.319',
'iscritical' => true,
'value' => sprintf ("%c%c%c%c%c%c%c", 48, 5, 2, 1, $pageSize, 4, 0)
);
$controls = array($pageControl);
ldap_set_option($l, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_bind($l, 'CN=bind-user,OU=my-users,DC=mydomain,DC=com', 'bind-user-password');
$continue = true;
while ($continue) {
ldap_set_option($l, LDAP_OPT_SERVER_CONTROLS, $controls);
$sr = ldap_search($l, 'OU=some-ou,DC=mydomain,DC=com', 'cn=*', array('sAMAccountName'), null, null, null, null);
ldap_parse_result ($l, $sr, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls); // (*)
if (isset($serverctrls)) {
foreach ($serverctrls as $i) {
if ($i["oid"] == '1.2.840.113556.1.4.319') {
$i["value"]{8} = chr($pageSize);
$i["iscritical"] = true;
$controls = array($i);
break;
}
}
}
$info = ldap_get_entries($l, $sr);
if ($info["count"] < $pageSize) {
$continue = false;
}
for ($entry = ldap_first_entry($l, $sr); $entry != false; $entry = ldap_next_entry($l, $entry)) {
$dn = ldap_get_dn($l, $entry);
}
}
正如你看到有代碼(*)
一行這使整個事情變得毫無用處。在我的路上,儘管關於這個問題的信息很少,但我發現了IñakiArenaza對PHP 4.3.10 ext/ldap
的補丁,但我也沒有嘗試,也不知道補丁是否可以應用於PHP5 ext/ldap
。補丁擴展ldap_parse_result()
到第7參數暴露給PHP:
--- ldap.c 2004-06-01 23:05:33.000000000 +0200
+++ /usr/src/php4/php4-4.3.10/ext/ldap/ldap.c 2005-09-03 17:02:03.000000000 +0200
@@ -74,7 +74,7 @@
ZEND_DECLARE_MODULE_GLOBALS(ldap)
static unsigned char third_argument_force_ref[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
-static unsigned char arg3to6of6_force_ref[] = { 6, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE };
+static unsigned char arg3to7of7_force_ref[] = { 7, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE };
static int le_link, le_result, le_result_entry, le_ber_entry;
@@ -124,7 +124,7 @@
#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP
PHP_FE(ldap_get_option, third_argument_force_ref)
PHP_FE(ldap_set_option, NULL)
- PHP_FE(ldap_parse_result, arg3to6of6_force_ref)
+ PHP_FE(ldap_parse_result, arg3to7of7_force_ref)
PHP_FE(ldap_first_reference, NULL)
PHP_FE(ldap_next_reference, NULL)
#ifdef HAVE_LDAP_PARSE_REFERENCE
@@ -1775,14 +1775,15 @@
Extract information from result */
PHP_FUNCTION(ldap_parse_result)
{
- pval **link, **result, **errcode, **matcheddn, **errmsg, **referrals;
+ pval **link, **result, **errcode, **matcheddn, **errmsg, **referrals, **serverctrls;
ldap_linkdata *ld;
LDAPMessage *ldap_result;
+ LDAPControl **lserverctrls, **ctrlp, *ctrl;
char **lreferrals, **refp;
char *lmatcheddn, *lerrmsg;
int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
- if (myargcount 6 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals) == FAILURE) {
+ if (myargcount 7 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals, &serverctrls) == FAILURE) {
WRONG_PARAM_COUNT;
}
@@ -1793,7 +1794,7 @@
myargcount > 3 ? &lmatcheddn : NULL,
myargcount > 4 ? &lerrmsg : NULL,
myargcount > 5 ? &lreferrals : NULL,
- NULL /* &serverctrls */,
+ myargcount > 6 ? &lserverctrls : NULL,
0);
if (rc != LDAP_SUCCESS) {
php_error(E_WARNING, "%s(): Unable to parse result: %s", get_active_function_name(TSRMLS_C), ldap_err2string(rc));
@@ -1805,6 +1806,29 @@
/* Reverse -> fall through */
switch(myargcount) {
+ case 7 :
+ zval_dtor(*serverctrls);
+
+ if (lserverctrls != NULL) {
+ array_init(*serverctrls);
+ ctrlp = lserverctrls;
+
+ while (*ctrlp != NULL) {
+ zval *ctrl_array;
+
+ ctrl = *ctrlp;
+ MAKE_STD_ZVAL(ctrl_array);
+ array_init(ctrl_array);
+
+ add_assoc_string(ctrl_array, "oid", ctrl->ldctl_oid,1);
+ add_assoc_bool(ctrl_array, "iscritical", ctrl->ldctl_iscritical);
+ add_assoc_stringl(ctrl_array, "value", ctrl->ldctl_value.bv_val,
+ ctrl->ldctl_value.bv_len,1);
+ add_next_index_zval (*serverctrls, ctrl_array);
+ ctrlp++;
+ }
+ ldap_controls_free (lserverctrls);
+ }
case 6 :
zval_dtor(*referrals);
if (array_init(*referrals) == FAILURE) {
其實剩下的唯一選擇是改變Active Directory配置,提高最大結果上限。相關選項被稱爲MaxPageSize
,可通過使用ntdsutil.exe
進行更改 - 請參閱"How to view and set LDAP policy in Active Directory by using Ntdsutil.exe"。
編輯(參考COM):
或者你可以去周圍的其他方式,並使用通過ADODB的COM的方法爲在eykanal提供的link建議。
太棒了!非常感謝,這對我非常有幫助! – Christian 2011-11-06 16:28:58
PHP 5.4支持分頁LDAP結果,現在狀態如何? – Squazic 2012-10-12 22:29:39
@Squazic:請參閱下面的回答http://stackoverflow.com/a/10140166/11354。似乎是可行的,從PHP 5.4開始。 – 2015-10-27 08:40:59