Archives for: December 2004, 02

12/02/04

Some work, and code generation part 2

- Lennert got his own blog in here. Check it out.
- Installed 2 new themes, and modified them a bit to my needs: wpc_rubric and Stockholm.
- Tried to get Gaim-Blogger to work with this Blog, I couldn't get it working tough.
- Re-installed my phpOpenTracker visitor tracker. jpGraph currently refuses to work tough ;-(


I worked some more on the PHP code generation stuff. Now there's "memberVariable" class, which can be used inside "classBuilder", so the member variables are abstracted and can easily be added to a classBuilder forged class by the application. I'm also working on memberFunction classes (like getMemberFunction, setMemberFunction and complexMemberFunction) that define class member functions. The first and second one create standard get and set methods, the third one can create custom methods.

Generating classes representing all objectClasses and attributeTypes defined in core.schema, cosine.schema, inetorgperson.schema and nis.schema takes 3.6 seconds, which is not too slow. It's the "Connecting to LDAP Server" and "Fetching schema information" parts that take the longest time, class generation is pretty quick.

One more new feature: all generated memberVariables get PHPDoc compliant comments. The memberFunctions should get it too (when I finish the classes, almost done in fact), and in the end classes should get their documentation too.


I'm trying to help Lode getting Gentoo on his brand new laptop. Not an easy one. Those damn cheap Medions :roll:

Permalink . Ikke . 10:10:59 pm . 280 Words . Life, Technology, Linux . . 433 views . 1 comment
Let PHP write code for you

I'm working on a project. A neat new server. Well, the server itself (you know, that PowerEdge 2400 I talked about yesterday) seems to be, well, screwed somehow, but I wanted to *do* somethin anyway.

The magic word is LDAP. We want everything into an LDAP directory where possible. System users, quota, email aliases, Horde preferences, mailing list members, Samba as a PDC using LDAP as userbase/password backend, everything. I was able to set all of this up on my workstation, so that won't be a big problem.

There is one major issue tough: frontends. We were not able to find any decent frontend suited to our needs. Of course there is PhpLdapAdmin, which is a great tool to browse your tree and edit base-level stuff, but it's only usefull for directory administrators. You cannot expect a normal user to use PLA to change his password.

So we have to write everything ourself. Because we'd like to give the user both webbased and CLI interfaces for all administration tasks, PHP was chosen as development platform, using PEAR's Net_LDAP modules, which is kinda neat. You can use it to retrieve LDAP Schema information, which is what I need here.

The objective is simple: create classes in PHP that represent an LDAP objectClass. So if a user in LDAP is an "inetOrgPerson", I need a class in PHP called "inetOrgPerson", like $ikke = new inetOrgPerson( "uid=ikke,dc=test,dc=com");

Because LDAP supports objectClass inheritance, this should be reflected in the PHP code too. LDAP supports multiple inheritance, a PHP class can only inherit from one class, but normally this isn't a big issue, because most objectClasses only SUP one parent objectClass.

Writing PHP files with classes representing all possible attibuteTypes and objectClasses by hand didn't look like a pleasant foresight to me, so I wanted to automate things. Of course I could write everything by hand once, but then if new schema's would be added to the LDAP server, I'd have to write more classes, change inheritance,... which is just plain stupid.

So I started to write PHP classes and functions that create PHP class files for me, based on data it retrieves from the LDAP server. At the moment, attributeTypes and objectClasses are only represented using member variables and (read) accessor functions, so something like the example I gave is not possible (yet), but it shouldn't be too hard to get that working too.

This is what it looks like now:

* schemaItem
        * atributeType inherits schemaItem
                * homeDirectory inherits attributeType
                * uid inherits attributeType
                * ...
        (attributeType inheritance isn't supported)
        * objectClass inherits schemaItem
                * top inherits objectClass
                        * person inherits top
                                * organisationalPerson inherits person
                                        * inetOrgPerson inherits organisationalPerson
                                * ...
                        * ...

Every attributeType is represented by (attributeTypeName).class.php in a certain directory, and every objectClass as (objectClassName).class.php in some other directory.

Some classes provide the factory methods to create these class files: there is the base classBuilder class, which provides the following functions:

- classBuilder( $name, $super = null )
     Constructor. $name is the name of the class to "build", $super is an optional base class $name should extend.
- addRequire( $req )
     Add a "require_once( $req )" to the output file.
- addVariable( $type, $name, $private = false )
     Add a member variable of type $type (mentioned in a comment), with name $name, to the class.
     $private is not used yet, but could be used in the future to force prefixing private variables using an
     _underscore_
- addVariableValue( $type, $name, $value, $vartype = VARIABLE_TYPE_STRING, $private = false )
     Add a member variable which has a predefined value. $type, $name and $private are the same things as in
     addVariable(3). $value is the standard value this variable should have, and $vartype defines the
     type of the variable: STRING, INT or BOOL, so strings can be written like 'var $test = "test"',
     integers like 'var $test = 1', and booleans like 'var $test = true' (watch the quotes).
- writeFile( $filename )
     Finally writes the class structure to file $filename.

Next to these there are some more private functions, like one to generate a PHP-syntax-compatible representation of an Array (also multi-dimensional arrays because the function can call itself, I think it's pretty nifty :)) )

As you can see it is not possible to add member functions to classes, which should not be necessary, because the base classes ("schemaItem", "attributeType" and "objectClass") provide the necessay ones.

Next, I got Writer classes for every type I got:

* schemaItemWriter inheits classBuilder
        * attributeTypeWriter inherits schemaItemWriter
        * objectClassWriter inherits schemaItemWriter

These classes take data from the LDAP server to give classBuilder all the information it needs to generate the class files for all objects.

This could sound very obscure, but actually it isn't. I have to admit the code is not documented tough :oops: The PhpDoc tags are there (thank you Vim and the Vim PHPDoc plugin), but the correct data isn't in there ;)

Now the results:

cat attributeTypes/email.class.php
<?php

require_once( "attributeType.class.php" );

class email extends attributeType {
        //Type: String
        var $_oid = "1.2.840.113549.1.9.1";
        //Type: String
        var $_name = "email";
        //Type: String
        var $_equality = "caseIgnoreIA5Match";
        //Type: String
        var $_substring = "caseIgnoreIA5SubstringsMatch";
        //Type: String
        var $_syntax = "1.3.6.1.4.1.1466.115.121.1.26{128}";
        //Type: Integer
        var $_maxLength = 128;
        //Type: Array
        var $_aliases = array("0" => "emailAddress","1" => "pkcs9email");

}

?>
cat objectClasses/top.class.php objectClasses/person.class.php
<?php

require_once( "objectClass.class.php" );

class top extends objectClass {
        //Type: String
        var $_oid = "2.5.6.0";
        //Type: String
        var $_name = "top";
        //Type: objectClass (Must)
        var $objectClass;

}

?>
<?php

require_once( "top.class.php" );

class person extends top {
        //Type: String
        var $_oid = "2.5.6.6";
        //Type: String
        var $_name = "person";
        //Type: Array
        var $_supClasses = array("0" => "top");
        //Type: sn (Must)
        var $sn;
        //Type: cn (Must)
        var $cn;
        //Type: userPassword (May)
        var $userPassword;
        //Type: telephoneNumber (May)
        var $telephoneNumber;
        //Type: seeAlso (May)
        var $seeAlso;
        //Type: description (May)
        var $description;
        //Type: Bool
        var $_structural = true;

}

?>
ls attributeTypes objectClasses | wc -l
224

Generating these files manually would be a PITA ;-)

As you can see this code is not ready (yet): for the objectClasses, there should be validator functions (are all Must attributes set?), accessor functions for all attributes, and last but not least, pulling and pushing data from/to the LDAP directory...

I'm very happy with the current results. Once all schema information is pulled from the server by Net_LDAP, the parsing of this data and creation of class files is very fast. Adding the other necessary functions should be a breeze (well, not really ;-)) now.

Once this is done, we need to write some simple backend for everything (an abstraction layer between the frontends and the actual objectClasses etc, something the user nor the frontend scripter should be aware of), and of course the frontends. PEAR Console_Getopt looks usefull :-)

I'm at university at the moment: there is a LanWar night going on (I'm not gaming, but well...).

Permalink . Ikke . 03:30:22 am . 1109 Words . Technology . . 951 views . 3 comments