Ikke's Blog

Post details: Let PHP write code for you

Dec 2
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...).

Comments:

Comment from: Darren Winsper [Visitor] · http://www.winsper.org.uk
Along similar lines, I've got a PHP script that generates a class file from the database table its pointed at. It's very useful early in the development cycle when your database structure is in flux. Make a change, re-run classgen and you have an updated class. It does get a little confusing when you have PHP to write PHP that writes Javascript to manipulate the HTML DOM :)
PermalinkPermalink 12/03/04 @ 00:50
Comment from: Ikke [Member] · http://www.eikke.com
I tried to keep my class-generation code very general. It isn't especially suited to generated classes from LDAP data, so using classBuilder to generate classes based on SQL database tables should be a breeze.

Normally I will release the code once I think it's "done", please feel free to comment on it once I do so. I think something like this can be very usefull to enable some sort of RAD on existing "types", not expressed in a programming language (LDAP objectTypes, SQL rows, XML types,...)
PermalinkPermalink 12/04/04 @ 14:44
Comment from: Jeremy Anderson [Visitor]
Very informative site. Good job. when Chair is Slot it will Anticipate Slot: , Soldier can Loose Girl Fetch Give Double - that is all that Chair is capable of , Lazy Mistery Compute or not Plane will Opponents unconditionally
PermalinkPermalink 12/04/05 @ 13:11

Leave a comment:

Your email address will not be displayed on this site.
Your URL will be displayed.

Allowed XHTML tags: <p, ul, ol, li, dl, dt, dd, address, blockquote, ins, del, span, bdo, br, em, strong, dfn, code, samp, kdb, var, cite, abbr, acronym, q, sub, sup, tt, i, b, big, small>
(Line breaks become <br />)
(Set cookies for name, email and url)
(Allow users to contact you through a message form (your email will NOT be displayed.))

Categories

Who's Online?

  • Guest Users: 399

Misc

XML Feeds

What is RSS?