diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php index 07eff73907..d9b2dd4f5d 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php @@ -24,11 +24,10 @@ class Collection implements CollectionInterface private $search; private $entries; - public function __construct(Connection $connection, Query $search, array $entries = array()) + public function __construct(Connection $connection, Query $search) { $this->connection = $connection; $this->search = $search; - $this->entries = array(); } /** @@ -36,78 +35,91 @@ class Collection implements CollectionInterface */ public function toArray() { - $this->initialize(); + if (null === $this->entries) { + $this->entries = iterator_to_array($this->getIterator(), false); + } return $this->entries; } public function count() { - $this->initialize(); + if (false !== $count = ldap_count_entries($this->connection->getResource(), $this->search->getResource())) { + return $count; + } - return count($this->entries); + throw new LdapException(sprintf('Error while retrieving entry count: %s', ldap_error($this->connection->getResource()))); } public function getIterator() { - return new ResultIterator($this->connection, $this->search); + $con = $this->connection->getResource(); + $search = $this->search->getResource(); + $current = ldap_first_entry($con, $search); + + if (0 === $this->count()) { + return; + } + + if (false === $current) { + throw new LdapException(sprintf('Could not rewind entries array: %s', ldap_error($con))); + } + + yield $this->getSingleEntry($con, $current); + + while (false !== $current = ldap_next_entry($con, $current)) { + yield $this->getSingleEntry($con, $current); + } } public function offsetExists($offset) { - $this->initialize(); + $this->toArray(); return isset($this->entries[$offset]); } public function offsetGet($offset) { + $this->toArray(); + return isset($this->entries[$offset]) ? $this->entries[$offset] : null; } public function offsetSet($offset, $value) { - $this->initialize(); + $this->toArray(); $this->entries[$offset] = $value; } public function offsetUnset($offset) { - $this->initialize(); + $this->toArray(); unset($this->entries[$offset]); } - private function initialize() + private function getSingleEntry($con, $current) { - if (null === $this->entries) { - return; + $attributes = ldap_get_attributes($con, $current); + + if (false === $attributes) { + throw new LdapException(sprintf('Could not fetch attributes: %s', ldap_error($con))); } - $con = $this->connection->getResource(); + $attributes = $this->cleanupAttributes($attributes); - $entries = ldap_get_entries($con, $this->search->getResource()); + $dn = ldap_get_dn($con, $current); - if (false === $entries) { - throw new LdapException(sprintf('Could not load entries: %s', ldap_error($con))); + if (false === $dn) { + throw new LdapException(sprintf('Could not fetch DN: %s', ldap_error($con))); } - if (0 === $entries['count']) { - return array(); - } - - unset($entries['count']); - - $this->entries = array_map(function (array $entry) { - $dn = $entry['dn']; - $attributes = $this->cleanupAttributes($entry); - - return new Entry($dn, $attributes); - }, $entries); + return new Entry($dn, $attributes); } - private function cleanupAttributes(array $entry = array()) + private function cleanupAttributes(array $entry) { $attributes = array_diff_key($entry, array_flip(range(0, $entry['count'] - 1)) + array( 'count' => null, diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/ResultIterator.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/ResultIterator.php deleted file mode 100644 index 2527069c5b..0000000000 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/ResultIterator.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Ldap\Adapter\ExtLdap; - -use Symfony\Component\Ldap\Entry; -use Symfony\Component\Ldap\Exception\LdapException; - -/** - * @author Charles Sarrazin - * - * @internal - */ -class ResultIterator implements \Iterator -{ - private $connection; - private $search; - private $current; - private $key; - - public function __construct(Connection $connection, Query $search) - { - $this->connection = $connection->getResource(); - $this->search = $search->getResource(); - } - - /** - * Fetches the current entry. - * - * @return Entry - */ - public function current() - { - $attributes = ldap_get_attributes($this->connection, $this->current); - - if (false === $attributes) { - throw new LdapException(sprintf('Could not fetch attributes: %s', ldap_error($this->connection))); - } - - $dn = ldap_get_dn($this->connection, $this->current); - - if (false === $dn) { - throw new LdapException(sprintf('Could not fetch DN: %s', ldap_error($this->connection))); - } - - return new Entry($dn, $attributes); - } - - public function next() - { - $this->current = ldap_next_entry($this->connection, $this->current); - ++$this->key; - } - - public function key() - { - return $this->key; - } - - public function valid() - { - return false !== $this->current; - } - - public function rewind() - { - $this->current = ldap_first_entry($this->connection, $this->search); - - if (false === $this->current) { - throw new LdapException(sprintf('Could not rewind entries array: %s', ldap_error($this->connection))); - } - } -} diff --git a/src/Symfony/Component/Ldap/LdapClient.php b/src/Symfony/Component/Ldap/LdapClient.php index 2bd32f6ac1..0a68c05875 100644 --- a/src/Symfony/Component/Ldap/LdapClient.php +++ b/src/Symfony/Component/Ldap/LdapClient.php @@ -64,24 +64,34 @@ final class LdapClient implements LdapClientInterface $query = $this->ldap->query($dn, $query, array('filter' => $filter)); $entries = $query->execute(); - $result = array(); + $result = array( + 'count' => 0, + ); foreach ($entries as $entry) { $resultEntry = array(); foreach ($entry->getAttributes() as $attribute => $values) { - $resultAttribute = $values; + $resultAttribute = array( + 'count' => count($values), + ); + + foreach ($values as $val) { + $resultAttribute[] = $val; + } + $attributeName = strtolower($attribute); $resultAttribute['count'] = count($values); - $resultEntry[] = $resultAttribute; - $resultEntry[$attribute] = $resultAttribute; + $resultEntry[$attributeName] = $resultAttribute; + $resultEntry[] = $attributeName; } $resultEntry['count'] = count($resultEntry) / 2; + $resultEntry['dn'] = $entry->getDn(); $result[] = $resultEntry; } - $result['count'] = count($result); + $result['count'] = count($result) - 1; return $result; } diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php index df30ec85a2..f25d181896 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php @@ -47,4 +47,22 @@ class AdapterTest extends LdapTestCase $this->assertEquals(array('Fabien Potencier'), $entry->getAttribute('cn')); $this->assertEquals(array('fabpot@symfony.com', 'fabien@potencier.com'), $entry->getAttribute('mail')); } + + /** + * @group functional + */ + public function testLdapQueryIterator() + { + $ldap = new Adapter($this->getLdapConfig()); + + $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); + $query = $ldap->createQuery('dc=symfony,dc=com', '(&(objectclass=person)(ou=Maintainers))', array()); + $result = $query->execute(); + $iterator = $result->getIterator(); + $iterator->rewind(); + $entry = $iterator->current(); + $this->assertInstanceOf(Entry::class, $entry); + $this->assertEquals(array('Fabien Potencier'), $entry->getAttribute('cn')); + $this->assertEquals(array('fabpot@symfony.com', 'fabien@potencier.com'), $entry->getAttribute('mail')); + } } diff --git a/src/Symfony/Component/Ldap/Tests/LdapClientTest.php b/src/Symfony/Component/Ldap/Tests/LdapClientTest.php index 60cdf27a12..30c5adfd40 100644 --- a/src/Symfony/Component/Ldap/Tests/LdapClientTest.php +++ b/src/Symfony/Component/Ldap/Tests/LdapClientTest.php @@ -20,7 +20,7 @@ use Symfony\Component\Ldap\LdapInterface; /** * @group legacy */ -class LdapClientTest extends \PHPUnit_Framework_TestCase +class LdapClientTest extends LdapTestCase { /** @var LdapClient */ private $client; @@ -72,13 +72,11 @@ class LdapClientTest extends \PHPUnit_Framework_TestCase ->method('getIterator') ->will($this->returnValue(new \ArrayIterator(array( new Entry('cn=qux,dc=foo,dc=com', array( - 'dn' => array('cn=qux,dc=foo,dc=com'), 'cn' => array('qux'), 'dc' => array('com', 'foo'), 'givenName' => array('Qux'), )), new Entry('cn=baz,dc=foo,dc=com', array( - 'dn' => array('cn=baz,dc=foo,dc=com'), 'cn' => array('baz'), 'dc' => array('com', 'foo'), 'givenName' => array('Baz'), @@ -101,78 +99,44 @@ class LdapClientTest extends \PHPUnit_Framework_TestCase $expected = array( 'count' => 2, 0 => array( - 'count' => 4, - 0 => array( - 'count' => 1, - 0 => 'cn=qux,dc=foo,dc=com', - ), - 'dn' => array( - 'count' => 1, - 0 => 'cn=qux,dc=foo,dc=com', - ), - 1 => array( - 'count' => 1, - 0 => 'qux', - ), + 'count' => 3, + 0 => 'cn', 'cn' => array( 'count' => 1, 0 => 'qux', ), - 2 => array( - 'count' => 2, - 0 => 'com', - 1 => 'foo', - ), + 1 => 'dc', 'dc' => array( 'count' => 2, 0 => 'com', 1 => 'foo', ), - 3 => array( - 'count' => 1, - 0 => 'Qux', - ), - 'givenName' => array( + 2 => 'givenname', + 'givenname' => array( 'count' => 1, 0 => 'Qux', ), + 'dn' => 'cn=qux,dc=foo,dc=com', ), 1 => array( - 'count' => 4, - 0 => array( - 'count' => 1, - 0 => 'cn=baz,dc=foo,dc=com', - ), - 'dn' => array( - 'count' => 1, - 0 => 'cn=baz,dc=foo,dc=com', - ), - 1 => array( - 'count' => 1, - 0 => 'baz', - ), + 'count' => 3, + 0 => 'cn', 'cn' => array( 'count' => 1, 0 => 'baz', ), - 2 => array( - 'count' => 2, - 0 => 'com', - 1 => 'foo', - ), + 1 => 'dc', 'dc' => array( 'count' => 2, 0 => 'com', 1 => 'foo', ), - 3 => array( - 'count' => 1, - 0 => 'Baz', - ), - 'givenName' => array( + 2 => 'givenname', + 'givenname' => array( 'count' => 1, 0 => 'Baz', ), + 'dn' => 'cn=baz,dc=foo,dc=com', ), ); $this->assertEquals($expected, $this->client->find('dc=foo,dc=com', 'bar', 'baz')); @@ -190,6 +154,25 @@ class LdapClientTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, call_user_func_array(array($reflMethod, 'invoke'), $args)); } + /** + * @group functional + * @requires extension ldap + */ + public function testLdapClientFunctional() + { + $config = $this->getLdapConfig(); + $ldap = new LdapClient($config['host'], $config['port']); + $ldap->bind('cn=admin,dc=symfony,dc=com', 'symfony'); + $result = $ldap->find('dc=symfony,dc=com', '(&(objectclass=person)(ou=Maintainers))'); + + $con = @ldap_connect($config['host'], $config['port']); + @ldap_bind($con, 'cn=admin,dc=symfony,dc=com', 'symfony'); + $search = @ldap_search($con, 'dc=symfony,dc=com', '(&(objectclass=person)(ou=Maintainers))', array('*')); + $expected = @ldap_get_entries($con, $search); + + $this->assertSame($expected, $result); + } + public function provideConfig() { return array(