Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Ldap - Extended support(Zend_Ldap_Ext) Component Proposal

Proposed Component Name Zend_Ldap - Extended support(Zend_Ldap_Ext)
Developer Notes - Extended support(Zend_Ldap_Ext)
Proposers Stefan Gehrig
Liasion: Ralph Schindler
Revision 0.1 - 29 March 2008: Opened proposal.
0.1.1 - 3 April 2008: Changed layout to use decks.
0.2 - 13 April 2008: Added Zend_Ldap_Ext::count() method
0.3 - 17 April 2008: Completely reworked the class skeletons to match the current version, I'm working on.
0.4 - 5 July 2008: Changed respository location
0.5 - 10 August 2008: Reworked the proposal as it was fairly outdated
0.6 - 27 November 2008: Updated proposal to match Zend requirements (merging of Zend_Ldap_Ext into Zend_Ldap)
0.7 - 4 December 2008: Updated proposal (includes new features like LDIF support, schema browsing, etc.) (wiki revision: 33)

Table of Contents

1. Overview

The existing Zend_Ldap component currently just responds to authentication use cases in all their varieties. There is no posibility to query a LDAP directory service in a unified and consistent way. The current component also lacks core CRUD (Create, Retrieve, Update and Delete) functionality - operations that are crucial to for example database abstraction layers.
This proposals tries to resolve these deficiencies in that it provides a simple two-ply object oriented model to connect to, query and perfom CRUD operations on an LDAP server. The first layer is a wrapper around the ext/ldap functions, spiced up with extended functionality such as copying and moving (renaming in a LDAP context) nodes and subtrees.
The second layer (Zend_Ldap_Node) provides an active-record-like interface to LDAP entries and stresses the tree-structure of LDAP data in providing (recursive) tree traversal methods.
To simplify the usage of the unfamiliar LDAP filter syntax this components proposes an object oriented approach to LDAP filter string generation, which can loosely be compared to Zend_Db_Select.
Usefull helper classes for creating and modifying LDAP DNs and converting attribute values complete this component.
It is important to note, that this proposal is a complete replacement for the current Zend_Ldap component and does not break backwards-compatibility.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will replace Zend_Ldap.
  • This component will not break backwards-compatibility with the existing Zend_Ldap.
  • This component will provide generic CRUD functions (getEntry(), add(), update() and delete()).
  • This component will provide generic search functions (search()).
  • This component will abstracts a LDAP search result resource into a collection class to abstract the cumbersome use of ext/ldap to retrieve attributes in a common format.
  • This component will provide an object oriented API to LDAP entries that factors in the tree-structure of LDAP.
  • This component will provide an active-record-like interface to single LDAP entries.
  • This component will provide an API to manipulate DN strings.
  • This component will allow for the creation of LDAP filters in an object oriented way.
  • This component will assist the user in populating and reading LDAP entry arrays.
  • This component will detect LDAP boolean values ('TRUE' and 'FALSE') and convert them to PHP booleans.
  • This component will provide a method to populate an LDAP entry array regardless of the PHP variable type.
  • This component will handle file resources transparently so that it's possible to add the contents of a file to an LDAP attribute.
  • This component will provide a method to set a LDAP userPassword attribute with SHA1 or MD5 hashed passwords.

4. Dependencies on Other Framework Components

  • PHP ext/ldap

5. Theory of Operation

This replacement Zend_Ldap component builds on the foundations layed out by the current Zend_Ldap component and extends its capabilities with methods to query an LDAP directory service and to perform creation, updating, retrieval and deletion operations on the LDAP server. It therefore wraps ext/ldap function calls in an object-oriented interface and unifies result handling. On this layer LDAP data is represented in an array format to allow for round-trips of the data. Helper functions will allow developers to handle LDAP data in a common way; this includes conversion of LDAP date/time attributes, LDAP boolean attributes and the creation of LDAP password attributes. These methods will also help the developer to build consistent LDAP data arrays for use with the different data modification methods of Zend_Ldap and will allow the creation of LDAP DN strings (a lot of these functions are inspired by PEAR::Package::Net_LDAP2).
Query results are encapsulated in Zend_Ldap_Collection which acts as an interface to the LDAP resultset. Zend_Ldap_Collection implemets the common PHP SPL interfaces Iterator and Countable and includes a LDAP entry cache to speed up multiple iterations.
On top of these core functionality Zend_Ldap_Node provides an active-record-like interface to single LDAP entries. Through the use of the SPL RecursiveIterator interface developers can traverse complete LDAP trees recursively with a single foreach()-loop. Zend_Ldap_Node can be extended by deleopers and form a basis for a LDAP data model.
Building LDAP filter strings is unfamiliar to SQL-accustomed developers and not always very intuitive with all its parentheses. Zend_Ldap_Filter proposes an object oriented approach to filter creation which is also inspired by PEAR::Package::Net_LDAP2. With automatic value escaping LDAP filter string creation could be a no-brainer this way.

6. Milestones / Tasks

  • Milestone 1: [DONE] Write initial proposal
  • Milestone 2: [CURRENT] Review by community
  • Milestone 3: [DONE] Checked in at
  • Milestone 4: [DONE] Review by Zend
  • Milestone 5: Component incubated
  • Milestone 6: [DONE] Write unit tests
  • Milestone 7: Write documentation
  • Milestone 8: Component cored

7. Class Index

  • Zend_Ldap
  • Zend_Ldap_Attribute
  • Zend_Ldap_Collection
  • Zend_Ldap_Converter
  • Zend_Ldap_Dn
  • Zend_Ldap_Exception
  • Zend_Ldap_Filter
  • Zend_Ldap_Filter_Abstract
  • Zend_Ldap_Filter_And
  • Zend_Ldap_Filter_Exception
  • Zend_Ldap_Filter_Logical
  • Zend_Ldap_Filter_Mask
  • Zend_Ldap_Filter_Not
  • Zend_Ldap_Filter_Or
  • Zend_Ldap_Filter_String
  • Zend_Ldap_Node
  • Zend_Ldap_Node_ChildrenIterator
  • Zend_Ldap_Node_Collection
  • Zend_Ldap_QueryResult

8. Use Cases

Core classes
Filter subpackage
Node subpackage

9. Class Skeletons

Core classes
Filter subpackage
Node subpackage
LDIF subpackage



proposal proposal Delete
proposals proposals Delete
ldap ldap Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Apr 27, 2008

    <p>I wonder if really nobody is interested in an extended LDAP support for the Zend Framework. The current implementation does not allow for anything more than authentication and ext/ldap usage is realy time-consuming, e.g. dealing with three different resource types (connection, search result, entry).I personally think, Zend Framework should provide a LDAP component for working with a LDAP directory entirely. <br />
    There is a lot of room for improvement and a procreative discussion among the community could help a lot.</p>

    1. Jun 23, 2008

      <p>I'm building a small internal tool for the company I work for to manage some internal projects and I'll use this component to browse/search our ldap server. I'll blog about once I'm done over at <a class="external-link" href=""></a>. If I find some shortcomings or bugs I'll be sure to let you know. <ac:emoticon ac:name="smile" /></p>

    2. Jul 05, 2008

      <p>I've heard requests for this functionality many times. Have you announced that it is ready for review on the general mailing list yet?</p>


      1. Jul 05, 2008

        <p>Hi Wil,</p>

        <p>yes I did make an announcement on the fw-general mailing yes. I only got a response from Michael B Allen more or less rejecting the idea of a Zend Framework LDAP component extension in the given form.<br />
        I didn't have any time to respond to his doubts yet but I think that even if this component does not use polymorphism (one of the main arguments of Michael B Allen) the object oriented interface creatly simplifies the usage of LDAP directory services in PHP code. <br />
        I currently use this component on an internal project and I'm looking forward to get back some impressions and review from Christer.</p>

        <p>Best regards</p>


  2. Apr 27, 2008

    <p>Checked in initial version under SVN url <a class="external-link" href=""></a></p>

  3. Jul 05, 2008

    <p>Changend SVN url to <a class="external-link" href=""></a></p>

  4. Jul 14, 2008

    <p>I am quite keen on the general concept of Zend and I have been using the framework for some time. The existing Zend_Ldap class and this proposed extension, however, are a poor substitute for PEAR's Net_LDAP2 (successor to Net_LDAP). The Net_LDAP package was modelled on CPAN's (perl) Net::LDAP package interface, which is pretty much the benchmark in terms of fully functional object oriented access to LDAP repositories.</p>

    <p>I hate to be negative here but I really think that you need to go away and have a good long hard look at both Net_LDAP2 and Net::LDAP. Both are under free software licenses and so it isn't hard or legally complex to examine what these packages do (especially in terms of schema browsing, object editing and updating, and connection to multiple LDAP servers for resiliency) and bring those ideas back into Zend, or even copy the entire interface design and concept back here.</p>

    <p>I think you're doing yourself and the Zend community a disservice by not looking in detail at at least the interfaces to these excellent packages. I have authored several systems built on top of Net::LDAP and I'm in the process of migrating one of those to PHP using Net_LDAP2, and these systems really are the swiss army knife of LDAP manipulation.</p>

    <p>Happily enough I have managed to develop an authentication adapter (Zend_Auth_Adapter_Ldap2 implements Zend_Auth_Adapter_Interface) built on top of Net_LDAP2 so I've been able to avoid the use of Zend's Zend_Ldap interface entirely, but I wish I didn't have to pull in quite so many PEAR components to get LDAP working the way I want it to work within my Zend application.</p>

    <p>Stefan, I think you're doing an excellent job in general, and I'd much prefer to see an extended LDAP interface within Zend, and I'm not sure what Michael Allen's issues are, but if the Zend leaders can't be pushed to support an extended LDAP interface within Zend then programmers are going to have to look outside of Zend for LDAP support.</p>

    1. Jul 19, 2008

      <p>Hi Del,</p>

      <p>thanks a lot for your constructive and suggestive comments. I think most points of criticism are actually founded on the fact that this proposal's page is somehow not really up-to-date with the current features implemented in code. Shame on me for this one - but I currently don't have enough time to update the proposal's page.<br />
      Did you have a look at the actual code from <a href=""></a><br />
      I already examined Net_LDAP2 very intensively and some ideas are borrowed from there - especially the filter and DN string escaping algorithms. I identified three main features of Net_LDAP2 that are currently not implemented in my proposal:</p>
      <li>schema browsing</li>
      <li>connection to multiple LDAP servers</li>
      <li>LDIF im-/export</li>

      <p>Of these three I'd consider the connection to multiple LDAP servers as the most important "core" feature that would add value to this proposal. The other two features could be implemented later on as for example schema browsing is just an add-on to the normal data retrieval procedure. LDIF im- and export can be integrated by porting existing LDIF libraries. This proposal emanates from my need for a Zend Framework LDAP integration in one of my current projects - and there is currently no need for schema browsing or LDIF im-/export, so I didn't consider these two features. As it's a one-man-show you have to concentrate on the main features and must not open too many "construction sites".<br />
      All other features of Net_LDAP2 are part of this proposal:</p>
      <li>CRUD operations (object editing and updating)</li>
      <li>object oriented filter handling (creation)</li>

      <p>Furthermore the proposal includes:</p>
      <li>possibility to traverse a tree or subtree recursively by use of a SPL RecursiveIteratorIterator (<a href=""></a>)</li>
      <li>converting from an to LDAP datetime values and creating password values.</li>

      <p>I think rated by the features currently implemented the proposal can not be considered a disservice even though some Net_LDAP2 features are still missing.<br />
      Regarding Michael Allen's issues he actually is right on his statement that the component is currently not structured in an optimal object-oriented way. I disagree with him in the fact that object-oriented components must use polymorphism but for example the Zend_Ldap_Helper "static" class is something that should be refactored out in the current code.</p>

      <blockquote><p>but if the Zend leaders can't be pushed to support an extended LDAP interface within Zend then programmers are going to have to look outside of Zend for LDAP support.</p></blockquote>

      <p>That's exactly my rationale to try to develop an Zend internal LDAP interface. Perhaps you can bing in your experiences and needs to enhance the current proposal and its features.</p>

      <p>Thanks again for your comment - nothing is more frustrating than no comment at all <ac:emoticon ac:name="wink" /></p>

      <p>Best regards<br />

  5. Aug 29, 2008

    <p>Very interesting and missing Component in the ZF i think. I am looking forward to this to be in the core.</p>

    <p>"On top of these core functionality Zend_Ldap_Node provides an active-record-like interface to single LDAP entries. Through the use of the SPL RecursiveIterator interface developers can traverse complete LDAP trees recursively with a single foreach()-loop." --> That sounds very useful!</p>

  6. Nov 01, 2008

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Official Response</ac:parameter><ac:rich-text-body>

    <p>This is approved for <strong>Zend Standard Incubator</strong> development.</p>

    <p>We would like you to explore the following ideas:</p>
    <li>Move the new api from Zend_Ldap_Ext into the Zend_Ldap class</li>
    <li>Research the possibility of moving all the ext/ldap calls into an adapter based solution as to make room for ActiveDirectory, userspace, and/or other vendor specific ldap implementations.</li>


    1. Nov 24, 2008

      <p>Development has been moved to <a href=""></a> (svn <a href=""></a>).</p>

      <p>Current version merges Zend_Ldap_Ext features into existing Zend_Ldap.</p>

  7. Nov 28, 2008

    <p>nice work , 1 am waiting to see this in ZF incubator library .I will need this in my new project</p>

    1. Dec 04, 2008

      <p>As I haven't received my SVN access to the incubator yet, the code is only available via <a class="external-link" href=""></a></p>

      <p>The current version has been updated with</p>
      <ul class="alternate">
      <li>schema browsing</li>
      <li>LDIF decoding and encoding<br />
      and a lot of smaller updates, fixed and new features.</li>

      <p>Please note that currently LDIF decoding is not really integrated into the component. It's just a static method that parses a LDIF string into an array representation of the LDAP data. No error checking will be done. There is also no support for LDIF change-sets.<br />
      LDIF decoding should be considered unstable.</p>

      <p>The current version also has only been tested on an OpenLDAP server as I don't have access to any other LDAP server (especially ActiveDirectory).</p>

      1. Dec 04, 2008

        <p>Send an email to Matthew Weier O'Phinney or Wil Sinclair or someone else from Zend, you will find their emails on mailing lists </p>

        1. Dec 07, 2008

          <p>The extended Zend_Ldap component (formerly known as Zend_Ldap_Ext) is now available in the Zend Framework Standard Incubator - including all available unit tests.</p>

          <p>Feel free to try and test the component - feedback is always appreciated!</p>

          <p>Thanks to Ralph for the SVN access!</p>

  8. Mar 18, 2009

    <p>Stefan, what is the status of this proposal? We'd really like to see it included in the 1.8 release.</p>

    1. Mar 19, 2009

      <p>Hi Wil,</p>

      <p>the proposal in the Standard Incubator should be feature-complete and completely coded (except for schema browsing on ActiveDirectory servers), unit tests are up and running (OpenLDAP: Tests: 359, Assertions: 1142 - Coverage 91.11%; ActiveDirectory: Tests: 359, Assertions: 1068 - Coverage 80.53%), leaving only the documentation missing. <br />
      I must admit that I don't have a clue on how to start with the documentation - I've never done any documentation on Zend Framework components until now. Perhaps you can give some general guidance...</p>

      <p>Best regards</p>


      <p>PS: Thanks for the nice ZF t-shirt <ac:emoticon ac:name="wink" /></p>

  9. Mar 23, 2009


    <p>Hi folks.</p>

    <p>I'm not sure if this is the correct place to report an issue with this compontent, but here is it:</p>

    <p>The problem is that when a Zend_Ldap_Node is going to be moved or renamed Zend_Ldap::rename is called with the recursive flag always set. Then, the rename operation is emulated because of this flag.</p>

    <p>I have an OpenLdap server with the unique overlay configured for uid and mail attributes. When I try to move an entry, as the rename operatios is emulated, a copy entry it's created at first, and the original node deleted after copying which become an error: <em>0x13 (LDAP_CONSTRAINT_VIOLATION)</em></p>

    <p>To solve the issue, I set the recursive flag only if the current node has children. A better implementation will be detect if the server supports renaming non-leaf nodes (openLdap supports it with a HBD backend), but I don't know how.</p>

    <p>Here is my patch:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    Index: library/incubator/Zend/Ldap/Node.php
    — library/incubator/Zend/Ldap/Node.php        (revisión: 14426)
    +++ library/incubator/Zend/Ldap/Node.php        (copia de trabajo)
    @@ -438,8 +438,9 @@

             $changedData = $this->getChangedData();
            if ($this>willBeMoved()) {
                $ldap>rename($this->_dn, $this->_newDn, true, false);
    +        if ($this->willBeMoved()) {
    +            $recursive = $this->hasChildren();
    +            $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
                 foreach ($this->_newDn->getRdn() as $key => $value) {
                     if (array_key_exists($key, $changedData)) {
    <p>Cheers!! </p>

    1. Mar 24, 2009

      <p>Hi AJ,</p>

      <p>thanks for that bug report (I think that as long as the new component is still in the incubator you cannot report bugs on the issue tracker - so reporting them here should be fine).<br />
      I implemented your patch as is into the current incubator version. Please feel free to update your local version.</p>

      <p>Best regards</p>


  10. Mar 30, 2009


    <p>What's the word on this component making it into 1.8? Is documentation the only task left?</p>

    <ul class="alternate">

    1. Mar 30, 2009

      <p>That's correct Jeff. The documentation is the only open task left (and writing documentation is not really my favorite domain <ac:emoticon ac:name="smile" /></p>

      <p>Best regards</p>


      1. Mar 31, 2009

        <p>Well, it's high-time I started contributing to this project. I'm willing to help you flesh out the docs if you'd like. Just shoot me an email!</p>


        1. Mar 31, 2009

          <p>Would be nice to have someone help me out with the docs. Do you have an email on which I can contact you (found none on your profile page)? You can contact me using the email gehrig(at)ishd(dot)de or stefan(dot)gehrig(dot)hn(at)googlemail(dot)com.</p>

  11. Apr 29, 2009

    <p>Great work on this by the way. Since finding this, I have scrapped my own implementation of an LDAP library and have begun integrating yours into our core framework; however I have found at least one issue.</p>

    <p>Calling Zend_Ldap->getSchema() for an OpenLdap server fails. This seems to be due to an underlying problem with ldap_read in that if the read returns more than a certain (not sure how many yet) number of attributes, then the ldap_read call return false. The same query succeeds when done with other tools, so I am not sure exactly what the issue is, but I will keep investigating.</p>

    <p>I would also like to volunteer to help provide documentation as I am obviously interested in seeing this make it into the framework as soon as possible. You can contact me at lance(at)lancehendrix(dot)com in regards to assisting you and Stefan with documentation or to discuss this issue.</p>

    1. Apr 30, 2009

      <p>Hi Lance,</p>

      <p>thanks four your kudos and your comment...</p>

      <p>Regarding the reported issue, I just tested this against a OpenLDAP 2.3.35 server using a Windows and a Linux PHP machine (both PHP 5.2.9) - both worked as expected. Perhaps there is an issue with your PHP version or with the LDAP libraries used when compiling your PHP binaries. If you could investigate this a little further, I'd really appreciate this.</p>

      <p>If it's really a common issue with too many attributes being returned on a single LDAP entry, I can perhaps change the code to retrieve all required attributes one by the other resulting in several LDAP queries.</p>

      <p>Regarding your proposal to help me with the documentation I'll contact you by email tomorrow.</p>

      <p>Thanks again and best regards</p>


      <p>PS: Zend_Ldap will most likely be moved to trunk after the 1.8-release. When it'll be included into a release is a question that must be answered by the Zend team.</p>

      1. May 11, 2009

        <p>This does appear to be an issue with something local to my installation and I will investigate further as I have time (working on getting a product to release at the moment). I would not suggest you change the framework as a result of this issue, as I think the way you have it currently coded is probably the best way to do it.</p>

  12. May 11, 2009

    <p>I do think I have found an issue now (I found what I thought were a couple of others, but eventually figured out it was my code and not your framework).</p>

    <p>To reproduce, I am running query on a node ($node of type Zend_Ldap_Node) $node->searchChildren() with a search filter of (objectClass=*) which returns Zend_Ldap_Node_Collection. Once I get the collection, I am "foreaching" over the collection.</p>

    <p>The issue seems to be when I get to the end of the collection (we have iterated over all available entries) and call $collection->next(), which the collection delegates to the Zend_Ldap_Collection_Iterator_Default by calling $this->_iterator->next() (class Zend_Ldap_Collection line 166 last changed in revision 13152 of the tree I am working with) which then calls ldap_next_entry() (class Zend_Ldap_Collection_Iterator_Default line 184 last changed in revision 13449) which returns false, which is expected when asking for the next entry when we are at the last entry; however, you then throw an exception of Zend_Ldap_Exception with message of 'getting next entry'.</p>

    <p>I believe the behavior should be (and the documentation on iterator semantics is very "thin" as I have implemented a few myself, so it is not clear exactly how they "should" work, IMHO) that when next is called on the last item in the iteration (that is, we just iterated beyond the last item), that neither an error is returned nor should an exception be thrown, but this fact should be "noted" so that when the next call to $iterator->valid() returns false, which will trigger an end to the foreach loop.</p>

    <p>My experience with using implicit iterators and "foreach" is that the infrastructure will request next(), then call valid(), then call current(), such that next should always return, but the next call to valid() should return false, thus current() will not be called again (for this iteration, at least until we call rewind()).</p>

    <p>I will probably implement a simple exception handler as a workaround in my code in the interim.</p>

    <p>Let me know if this is not your understanding/experience and let me know where I might be going wrong with attempting to "foreach" over the returned Zend_Ldap_Node_Collection; otherwise, this framework has been pretty much bullet-proof and rather intuitive if you are familiar with the Zend_Db framework.</p>

    <p>Thanks again for your efforts on this!</p>

    1. May 12, 2009

      <p>Hi Lance,</p>

      <p>thanks for your report, but I don't dare to tell you, that I couldn't reproduce this issue.<br />
      I added two tests to the Zend_Ldap test-suite trying to get the exception you described, but both test do not throw any exceptions. Please confirm that I understood your procedure correctly:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $node = $this->_getLdap()->getBaseNode();
      $nodes = $node->searchChildren('(objectClass=*)');
      foreach ($nodes as $rdn => $n) {
      // do nothing - just iterate

      <p>Is this what you're doing? By the way: why are you calling <code>Zend_Ldap_Node_Collection::next()</code> after a full iteration? But that's not the point - actually the code must not throw an exception in your case.</p>

      <p>Essentially the exception in <code>Zend_Ldap_Collection_Iterator_Default::next()</code> is only thrown when the LDAP servers reports an error code > <code>LDAP_SUCCESS</code> and error code != <code>LDAP_SIZELIMIT_EXCEEDED</code>. I changed the <code>Zend_Ldap_Collection_Iterator_Default::next()</code> method to include the error message in the exception message. Could you please check, what the exception message reads now?</p>

      <p>Best regards</p>