Skip to content

Commit 5e557dc

Browse files
committed
Add applyOperations batch method to EntryManager
Also introduce new UpdateOperation class.
1 parent a56c809 commit 5e557dc

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

Adapter/ExtLdap/EntryManager.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Ldap\Adapter\EntryManagerInterface;
1515
use Symfony\Component\Ldap\Entry;
16+
use Symfony\Component\Ldap\Exception\UpdateOperationException;
1617
use Symfony\Component\Ldap\Exception\LdapException;
1718
use Symfony\Component\Ldap\Exception\NotBoundException;
1819

@@ -121,4 +122,21 @@ private function getConnectionResource()
121122

122123
return $this->connection->getResource();
123124
}
125+
126+
/**
127+
* @param iterable|UpdateOperation[] $operations An array or iterable of UpdateOperation instances
128+
*
129+
* @throws UpdateOperationException in case of an error
130+
*/
131+
public function applyOperations(string $dn, iterable $operations): void
132+
{
133+
$operationsMapped = array();
134+
foreach ($operations as $modification) {
135+
$operationsMapped[] = $modification->toArray();
136+
}
137+
138+
if (!@ldap_modify_batch($this->getConnectionResource(), $dn, $operationsMapped)) {
139+
throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": "%s".', $dn, ldap_error($this->getConnectionResource())));
140+
}
141+
}
124142
}

Adapter/ExtLdap/UpdateOperation.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
13+
14+
use Symfony\Component\Ldap\Exception\UpdateOperationException;
15+
16+
class UpdateOperation
17+
{
18+
private $operationType;
19+
private $values;
20+
private $attribute;
21+
22+
private $validOperationTypes = array(
23+
LDAP_MODIFY_BATCH_ADD,
24+
LDAP_MODIFY_BATCH_REMOVE,
25+
LDAP_MODIFY_BATCH_REMOVE_ALL,
26+
LDAP_MODIFY_BATCH_REPLACE,
27+
);
28+
29+
/**
30+
* @param int $operationType An LDAP_MODIFY_BATCH_* constant
31+
* @param string $attribute The attribute to batch modify on
32+
*
33+
* @throws UpdateOperationException on consistency errors during construction
34+
*/
35+
public function __construct(int $operationType, string $attribute, ?array $values)
36+
{
37+
$this->assertValidOperationType($operationType);
38+
$this->assertNullValuesOnRemoveAll($operationType, $values);
39+
40+
$this->operationType = $operationType;
41+
$this->attribute = $attribute;
42+
$this->values = $values;
43+
}
44+
45+
public function toArray(): array
46+
{
47+
return array(
48+
'attrib' => $this->attribute,
49+
'modtype' => $this->operationType,
50+
'values' => $this->values,
51+
);
52+
}
53+
54+
/**
55+
* @param int $operationType
56+
*/
57+
private function assertValidOperationType(int $operationType): void
58+
{
59+
if (!in_array($operationType, $this->validOperationTypes, true)) {
60+
throw new UpdateOperationException(sprintf('"%s" is not a valid modification type.', $operationType));
61+
}
62+
}
63+
64+
/**
65+
* @param int $operationType
66+
* @param array|null $values
67+
*
68+
* @throws \Symfony\Component\Ldap\Exception\UpdateOperationException
69+
*/
70+
private function assertNullValuesOnRemoveAll(int $operationType, ?array $values): void
71+
{
72+
if (LDAP_MODIFY_BATCH_REMOVE_ALL === $operationType && null !== $values) {
73+
throw new UpdateOperationException(sprintf('$values must be null for LDAP_MODIFY_BATCH_REMOVE_ALL operation, "%s" given.', gettype($values)));
74+
}
75+
}
76+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Ldap\Exception;
13+
14+
class UpdateOperationException extends LdapException
15+
{
16+
}

Tests/Adapter/ExtLdap/LdapManagerTest.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter;
1515
use Symfony\Component\Ldap\Adapter\ExtLdap\Collection;
16+
use Symfony\Component\Ldap\Adapter\ExtLdap\UpdateOperation;
1617
use Symfony\Component\Ldap\Entry;
18+
use Symfony\Component\Ldap\Exception\UpdateOperationException;
1719
use Symfony\Component\Ldap\Exception\LdapException;
1820
use Symfony\Component\Ldap\Exception\NotBoundException;
1921

@@ -238,4 +240,105 @@ public function testLdapAddAttributeValuesError()
238240

239241
$entryManager->addAttributeValues($entry, 'mail', $entry->getAttribute('mail'));
240242
}
243+
244+
public function testLdapApplyOperationsRemoveAllWithArrayError()
245+
{
246+
$entryManager = $this->adapter->getEntryManager();
247+
248+
$result = $this->executeSearchQuery(1);
249+
$entry = $result[0];
250+
251+
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(UpdateOperationException::class);
252+
253+
$entryManager->applyOperations($entry->getDn(), array(new UpdateOperation(LDAP_MODIFY_BATCH_REMOVE_ALL, 'mail', array())));
254+
}
255+
256+
public function testLdapApplyOperationsWithWrongConstantError()
257+
{
258+
$entryManager = $this->adapter->getEntryManager();
259+
260+
$result = $this->executeSearchQuery(1);
261+
$entry = $result[0];
262+
263+
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(UpdateOperationException::class);
264+
265+
$entryManager->applyOperations($entry->getDn(), array(new UpdateOperation(512, 'mail', array())));
266+
}
267+
268+
public function testApplyOperationsAddRemoveAttributeValues()
269+
{
270+
$entryManager = $this->adapter->getEntryManager();
271+
272+
$result = $this->executeSearchQuery(1);
273+
$entry = $result[0];
274+
275+
$entryManager->applyOperations($entry->getDn(), array(
276+
new UpdateOperation(LDAP_MODIFY_BATCH_ADD, 'mail', array('fabpot@example.org', 'fabpot2@example.org')),
277+
new UpdateOperation(LDAP_MODIFY_BATCH_ADD, 'mail', array('fabpot3@example.org', 'fabpot4@example.org')),
278+
));
279+
280+
$result = $this->executeSearchQuery(1);
281+
$newEntry = $result[0];
282+
283+
$this->assertCount(6, $newEntry->getAttribute('mail'));
284+
285+
$entryManager->applyOperations($entry->getDn(), array(
286+
new UpdateOperation(LDAP_MODIFY_BATCH_REMOVE, 'mail', array('fabpot@example.org', 'fabpot2@example.org')),
287+
new UpdateOperation(LDAP_MODIFY_BATCH_REMOVE, 'mail', array('fabpot3@example.org', 'fabpot4@example.org')),
288+
));
289+
290+
$result = $this->executeSearchQuery(1);
291+
$newNewEntry = $result[0];
292+
293+
$this->assertCount(2, $newNewEntry->getAttribute('mail'));
294+
}
295+
296+
public function testUpdateOperationsWithIterator()
297+
{
298+
$iteratorAdd = new \ArrayIterator(array(
299+
new UpdateOperation(LDAP_MODIFY_BATCH_ADD, 'mail', array('fabpot@example.org', 'fabpot2@example.org')),
300+
new UpdateOperation(LDAP_MODIFY_BATCH_ADD, 'mail', array('fabpot3@example.org', 'fabpot4@example.org')),
301+
));
302+
303+
$iteratorRemove = new \ArrayIterator(array(
304+
new UpdateOperation(LDAP_MODIFY_BATCH_REMOVE, 'mail', array('fabpot@example.org', 'fabpot2@example.org')),
305+
new UpdateOperation(LDAP_MODIFY_BATCH_REMOVE, 'mail', array('fabpot3@example.org', 'fabpot4@example.org')),
306+
));
307+
308+
$entryManager = $this->adapter->getEntryManager();
309+
310+
$result = $this->executeSearchQuery(1);
311+
$entry = $result[0];
312+
313+
$entryManager->applyOperations($entry->getDn(), $iteratorAdd);
314+
315+
$result = $this->executeSearchQuery(1);
316+
$newEntry = $result[0];
317+
318+
$this->assertCount(6, $newEntry->getAttribute('mail'));
319+
320+
$entryManager->applyOperations($entry->getDn(), $iteratorRemove);
321+
322+
$result = $this->executeSearchQuery(1);
323+
$newNewEntry = $result[0];
324+
325+
$this->assertCount(2, $newNewEntry->getAttribute('mail'));
326+
}
327+
328+
public function testUpdateOperationsThrowsExceptionWhenAddedDuplicatedValue()
329+
{
330+
$duplicateIterator = new \ArrayIterator(array(
331+
new UpdateOperation(LDAP_MODIFY_BATCH_ADD, 'mail', array('fabpot@example.org')),
332+
new UpdateOperation(LDAP_MODIFY_BATCH_ADD, 'mail', array('fabpot@example.org')),
333+
));
334+
335+
$entryManager = $this->adapter->getEntryManager();
336+
337+
$result = $this->executeSearchQuery(1);
338+
$entry = $result[0];
339+
340+
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(UpdateOperationException::class);
341+
342+
$entryManager->applyOperations($entry->getDn(), $duplicateIterator);
343+
}
241344
}

0 commit comments

Comments
 (0)