Patch ldap_modify_batch.patch for LDAP related Bug #64317
Patch version 2013-02-27 23:35 UTC
Return to Bug #64317 |
Download this patch
Patch Revisions:
Developer: ondra.hosek@gmail.com
diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c
index 3cfa209..430bee4 100644
--- a/ext/ldap/ldap.c
+++ b/ext/ldap/ldap.c
@@ -152,6 +152,15 @@ PHP_MINIT_FUNCTION(ldap)
REGISTER_LONG_CONSTANT("LDAP_DEREF_FINDING", LDAP_DEREF_FINDING, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("LDAP_DEREF_ALWAYS", LDAP_DEREF_ALWAYS, CONST_PERSISTENT | CONST_CS);
+ /* Constants to be used with ldap_modify_batch() */
+ REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_ADD", LDAP_MODIFY_BATCH_ADD, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE", LDAP_MODIFY_BATCH_REMOVE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE_ALL", LDAP_MODIFY_BATCH_REMOVE_ALL, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REPLACE", LDAP_MODIFY_BATCH_REPLACE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_ATTRIB", LDAP_MODIFY_BATCH_ATTRIB, CONST_PERSISTENT | CONST_CS);
+ REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_MODTYPE", LDAP_MODIFY_BATCH_MODTYPE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_VALUES", LDAP_MODIFY_BATCH_VALUES, CONST_PERSISTENT | CONST_CS);
+
#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
/* LDAP options */
REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT | CONST_CS);
@@ -1432,6 +1441,356 @@ PHP_FUNCTION(ldap_delete)
}
/* }}} */
+/* {{{ _ldap_str_equal_to_const
+ */
+static int _ldap_str_equal_to_const(const char *str, uint str_len, const char *cstr)
+{
+ int i;
+
+ if (strlen(cstr) != str_len)
+ return 0;
+
+ for (i = 0; i < str_len; ++i) {
+ if (str[i] != cstr[i]) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ _ldap_strlen_max
+ */
+static int _ldap_strlen_max(const char *str, uint max_len)
+{
+ int i;
+
+ for (i = 0; i < max_len; ++i) {
+ if (str[i] == '\0') {
+ return i;
+ }
+ }
+
+ return max_len;
+}
+/* }}} */
+
+/* {{{ _ldap_hash_fetch
+ */
+static void _ldap_hash_fetch(zval *hashTbl, const char *key, zval **out)
+{
+ zval **fetched;
+ if (zend_hash_find(Z_ARRVAL_P(hashTbl), key, strlen(key)+1, (void **) &fetched) == SUCCESS) {
+ *out = *fetched;
+ }
+ else {
+ *out = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ proto bool ldap_modify_batch(resource link, string dn, array modifs)
+ Perform multiple modifications as part of one operation */
+PHP_FUNCTION(ldap_modify_batch)
+{
+ ldap_linkdata *ld;
+ zval *link, *mods, *mod, *modinfo, *modval;
+ zval *attrib, *modtype, *vals;
+ zval **fetched;
+ char *dn;
+ int dn_len;
+ int i, j, k;
+ int num_mods, num_modprops, num_modvals;
+ LDAPMod **ldap_mods;
+ uint oper;
+
+ /*
+ $mods = array(
+ array(
+ "attrib" => "unicodePwd",
+ "modtype" => LDAP_MODIFY_BATCH_REMOVE,
+ "values" => array($oldpw)
+ ),
+ array(
+ "attrib" => "unicodePwd",
+ "modtype" => LDAP_MODIFY_BATCH_ADD,
+ "values" => array($newpw)
+ ),
+ array(
+ "attrib" => "userPrincipalName",
+ "modtype" => LDAP_MODIFY_BATCH_REPLACE,
+ "values" => array("janitor@corp.contoso.com")
+ ),
+ array(
+ "attrib" => "userCert",
+ "modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
+ )
+ );
+ */
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &mods) != SUCCESS) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
+
+ /* perform validation */
+ {
+ char *modkey;
+ uint modkeylen;
+ long modtype;
+
+ /* to store the wrongly-typed keys */
+ char *tmpStr;
+ uint tmpUint;
+ ulong tmpUlong;
+
+ /* make sure the DN contains no NUL bytes */
+ if (_ldap_strlen_max(dn, dn_len) != dn_len) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "DN must not contain NUL bytes");
+ RETURN_FALSE;
+ }
+
+ /* make sure the top level is a normal array */
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(mods));
+ if (zend_hash_get_current_key_type(Z_ARRVAL_P(mods)) != HASH_KEY_IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifications array must not be string-indexed");
+ RETURN_FALSE;
+ }
+
+ num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods));
+
+ for (i = 0; i < num_mods; i++) {
+ /* is the numbering consecutive? */
+ if (zend_hash_index_find(Z_ARRVAL_P(mods), i, (void **) &fetched) != SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifications array must have consecutive indices 0, 1, ...");
+ RETURN_FALSE;
+ }
+ mod = *fetched;
+
+ /* is it an array? */
+ if (Z_TYPE_P(mod) != IS_ARRAY) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each entry of modifications array must be an array itself");
+ RETURN_FALSE;
+ }
+
+ /* for the modification hashtable... */
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(mod));
+ num_modprops = zend_hash_num_elements(Z_ARRVAL_P(mod));
+
+ for (j = 0; j < num_modprops; j++) {
+ /* are the keys strings? */
+ if (zend_hash_get_current_key_ex(Z_ARRVAL_P(mod), &modkey, &modkeylen, &tmpUlong, 0, NULL) != HASH_KEY_IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each entry of modifications array must be string-indexed");
+ RETURN_FALSE;
+ }
+
+ /* modkeylen includes the terminating NUL byte; remove that */
+ --modkeylen;
+
+ /* is this a valid entry? */
+ if (
+ !_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_ATTRIB) &&
+ !_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_MODTYPE) &&
+ !_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_VALUES)
+ ) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The only allowed keys in entries of the modifications array are 'attrib', 'modtype' and 'values'");
+ RETURN_FALSE;
+ }
+
+ zend_hash_get_current_data(Z_ARRVAL_P(mod), (void **) &fetched);
+ modinfo = *fetched;
+
+ /* does the value type match the key? */
+ if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_ATTRIB)) {
+ if (Z_TYPE_P(modinfo) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must be a string");
+ RETURN_FALSE;
+ }
+
+ if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not contain NUL bytes");
+ RETURN_FALSE;
+ }
+ }
+ else if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_MODTYPE)) {
+ if (Z_TYPE_P(modinfo) != IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_MODTYPE "' value must be a long");
+ RETURN_FALSE;
+ }
+
+ /* is the value in range? */
+ modtype = Z_LVAL_P(modinfo);
+ if (
+ modtype != LDAP_MODIFY_BATCH_ADD &&
+ modtype != LDAP_MODIFY_BATCH_REMOVE &&
+ modtype != LDAP_MODIFY_BATCH_REPLACE &&
+ modtype != LDAP_MODIFY_BATCH_REMOVE_ALL
+ ) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The '" LDAP_MODIFY_BATCH_MODTYPE "' value must match one of the LDAP_MODIFY_BATCH_* constants");
+ RETURN_FALSE;
+ }
+
+ /* if it's REMOVE_ALL, there must not be a values array; otherwise, there must */
+ if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
+ if (zend_hash_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES) + 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must not be provided");
+ RETURN_FALSE;
+ }
+ }
+ else {
+ if (!zend_hash_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES) + 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is not LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must be provided");
+ RETURN_FALSE;
+ }
+ }
+ }
+ else if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_VALUES)) {
+ if (Z_TYPE_P(modinfo) != IS_ARRAY) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' value must be an array");
+ RETURN_FALSE;
+ }
+
+ /* is the array not empty? */
+ num_modvals = zend_hash_num_elements(Z_ARRVAL_P(modinfo));
+ if (num_modvals == 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have at least one element");
+ RETURN_FALSE;
+ }
+
+ /* are its keys integers? */
+ if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must not be string-indexed");
+ RETURN_FALSE;
+ }
+
+ /* are the keys consecutive? */
+ for (k = 0; k < num_modvals; k++) {
+ if (zend_hash_index_find(Z_ARRVAL_P(modinfo), k, (void **) &fetched) != SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have consecutive indices 0, 1, ...");
+ RETURN_FALSE;
+ }
+ modval = *fetched;
+
+ /* is the data element a string? */
+ if (Z_TYPE_P(modval) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each element of a '" LDAP_MODIFY_BATCH_VALUES "' array must be a string");
+ RETURN_FALSE;
+ }
+ }
+ }
+
+ zend_hash_move_forward(Z_ARRVAL_P(mod));
+ }
+ }
+ }
+ /* validation was successful */
+
+ /* allocate array of modifications */
+ ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0);
+
+ /* for each modification */
+ for (i = 0; i < num_mods; i++) {
+ /* allocate the modification struct */
+ ldap_mods[i] = emalloc(sizeof(LDAPMod));
+
+ /* fetch the relevant data */
+ zend_hash_index_find(Z_ARRVAL_P(mods), i, (void **) &fetched);
+ mod = *fetched;
+
+ _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib);
+ _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_MODTYPE, &modtype);
+ _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_VALUES, &vals);
+
+ /* map the modification type */
+ switch (Z_LVAL_P(modtype)) {
+ case LDAP_MODIFY_BATCH_ADD:
+ oper = LDAP_MOD_ADD;
+ break;
+ case LDAP_MODIFY_BATCH_REMOVE:
+ case LDAP_MODIFY_BATCH_REMOVE_ALL:
+ oper = LDAP_MOD_DELETE;
+ break;
+ case LDAP_MODIFY_BATCH_REPLACE:
+ oper = LDAP_MOD_REPLACE;
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown and uncaught modification type.");
+ RETURN_FALSE;
+ }
+
+ /* fill in the basic info */
+ ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
+ ldap_mods[i]->mod_type = estrndup(Z_STRVAL_P(attrib), Z_STRLEN_P(attrib));
+
+ if (Z_LVAL_P(modtype) == LDAP_MODIFY_BATCH_REMOVE_ALL) {
+ /* no values */
+ ldap_mods[i]->mod_bvalues = NULL;
+ }
+ else {
+ /* allocate space for the values as part of this modification */
+ num_modvals = zend_hash_num_elements(Z_ARRVAL_P(vals));
+ ldap_mods[i]->mod_bvalues = emalloc((num_modvals+1) * sizeof(struct berval *));
+
+ /* for each value */
+ for (j = 0; j < num_modvals; j++) {
+ /* fetch it */
+ zend_hash_index_find(Z_ARRVAL_P(vals), j, (void **) &fetched);
+ modval = *fetched;
+
+ /* allocate the data struct */
+ ldap_mods[i]->mod_bvalues[j] = emalloc(sizeof(struct berval));
+
+ /* fill it */
+ ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(modval);
+ ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(Z_STRVAL_P(modval), Z_STRLEN_P(modval));
+ }
+
+ /* NULL-terminate values */
+ ldap_mods[i]->mod_bvalues[num_modvals] = NULL;
+ }
+ }
+
+ /* NULL-terminate modifications */
+ ldap_mods[num_mods] = NULL;
+
+ /* perform (finally) */
+ if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Batch Modify: %s", ldap_err2string(i));
+ RETVAL_FALSE;
+ } else RETVAL_TRUE;
+
+ /* clean up */
+ {
+ for (i = 0; i < num_mods; i++) {
+ /* attribute */
+ efree(ldap_mods[i]->mod_type);
+
+ if (ldap_mods[i]->mod_bvalues != NULL) {
+ /* each BER value */
+ for (j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) {
+ /* free the data bytes */
+ efree(ldap_mods[i]->mod_bvalues[j]->bv_val);
+
+ /* free the bvalue struct */
+ efree(ldap_mods[i]->mod_bvalues[j]);
+ }
+
+ /* the BER value array */
+ efree(ldap_mods[i]->mod_bvalues);
+ }
+
+ /* the modification */
+ efree(ldap_mods[i]);
+ }
+
+ /* the modifications array */
+ efree(ldap_mods);
+ }
+}
+/* }}} */
+
/* {{{ proto int ldap_errno(resource link)
Get the current ldap error number */
PHP_FUNCTION(ldap_errno)
@@ -2515,6 +2874,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify, 0, 0, 3)
ZEND_ARG_INFO(0, entry)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify_batch, 0, 0, 3)
+ ZEND_ARG_INFO(0, link_identifier)
+ ZEND_ARG_INFO(0, dn)
+ ZEND_ARG_INFO(0, modifications_info)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_add, 0, 0, 3)
ZEND_ARG_INFO(0, link_identifier)
ZEND_ARG_INFO(0, dn)
@@ -2668,6 +3033,7 @@ const zend_function_entry ldap_functions[] = {
PHP_FE(ldap_dn2ufn, arginfo_ldap_dn2ufn)
PHP_FE(ldap_add, arginfo_ldap_add)
PHP_FE(ldap_delete, arginfo_ldap_delete)
+ PHP_FE(ldap_modify_batch, arginfo_ldap_modify_batch)
PHP_FALIAS(ldap_modify, ldap_mod_replace, arginfo_ldap_modify)
/* additional functions for attribute based modifications, Gerrit Thomson */
diff --git a/ext/ldap/php_ldap.h b/ext/ldap/php_ldap.h
index 2ed8fd8..d8d5d4a 100644
--- a/ext/ldap/php_ldap.h
+++ b/ext/ldap/php_ldap.h
@@ -50,4 +50,14 @@ ZEND_END_MODULE_GLOBALS(ldap)
#define phpext_ldap_ptr ldap_module_ptr
+/* Constants for ldap_modify_batch */
+#define LDAP_MODIFY_BATCH_ADD 0x01
+#define LDAP_MODIFY_BATCH_REMOVE 0x02
+#define LDAP_MODIFY_BATCH_REMOVE_ALL 0x12
+#define LDAP_MODIFY_BATCH_REPLACE 0x03
+
+#define LDAP_MODIFY_BATCH_ATTRIB "attrib"
+#define LDAP_MODIFY_BATCH_MODTYPE "modtype"
+#define LDAP_MODIFY_BATCH_VALUES "values"
+
#endif /* PHP_LDAP_H */
diff --git a/ext/ldap/tests/ldap_modify_batch_basic.phpt b/ext/ldap/tests/ldap_modify_batch_basic.phpt
new file mode 100644
index 0000000..4f6705c
--- /dev/null
+++ b/ext/ldap/tests/ldap_modify_batch_basic.phpt
@@ -0,0 +1,109 @@
+--TEST--
+ldap_modify_batch() - Basic batch modify operation
+--CREDITS--
+Patrick Allaert <patrickallaert@php.net>
+Ondřej Hošek <ondra.hosek@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php require_once('skipifbindfailure.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+
+$link = ldap_connect_and_bind($host, $port, $user, $passwd, $protocol_version);
+insert_dummy_data($link);
+
+$mods = array(
+ array(
+ "attrib" => "telephoneNumber",
+ "modtype" => LDAP_MODIFY_BATCH_ADD,
+ "values" => array(
+ "+1 555 5551717"
+ )
+ ),
+ array(
+ "attrib" => "sn",
+ "modtype" => LDAP_MODIFY_BATCH_REPLACE,
+ "values" => array("Brown-Smith")
+ ),
+ array(
+ "attrib" => "description",
+ "modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
+ )
+);
+
+var_dump(
+ ldap_modify_batch($link, "cn=userA,dc=my-domain,dc=com", $mods),
+ ldap_get_entries($link, ldap_search($link, "dc=my-domain,dc=com", "(sn=Brown-Smith)"))
+);
+?>
+===DONE===
+--CLEAN--
+<?php
+require "connect.inc";
+
+$link = ldap_connect_and_bind($host, $port, $user, $passwd, $protocol_version);
+
+remove_dummy_data($link);
+?>
+--EXPECT--
+bool(true)
+array(2) {
+ ["count"]=>
+ int(1)
+ [0]=>
+ array(12) {
+ ["objectclass"]=>
+ array(2) {
+ ["count"]=>
+ int(1)
+ [0]=>
+ string(6) "person"
+ }
+ [0]=>
+ string(11) "objectclass"
+ ["cn"]=>
+ array(2) {
+ ["count"]=>
+ int(1)
+ [0]=>
+ string(5) "userA"
+ }
+ [1]=>
+ string(2) "cn"
+ ["userpassword"]=>
+ array(2) {
+ ["count"]=>
+ int(1)
+ [0]=>
+ string(4) "oops"
+ }
+ [2]=>
+ string(12) "userpassword"
+ ["telephonenumber"]=>
+ array(3) {
+ ["count"]=>
+ int(2)
+ [0]=>
+ string(14) "xx-xx-xx-xx-xx"
+ [1]=>
+ string(14) "+1 555 5551717"
+ }
+ [3]=>
+ string(15) "telephonenumber"
+ ["sn"]=>
+ array(2) {
+ ["count"]=>
+ int(1)
+ [0]=>
+ string(11) "Brown-Smith"
+ }
+ [4]=>
+ string(2) "sn"
+ ["count"]=>
+ int(5)
+ ["dn"]=>
+ string(28) "cn=userA,dc=my-domain,dc=com"
+ }
+}
+===DONE===
diff --git a/ext/ldap/tests/ldap_modify_batch_error.phpt b/ext/ldap/tests/ldap_modify_batch_error.phpt
new file mode 100644
index 0000000..687c371
--- /dev/null
+++ b/ext/ldap/tests/ldap_modify_batch_error.phpt
@@ -0,0 +1,104 @@
+--TEST--
+ldap_modify_batch() - Batch modify operations that should fail
+--CREDITS--
+Patrick Allaert <patrickallaert@php.net>
+Ondřej Hošek <ondra.hosek@gmail.com>
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php require_once('skipifbindfailure.inc'); ?>
+--FILE--
+<?php
+require "connect.inc";
+
+$link = ldap_connect_and_bind($host, $port, $user, $passwd, $protocol_version);
+
+$addGivenName = array(
+ array(
+ "attrib" => "givenName",
+ "modtype" => LDAP_MODIFY_BATCH_ADD,
+ "values" => array("Jack")
+ )
+);
+
+// Too few parameters
+var_dump(ldap_modify_batch());
+var_dump(ldap_modify_batch($link));
+var_dump(ldap_modify_batch($link, "dc=my-domain,dc=com"));
+
+// Too many parameters
+var_dump(ldap_modify_batch($link, "dc=my-domain,dc=com", $addGivenName, "Invalid additional parameter"));
+
+// DN not found
+var_dump(ldap_modify_batch($link, "dc=my-domain,dc=com", $addGivenName));
+
+// Invalid DN
+var_dump(ldap_modify_batch($link, "weirdAttribute=val", $addGivenName));
+
+// prepare
+$entry = array(
+ "objectClass" => array(
+ "top",
+ "dcObject",
+ "organization"),
+ "dc" => "my-domain",
+ "o" => "my-domain",
+);
+
+ldap_add($link, "dc=my-domain,dc=com", $entry);
+
+// invalid domain
+$mods = array(
+ array(
+ "attrib" => "dc",
+ "modtype" => LDAP_MODIFY_BATCH_REPLACE,
+ "values" => array("Wrong Domain")
+ )
+);
+
+var_dump(ldap_modify_batch($link, "dc=my-domain,dc=com", $mods));
+
+// invalid attribute
+$mods = array(
+ array(
+ "attrib" => "weirdAttribute",
+ "modtype" => LDAP_MODIFY_BATCH_ADD,
+ "values" => array("weirdVal", "anotherWeirdval")
+ )
+);
+
+var_dump(ldap_modify_batch($link, "dc=my-domain,dc=com", $mods));
+?>
+===DONE===
+--CLEAN--
+<?php
+require "connect.inc";
+
+$link = ldap_connect_and_bind($host, $port, $user, $passwd, $protocol_version);
+
+ldap_delete($link, "dc=my-domain,dc=com");
+?>
+--EXPECTF--
+Warning: ldap_modify_batch() expects exactly 3 parameters, 0 given in %s on line %d
+NULL
+
+Warning: ldap_modify_batch() expects exactly 3 parameters, 1 given in %s on line %d
+NULL
+
+Warning: ldap_modify_batch() expects exactly 3 parameters, 2 given in %s on line %d
+NULL
+
+Warning: ldap_modify_batch() expects exactly 3 parameters, 4 given in %s on line %d
+NULL
+
+Warning: ldap_modify_batch(): Batch Modify: No such object in %s on line %d
+bool(false)
+
+Warning: ldap_modify_batch(): Batch Modify: Invalid DN syntax in %s on line %d
+bool(false)
+
+Warning: ldap_modify_batch(): Batch Modify: Naming violation in %s on line %d
+bool(false)
+
+Warning: ldap_modify_batch(): Batch Modify: Undefined attribute type in %s on line %d
+bool(false)
+===DONE===
|