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_Session_SaveHandler_DbTable Component Proposal

Proposed Component Name Zend_Session_SaveHandler_DbTable
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Session_SaveHandler_DbTable
Proposers Jordan Raub
Felix Jendrusch
Darby Felton, Zend liaison
Revision
  • 1.0 - 4 March 2007: Added proposal draft
  • 1.1 - 20 July 2007: Removed optional Zend_ACL dependency.
  • 1.2 - 24 July 2007: Removed optional Zend_Auth dependency and clarified Zend_Db_Table to Zend_Session_SaveHandler_DbTable relationships
  • 1.3 - 30 July 2007: Added Save_Path and Session name support (both optional). Unit tests written for virtually all configuration modes
  • 1.4 - 1 Aug 2007: Zend_Session_SaveHandler_Db changed to Zend_Session_SaveHandler_DbTable
(wiki revision: 19)

Table of Contents

1. Overview

Zend_Session_SaveHandler_DbTable is a database independent adapter to save session information in a database

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Zend_Session_SaveHandler_DbTable will save session data in a database independent way

4. Dependencies on Other Framework Components

  • Zend_Db_Table
  • Zend_Session_SaveHandler_Interface

5. Theory of Operation

Since Zend_Session_SaveHandler_Interface extends Zend_Db_Table, all normal table operations are available. Also when instantiating a new Zend_Session_SaveHandler_Interface($config), the $config array is a combined config for Zend_Db_Table and Zend_Session_SaveHandler_Interface. So the 'name' and 'primary' keys in my below use case refer to Zend_Db_Table config keys.

In this table id is the actual session ID given to the user. This session id depends on session.hash_function and session.hash_bits_per_character for length I use sha1 with 5bits/char so 32 chars. The defaults for php are session.hash_function refers to md5 and session.hash_bits_per_character = 4. see http://php.net/session

Session modification time in the implemented tables is currently a Unix Timestamp.

Session data column contains the serialized $_SESSION array, as is in the regular save handler seen in files.

Zend_Session_SaveHandler_DbTable saves all data from a session into a single DB table.

Dump of a basic MySQL tables for use with Zend_Session_SaveHandler_DbTable:

UC-01

CREATE TABLE `Sessions` (
`id` varchar(32) NOT NULL,
`modified` int(11) default NULL,
`lifetime` int(11) default NULL,
`data` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB

UC-02

CREATE TABLE `Sessions` (
`id` varchar(32) NOT NULL,
`save_path` varchar(32) NOT NULL,
`name` varchar(32) NOT NULL default '',
`modified` int(11) default NULL,
`lifetime` int(11) default NULL,
`data` text,
PRIMARY KEY (`id`,`save_path`,`name`)
) ENGINE=InnoDB;

6. Milestones / Tasks

  • Milestone 1: [DONE] Write Proposal
  • Milestone 2: [DONE] Basic Functionallity working with Unit tests
  • Milestone 3: Working prototype checked into the incubator supporting use case #1 along with Unit tests

7. Class Index

  • Zend_Session_SaveHandler_DbTable
  • Zend_Session_SaveHandler_Exception

8. Use Cases

UC-01

The simplest way to use Zend_Session_SaveHandler_DbTable

Now you can use Zend_Session as normal, also if you have some legacy code you can use $_SESSION as you normally would. Everything will be saved in the DB table.

UC-02

You can also have a column for the session Name and/or session.save_path

UC-03

You can also make sessions persistant

9. Class Skeletons

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Mar 04, 2007

    <p>Thanks to Ralf Eggert for the help.</p>

  2. Mar 13, 2007

    <p>Thanks for starting the work on the Zend_Session_SaveHandler_Db proposal since the lack of time kept me from doing it myself in the last weeks. Just a couple of things.</p>

    <p>First, I am not quite sure what you want to do with the "role" field. Maybe you could explain this a little more. I also think using a "user id" rather than the "user name" might be a better approach. Maybe this field could be called "user identifier" since some might use an id or a unique name to identify the user.</p>

    <p>I attached my current version of a Zend_Session_SaveHandler_Db class to this proposal. Please don't worry about the naming since it should be renamed to Zend_Session_SaveHandler_Db if the class is finished. </p>

    <p><a class="external-link" href="http://framework.zend.com/wiki/download/attachments/20924/Db.php.txt">http://framework.zend.com/wiki/download/attachments/20924/Db.php.txt</a></p>

    <p>From my work with this class, I really think we need a hook or something for the write() method to allow programmers to add the own functionalities when saving the session data. For example, I needed to add the possibility to use permanent sessions if some one wants to save his login permanently. I also needed to save the member id in the session table. This logic is currently stuffed into the write() method but I think Zend_Session_SaveHandler_Db should really provide a hook for individual changes.</p>

    <p>Or maybe the need for permanent sessions and saving a user identifier is a basic requirement which fits the 80/20 rule. Then it should be implemented in the save handler right away.</p>

    1. Mar 13, 2007

      <p>I thought the role could be similar for the user identifier but for the parent role of this user. This does have a dependency with ACL though.</p>

      <p>I agree with the "user identifier" idea since in my testing i am using a varchar(32) as a unique id that is also the primary key in my users table. whereas someone else may use an ID like you suggest.</p>

      <p>I like the idea of permanent sessions, maybe in the table definition have a flag for permanent sessions?</p>

      1. Mar 13, 2007

        <p>I haven't had a chance to really dig into Zend_Acl and Zend_Auth, but is the 'role' field sufficient? Are roles limited to short strings?</p>

        1. Mar 13, 2007

          <p>Well, as I understand, the ACL roles and resources are stored as strings which could then be stored in a db. There are users which inherit from other roles in your defined permissions hierarchy. So what I'm thinking is having something like this: Dean <- Prof <- TA <- student. Each of these inherit from the parent. so Student Jordan would have role => 'student' and Prof Ralf would have role => 'prof' in the table. But also maybe this isn't as shown, but as Directed Acyclic Graph (DAG). i.e. TA's inherit from Prof and Students inherit from Prof, but they each inherit from different things. </p>

          <p>Dean <- Prof <- GA <- student<br />
          Dean <- Prof <- TA <- student</p>

          <p>here students have the least privileges then GA's and TA's but they both have different (maybe similar) privileges. Then profs have the general privileges of both, etc.</p>

          <p>So, in the Table for Role<<span style="text-decoration: line-through;">>Role relationships there might also be role<</span>>user relationships? And, this is where the role in the proposal comes from.</p>

          <p>This may not fit the general scenario. I'm guessing this is off topic for this, but throwing the idea out there anyway.</p>

  3. Jul 20, 2007

    <p>I've pulled out the Zend_Acl dependency since a user may have multiple roles so a 1-1 relationship doesn't make sense.</p>

    <p> I look forward to feedback!</p>

  4. Jul 24, 2007

    <p>Following are notes from my initial review, organized by section. These are just my opinions, so please feel free to rebut my assertions! <ac:emoticon ac:name="smile" /></p>

    <h3>2. References</h3>
    <ul>
    <li>Add some references; suggestions follow:
    <ul>
    <li><a class="external-link" href="http://framework.zend.com/manual/en/zend.session.html">http://framework.zend.com/manual/en/zend.session.html</a></li>
    <li><a class="external-link" href="http://www.php.net/session">http://www.php.net/session</a></li>
    <li><a class="external-link" href="http://www.php.net/manual/en/function.session-set-save-handler.php">http://www.php.net/manual/en/function.session-set-save-handler.php</a></li>
    </ul>
    </li>
    </ul>

    <h3>3. Requirements, Constraints, and Acceptance Criteria</h3>
    <ul>
    <li>Define what it is to "work in a seamless way," or remove the associated criterion.</li>
    <li>"Zend_Session_SaveHandler_Db will optionally save username information from Zend_Auth and also pull role information for Zend_Acl":
    <ul>
    <li>A Zend_Auth identity need not be a username; it could be of a different semantic type (e.g., integer primary key, e-mail address).</li>
    <li>What role information is retrieved from Zend_Acl? For what purpose(s)? Why would this not be done at the application level?</li>
    <li>This component should focus on saving data; it should not be aware of the semantics of such data (e.g., what user information to store, roles to retrieve from Zend_Acl).</li>
    </ul>
    </li>
    <li>Additional requirements, constraints, and acceptance criteria should be added to reflect decisions on these and others' suggestions.</li>
    </ul>

    <h3>4. Dependencies on Other Framework Components</h3>
    <ul>
    <li>Remove the optional Zend_Auth requirement, unless it is explained what dependencies exist and for what purposes?</li>
    </ul>

    <h3>5. Theory of Operation</h3>
    <ul>
    <li>Add some explanations of how the component will operate, what functions it performs, etc.</li>
    <li>Describe the available configuration options:
    <ul>
    <li><code>name</code>
    <ul>
    <li>Is this the DB table name? Maybe a better option name would be "<code>tableName</code>"?</li>
    </ul>
    </li>
    <li><code>user</code>
    <ul>
    <li>Explain why this is needed, or remove it from the option list.</li>
    </ul>
    </li>
    <li><code>expire</code>
    <ul>
    <li>Maybe a better name would be "<code>columnExpire</code>"?</li>
    </ul>
    </li>
    <li><code>lifetime</code>
    <ul>
    <li>Why is this needed separately from Zend_Session? Zend_Session exposes PHP session options such as <code>session.gc_maxlifetime</code>.</li>
    </ul>
    </li>
    <li><code>primary</code>
    <ul>
    <li>Explain for what purpose this option is available, or remove it from the option list.</li>
    <li>How does this depend on <code>session.hash_function</code> and <code>session.hash_bits_per_character</code>?</li>
    <li>How to specify compound keys or is this not allowed? (e.g., array('column1', 'column2'))</li>
    </ul>
    </li>
    <li>Are there any other available options that are not listed?</li>
    </ul>
    </li>
    <li>Explain the setup requirements (e.g., SQL DDL)
    <ul>
    <li>List the acceptable data types for the 'expire' column.</li>
    <li>Describe any table constraints, such as for uniqueness.</li>
    <li>Illustrate with additional sample table schemas, perhaps for varied RDBMS vendors.</li>
    </ul>
    </li>
    </ul>

    <h3>8. Use Cases</h3>
    <ul>
    <li>Additional use cases would help to cover all available functionality and to solve various scenarios that are expected to be commonly encountered by users.</li>
    </ul>

    1. Jul 25, 2007

      <p>I sat down to redo this again and was halfway done before I saw your comments. <ac:emoticon ac:name="smile" /><br />
      I had already decided that having Zend_Auth optional wasn't the greatest idea, but I do appreciate the other comments.<br />
      In the redone proposal you can see that name and primary are the same as in Zend_Db_Table, so you can have compound keys just use the 'primary' key in your $config as you normally would for Zend_Db_Table. Similar for name.<br />
      I did send something about this in the mailing list but what do you think of these ideas?</p>
      <ul>
      <li>What if session.save_path is used as the default for the Zend_Session_SaveHandler_Db table name? I'm thinking a path in a file system is analogous to a table in a database.</li>
      <li>This one might be more contrived, session.name could be the default name of the data column in a Zend_Session_SaveHandler_Db database table.</li>
      </ul>

      1. Jul 30, 2007

        <p>Great! The proposal looks overall much better to me now. <ac:emoticon ac:name="smile" /></p>

        <ul>
        <li>About using <code>session.save_path</code> as the table name, have you tested this? I seem to recall there being some problem with <code>session.save_path</code> not being a valid, writable filesystem path for ext/session. Maybe this does not apply when using a custom handler?</li>
        </ul>

        <ul>
        <li>I suppose that <code>session.name</code> could be used as the default name of the data column in the database table, but recall that this is the name used for the cookie and/or GET parameter value that identifies the session, which seems a bit of a mismatch (since the data column stores data, not the session identifier). I think it's probably a bit confusing, to be honest. Maybe I misunderstand?</li>
        </ul>

        <p>Though there are some other wrinkles that we could try to iron out here, my feeling is that the proposal is strong enough to warrant granting you the SVN access needed to work on this new component in the incubator.</p>

        <p>I'll see if the rest of the team agrees, and I'll let you know!</p>

        <p>Thanks for this nice proposal!</p>

        1. Jul 30, 2007

          <p>OOPS! I forgot to mention that part. <ac:emoticon ac:name="smile" /><br />
          The functionality for the session.save_path being the default table name is already there <ac:emoticon ac:name="smile" /></p>

          <p>If $config<ac:link><ri:page ri:content-title="'name'" /></ac:link> is not set, ini_get('session.save_path') is substituted for table name. a split on . returns schema and table name. (schema is not required)</p>

  5. Jul 29, 2007

    <p>I like the current state of this proposal with fewer dependencies very much. Not everyone is using Zend_Auth/Zend_Acl. It would be good to have just a simple class to store sessions in a database. Having that it would be very easy to extend it to create more functionality.</p>

  6. Aug 02, 2007

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comments</ac:parameter><ac:rich-text-body>
    <p>This proposal is approved for incubator development.</p>

    <p>Since it has a strong correlation to Zend_Db_Table, maybe a better name would be Zend_Session_SaveHandler_DbTable? This would be consistent with Zend_Auth_Adapter_DbTable.</p></ac:rich-text-body></ac:macro>

  7. Aug 06, 2007

    <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[I have tried the Zend_Session_SaveHandler_DbTable form Incubator but i am having errors
    in my index
    $configs = new Zend_Config_Ini('../config/config.ini', 'session');
    Zend_Session::setSaveHandler(new Zend_Session_SaveHandler_DbTable($configs->toArray()));
    Zend_Session::start()
    i get error
    Fatal error: Uncaught exception 'Zend_Session_Exception' with message 'Zend_Session::start() - Trying to get property of non-object' in C:\www\sou2l\library\Zend\Session.php:379 Stack trace: #0 C:\www\sou2l\document_root\index.php(23): Zend_Session::start() #1

    Unknown macro: {main}

    thrown in C:\www\sou2l\library\Zend\Session.php on line 379

    ]]></ac:plain-text-body></ac:macro>

    1. Aug 07, 2007

      <p>What is your config and your table setup?</p>

      1. Aug 07, 2007

        <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[Strange, we're having the same issue. It works fine on the first page load, when the sessions table is empty. Once a row is inserted, it fails on subsequent page loads (row updates I presume) with:

        Fatal error: Uncaught exception 'Zend_Session_Exception' with message 'Zend_Session::start() - Trying to get property of non-object' in /home/web/lib/Zend/Session.php:379 Stack trace: #0 /home/web/public/index.php(72): Zend_Session::start() #1

        Unknown macro: {main}

        thrown in /home/web/lib/Zend/Session.php on line 379

        Doing a DELETE FROM `sessions` fixes it for the next load.

        Table structure:

        CREATE TABLE `sessions` (
        `id` varchar(32) NOT NULL,
        `modified` int(11) default NULL,
        `lifetime` int(11) default NULL,
        `data` text,
        PRIMARY KEY (`id`)
        ) ENGINE=InnoDB

        (there are additional trailing commas at the ends of the two CREATE TABLE statements at the top of this wiki article)

        Here's how we're bootstrapping:

        // initialize and connect to the database (pdo_mysql)
        // (buffered queries and persistent connections enabled)
        $db = ...
        $db->getConnection();

        Zend_Loader::loadClass('Zend_Db_Table');
        Zend_Db_Table::setDefaultAdapter($db);

        // initialize and start session
        Zend_Loader::loadClass('Zend_Session');
        Zend_Loader::loadClass('Zend_Session_SaveHandler_DbTable');

        $sessionDbParams = array(
        'name' => 'sessions', //table name as per Zend_Db_Table
        'primary' => 'id', //the sessionID given by php
        'modifiedColumn' => 'modified', //time the session should expire
        'dataColumn' => 'data', //serialized data
        'lifetimeColumn' => 'lifetime' //end of life for a specific record
        );

        Zend_Session::setSaveHandler(new Zend_Session_SaveHandler_DbTable($sessionDbParams));
        Zend_Session::start();

        Haven't had time to really dive in and see where it's failing... I'll take a look later tonight, but just thought I'd post since someone else is experiencing it.]]></ac:plain-text-body></ac:macro>

        1. Aug 07, 2007

          <p>fixed in repository</p>

          1. Aug 07, 2007

            <p>That was fast - it's working great Jordan, thanks for the fix and a great component in general.</p>

  8. Aug 08, 2007

    <p>It works <ac:emoticon ac:name="smile" /> thanks</p>

  9. Aug 10, 2007

    <p>Just a quick note, your table definitions wouldn't import straight to MySQL for me.  The comma at the end of the PK line caused it to throw an error...</p>

    1. Aug 14, 2007

      <p>fixed it, thx <ac:emoticon ac:name="smile" /></p>

  10. Aug 10, 2007

    <p>I'm not very well versed in most RDBMS, but I do have a db session handler written in php5 for mysql. I used something like:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    `modified` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
    ]]></ac:plain-text-body></ac:macro>
    <p>Do most RDBMS have this kind of option?</p>

    <p> Also, Is there a way currently to add additional data into the session table?  For example, in my straight PHP solution, I had a uid column, and an ip column.  The IP I filled in with $_SERVER['REMOTE_ADDR'] and the other with the users id if they were logged in.  The reason was that I had a way to view currently logged in users (SELECT ... WHERE uid NOT NULL) including their IP, and it even allows you to then kick a user off the system by removing their session row.</p>

  11. Aug 21, 2007

    <p>Can't be 100% sure but it seems this breaks Zend_Db_Profiler (sort of). It looks like the profiler is still giving some basic output but spits fatal errors trying to load Zend/Db/Profiler/Query.php. All seems normal when I revert back to file-based session handling... I'll glance at paths and bootstrap again in case I've done something stupid.</p>

    1. Aug 27, 2007

      <p>I would think this might have to do with the order of the destructors being called although I will look into this. </p>

  12. Oct 02, 2007

    <p>Does any else have an issue with this session handler regenerating IDs and inserting them into new rows in the db as opposed to updating the respective row?</p>

    <p>This happens when I hold down <cmd>+r (<ctrl>+r for you windows users) for a rapid refresh.</p>

    <p>This is a simple test I run for performance and stress testing. I try to see how many page loads in a given period. Easier than putting in bench specific code.</p>

    <p>When I do the rapid refresh test the session handler does not give any errors, but creates a new session. This does not happen if I press <cmd>+r slowly. My page counter increments as it should.</p>

    <p>As a note when the session handler creates a new row, the last session is ignored and abandoned, no info is transferred from the `data` field. Here is a dump of  my date:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    mysql> select * from Sessions;
    -----------------------------------------------------------------------------------------+

    id modified lifetime data

    -----------------------------------------------------------------------------------------+

    127e86a27b6f5104c7c03dad9e379576 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    17695c011630fa1a1029b9814cb3cce3 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    2cc3f6306f78b002c3297d3045a9c760 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    37989584220d165d2ca615cd9912e9eb 1191218801 2592000 stats a:1:
    Unknown macro: {s}

    422d6a23c3308f2ab143c888388c94f0 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    47f279f9aa66e98448cdc76bad42e310 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    9e8e01ce6520eabe9b68179cc6b36dac 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    b3ce9e5b0b230630d631a6e039e3ce3d 1191218801 2592000 stats a:1:
    Unknown macro: {s}

    ca0de03c5b09f17bfa371d5692efc840 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    cdf01158504180e9509f1a9f3e475dee 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    d16a552ccc93671799e0c775e4775315 1191218809 2592000 stats a:1:
    Unknown macro: {s}

    e9391c2e2b65ad436602966ea11460b4 1191218802 2592000 stats a:1:
    Unknown macro: {s}

    -----------------------------------------------------------------------------------------+

    12 rows in set (0.00 sec)
    ]]></ac:plain-text-body></ac:macro>

    <p>As you can see the 4th and 2nd to last row have pages_loaded incremented. The others were created by quickly holding <cmd>+r.</p>

    <p>I have not had a lot of time to debug this. I thought I was keeping it simple.</p>

    <p>If other people could try the refresh test to see if this happens on your box, please let me know, It may make trouble shooting easier.</p>

    <p>I am running on a Macbook Pro. I will not be able to test this on my FreeBSD Unix server until next week.</p>

    <p>Thanks,</p>

    <p>Jeremy</p>

    <p>P.S. This is the same email I posted in the general mailing list.</p>

  13. Oct 02, 2007

    <p>In regards to the refresh test...</p>

    <p>So is this a bug or a feature?</p>

  14. Oct 09, 2007

    <p>Hi Jeremy,</p>

    <p>I don't seem to have this problem.</p>

    <p>My platform is Debian Etch, MySQL v5 and PHP5.2.</p>

    <p>Best regards,</p>

    <p>Patrick</p>

    1. Oct 09, 2007

      <p>Ehr Jeremy, let me rephrase that: I DO have this problem too!<br />
      When I do a Zend_Session::regenerateId(); and press F5 (Page Refresh) a few times quick after each other multiple sessions get inserted into the database.</p>

      1. Oct 09, 2007

        <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[By the way: I've also tested it with file-based sessions, and this problem occurs here too.

        config.ini:

        sessions.save_path = /home/patrick/web/framework/application/var/sessions
        sessions.strict = on
        ;sessions.save_handler =
        ;sessions.name =
        sessions.gc_probability = 1
        sessions.gc_divisor = 100
        ;sessions.gc_maxlifetime =
        sessions.hash_function = 1
        sessions.hash_bits_per_character = 4

        bootstrap.php:

        Zend_Session::setOptions($config->sessions->toArray());
        Zend_Session::start();

        IndexController.php:

        class IndexController extends Zend_Controller_Action
        {
        public function indexAction()

        Unknown macro: { Zend_Session}

        }

        I am wondering why this problem would occur?

        ]]></ac:plain-text-body></ac:macro>

        1. Oct 09, 2007

          <p>It seems that this is an internal problem of PHP's function <strong>session_regenerate_id(true);</strong></p>

          <p>Try it yourself:</p>
          <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
          <?php
          session_start();

          $old_sessionid = session_id();

          session_regenerate_id(true);

          $new_sessionid = session_id();

          echo "Old Session: $old_sessionid<br />";
          echo "New Session: $new_sessionid<br />";

          print_r($_SESSION);

          echo session_save_path();

          ?>
          ]]></ac:plain-text-body></ac:macro>

          1. Oct 09, 2007

            <p>I've filed a bug report at php.net: <a class="external-link" href="http://bugs.php.net/bug.php?id=42899">http://bugs.php.net/bug.php?id=42899</a></p>

            1. Oct 10, 2007

              <p>Your script shows the expected behavior. using session_regenerate_id gives a new session id so every page load of that test script will have a new id. That is not a bug.</p>

              <p>Please check the mailing list archives for more information on Jeremy Postlethwaite's problem</p>

              <p>Also, please log all bug reports into JIRA <a class="external-link" href="http://framework.zend.com/issues/secure/Dashboard.jspa">http://framework.zend.com/issues/secure/Dashboard.jspa</a> or just use the mailing list.</p>

              <p>Thanks,<br />
              Jordan</p>

  15. Nov 05, 2007

    <p>There ara no tests and documentation in incubator for this class. Could you write them ?</p>

  16. Feb 07, 2008

    <p>Hi</p>

    <p>This very useful class just need some attention. With tests and documentation it could get into core.</p>

    <p>regards<br />
    holo</p>

  17. Jul 16, 2008

    <p>According to JIRA,this component has just been promoted to core. Yay! <ac:emoticon ac:name="smile" /></p>