Ответ 1
Я был поражен той же проблемой при разработке Zend_Ldap для Zend Framework. Я попытаюсь объяснить, какова реальная проблема, но чтобы сделать ее коротким: до PHP 5.4, невозможно было использовать выгружаемые результаты из Active Directory с неподдерживаемой версией PHP (ext/ldap
) из-за ограничения именно в этом расширении.
Попробуй разобраться во всем этом... Microsoft Active Directory использует так называемый серверный элемент управления для выполнения поискового вызова на стороне сервера. Этот элемент управления описан в RFC 2696 "Расширение LDAP-управления для простого манипулирования с помощью постраничных результатов" .
ext/php
предлагает доступ к управляющим расширениям LDAP через ldap_set_option()
и LDAP_OPT_SERVER_CONTROLS
и LDAP_OPT_CLIENT_CONTROLS
соответственно. Чтобы установить paged-элемент управления, вам нужен элемент управления-oid, который равен 1.2.840.113556.1.4.319
, и нам нужно знать, как кодировать контрольное значение (это описано в RFC). Значение представляет собой строку октета, обертывающую BER-кодированную версию следующей SEQUENCE (скопированной из 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 предоставляет оболочку для функции API LDAP ldap_parse_result()
, но требуемый последний параметр serverctrlsp
не подвергается функции PHP, поэтому там это не способ получить требуемую информацию. A отчет об ошибке был подан для этой проблемы, но с 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);
}
}
Как вы видите, существует одна строка кода (*)
, которая делает все это бесполезным. На моем пути, хотя и редкая информация по этому вопросу, я нашел патч против PHP 4.3.10 ext/ldap
от Iñaki Arenaza, но я тоже не пробовал, и не знаю, можно ли применить патч к 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
- см. "Как просмотреть и установить политику LDAP в Active Directory с помощью Ntdsutil.exe" .
EDIT (ссылка на COM):
Или вы можете пойти наоборот и использовать COM-подход через ADODB, как предлагается в ссылка, предоставляемая eykanal.