code:
<?php
/**
 * Prototype Castable Object.. for DataObject queries
 *
 * Storage for Data that may be cast into a variety of formats.
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.01 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   Database
 * @package    DB_DataObject
 * @author     Alan Knowles <alan@akbkhome.com>
 * @copyright  1997-2008 The PHP Group
 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
 * @version    CVS: $Id: Cast.php 287158 2009-08-12 13:58:31Z alan_k $
 * @link       http://pear.php.net/package/DB_DataObject
 */

/**
 *
 * Common usages:
 *   // blobs
 *   $data = DB_DataObject_Cast::blob($somefile);
 *   $data = DB_DataObject_Cast::string($somefile);
 *   $dataObject->someblobfield = $data
 *
 *   // dates?
 *   $d1 = new DB_DataObject_Cast::date('12/12/2000');
 *   $d2 = new DB_DataObject_Cast::date(2000,12,30);
 *   $d3 = new DB_DataObject_Cast::date($d1->year, $d1->month+30, $d1->day+30);
 *
 *   // time, datetime.. ?????????
 *
 *   // raw sql????
 *    $data = DB_DataObject_Cast::sql('cast("123123",datetime)');
 *    $data = DB_DataObject_Cast::sql('NULL');
 *
 *   // int's/string etc. are probably pretty pointless..!!!!
 *
 *
 *   inside DB_DataObject,
 *   if (is_a($v,'db_dataobject_class')) {
 *           $value .= $v->toString(DB_DATAOBJECT_INT,'mysql');
 *   }
 *
 *
 *
 *

 */
class DB_DataObject_Cast
{

    /**
     * Type of data Stored in the object..
     *
     * @var string       (date|blob|.....?)
     * @access public
     */
    public $type;

    /**
     * Data For date representation
     *
     * @var int  day/month/year
     * @access public
     */
    public $day;
    public $month;
    public $year;

    /**
     * Generic Data..
     *
     * @var string
     * @access public
     */

    public $value;
    /**
     * Data For time representation ** does not handle timezones!!
     *
     * @var int  hour/minute/second
     * @access public
     */
    public $hour;
    public $minute;
    public $second;

    /**
     * Blob consructor
     *
     * create a Cast object from some raw data.. (binary)
     *
     *
     * @param string (with binary data!)
     *
     * @return   object DB_DataObject_Cast
     * @access   public
     */

    public static function blob($value)
    {
        $r = new DB_DataObject_Cast;
        $r->type = 'blob';
        $r->value = $value;
        return $r;
    }

    /**
     * String consructor (actually use if for ints and everything else!!!
     *
     * create a Cast object from some string (not binary)
     *
     *
     * @param string (with binary data!)
     *
     * @return   object DB_DataObject_Cast
     * @access   public
     */

    public static function string($value)
    {
        $r = new DB_DataObject_Cast;
        $r->type = 'string';
        $r->value = $value;
        return $r;
    }

    /**
     * SQL constructor (for raw SQL insert)
     *
     * create a Cast object from some sql
     *
     * @param string (with binary data!)
     *
     * @return   object DB_DataObject_Cast
     * @access   public
     */

    public static function sql($value)
    {
        $r = new DB_DataObject_Cast;
        $r->type = 'sql';
        $r->value = $value;
        return $r;
    }

    /**
     * DateTime Constructor
     *
     * create a Cast object from a Date/Time
     * Maybe should accept a Date object.!
     * NO VALIDATION DONE, although some crappy re-calcing done!
     *
     * @param vargs... accepts
     *              noargs (now)
     *              yyyy-mm-dd HH:MM:SS (Iso)
     *              array(yyyy,mm,dd,HH,MM,SS)
     *
     *
     * @return bool|object
     * @access   public
     * @author   therion 5 at hotmail
     */

    public static function dateTime()
    {
        $args = func_get_args();
        switch (count($args)) {
            case 0: // no args = now!
                $datetime = date('Y-m-d G:i:s', time());
                // no break

            case 1:
                // continue on from 0 args.
                if (!isset($datetime)) {
                    $datetime = $args[0];
                }

                $parts = explode(' ', $datetime);
                $bits = explode('-', $parts[0]);
                $bits = array_merge($bits, explode(':', $parts[1]));
                break;

            default: // 2 or more..
                $bits = $args;

        }

        if (count($bits) != 6) {
            // PEAR ERROR?
            return false;
        }

        $r = DB_DataObject_Cast::date($bits[0], $bits[1], $bits[2]);
        if (!$r) {
            return $r; // pass thru error (False) - doesnt happen at present!
        }
        // change the type!
        $r->type = 'datetime';

        // should we mathematically sort this out..
        // (or just assume that no-one's dumb enough to enter 26:90:90 as a time!
        $r->hour = $bits[3];
        $r->minute = $bits[4];
        $r->second = $bits[5];
        return $r;
    }

    /**
     * Date Constructor
     *
     * create a Cast object from some string (not binary)
     * NO VALIDATION DONE, although some crappy re-calcing done!
     *
     * @param vargs... accepts
     *       dd/mm
     *       dd/mm/yyyy
     *       yyyy-mm
     *       yyyy-mm-dd
     *       array(yyyy,dd)
     *       array(yyyy,dd,mm)
     *
     *
     *
     * @return   object DB_DataObject_Cast
     * @access   public
     */

    public static function date()
    {
        $args = func_get_args();
        switch (count($args)) {
            case 0: // no args = today!
                $bits = explode('-', date('Y-m-d'));
                break;
            case 1: // one arg = a string

                if (strpos($args[0], '/') !== false) {
                    $bits = array_reverse(explode('/', $args[0]));
                } else {
                    $bits = explode('-', $args[0]);
                }
                break;
            default: // 2 or more..
                $bits = $args;
        }
        if (count($bits) == 1) { // if YYYY set day = 1st..
            $bits[] = 1;
        }

        if (count($bits) == 2) { // if YYYY-DD set day = 1st..
            $bits[] = 1;
        }

        // if year < 1970 we cant use system tools to check it...
        // so we make a few best gueses....
        // basically do date calculations for the year 2000!!!
        // fix me if anyone has more time...
        if (($bits[0] < 1975) || ($bits[0] > 2030)) {
            $oldyear = $bits[0];
            $bits = explode('-', date('Y-m-d', mktime(1, 1, 1, $bits[1], $bits[2], 2000)));
            $bits[0] = ($bits[0] - 2000) + $oldyear;
        } else {
            // now mktime
            $bits = explode('-', date('Y-m-d', mktime(1, 1, 1, $bits[1], $bits[2], $bits[0])));
        }
        $r = new DB_DataObject_Cast;
        $r->type = 'date';
        list($r->year, $r->month, $r->day) = $bits;
        return $r;
    }

    /**
     * time Constructor
     *
     * create a Cast object from a Date/Time
     * Maybe should accept a Date object.!
     * NO VALIDATION DONE, and no-recalcing done!
     *
     * @param vargs... accepts
     *              noargs (now)
     *              HH:MM:SS (Iso)
     *              array(HH,MM,SS)
     *
     *
     * @return bool|object
     * @access   public
     * @author   therion 5 at hotmail
     */
    public static function time()
    {
        $args = func_get_args();
        switch (count($args)) {
            case 0: // no args = now!
                $time = date('G:i:s', time());
                // no break

            case 1:
                // continue on from 0 args.
                if (!isset($time)) {
                    $time = $args[0];
                }
                $bits = explode(':', $time);
                break;

            default: // 2 or more..
                $bits = $args;

        }

        if (count($bits) != 3) {
            return false;
        }

        // now take data from bits into object fields
        $r = new DB_DataObject_Cast;
        $r->type = 'time';
        $r->hour = $bits[0];
        $r->minute = $bits[1];
        $r->second = $bits[2];
        return $r;
    }

    /**
     * get the string to use in the SQL statement for this...
     *
     *
     * @param bool $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     */

    public function toString($to = false, $db)
    {
        // if $this->type is not set, we are in serious trouble!!!!
        // values for to:
        $method = 'toStringFrom' . $this->type;
        return $this->$method($to, $db);
    }

    /**
     * get the string to use in the SQL statement from a blob of binary data
     *   ** Suppots only blob->postgres::bytea
     *
     * @param int $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     */
    public function toStringFromBlob($to, $db)
    {
        // first weed out invalid casts..
        // in blobs can only be cast to blobs.!

        // perhaps we should support TEXT fields???

        if (!($to & DB_DATAOBJECT_BLOB)) {
            return (new PEAR)->raiseError('Invalid Cast from a DB_DataObject_Cast::blob to something other than a blob!');
        }

        switch ($db->dsn["phptype"]) {
            case 'pgsql':
                return "'" . pg_escape_bytea($this->value) . "'::bytea";

            case 'mysql':
                return "'" . mysql_real_escape_string($this->value, $db->connection) . "'";

            case 'mysqli':
                // this is funny - the parameter order is reversed ;)
                return "'" . mysqli_real_escape_string($db->connection, $this->value) . "'";

            case 'sqlite':
                // this is funny - the parameter order is reversed ;)
                return "'" . sqlite_escape_string($this->value) . "'";

            case 'mssql':

                if (is_numeric($this->value)) {
                    return $this->value;
                }
                $unpacked = unpack('H*hex', $this->value);
                return '0x' . $unpacked['hex'];

            default:
                return (new PEAR)->raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
        }
    }

    /**
     * get the string to use in the SQL statement for a blob from a string!
     *   ** Suppots only string->postgres::bytea
     *
     *
     * @param int $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     */
    public function toStringFromString($to, $db)
    {
        // first weed out invalid casts..
        // in blobs can only be cast to blobs.!

        // perhaps we should support TEXT fields???
        //

        // $to == a string field which is the default type (0)
        // so we do not test it here. - we assume that number fields
        // will accept a string?? - which is stretching it a bit ...
        // should probaly add that test as some point.

        switch ($db->dsn['phptype']) {
            case 'pgsql':
                return "'" . pg_escape_string($this->value) . "'::bytea";

            case 'mysql':
                return "'" . mysql_real_escape_string($this->value, $db->connection) . "'";

            case 'mysqli':
                return "'" . mysqli_real_escape_string($db->connection, $this->value) . "'";

            case 'mssql':
                // copied from the old DB mssql code...?? not sure how safe this is.
                return "'" . str_replace(
                    array("'", "\\\r\n", "\\\n"),
                    array("''", "\\\\\r\n\r\n", "\\\\\n\n"),
                    $this->value
                ) . "'";

            default:
                return (new PEAR)->raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
        }
    }

    /**
     * get the string to use in the SQL statement for a date
     *
     *
     *
     * @param int $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     */
    public function toStringFromDate($to, $db)
    {
        // first weed out invalid casts..
        // in blobs can only be cast to blobs.!
        // perhaps we should support TEXT fields???
        //

        if (($to !== false) && !($to & DB_DATAOBJECT_DATE)) {
            return (new PEAR)->raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a date!' .
                ' (why not just use native features)');
        }
        return "'{$this->year}-{$this->month}-{$this->day}'";
    }

    /**
     * get the string to use in the SQL statement for a datetime
     *
     *
     *
     * @param int $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     * @author   therion 5 at hotmail
     */

    public function toStringFromDateTime($to, $db)
    {
        // first weed out invalid casts..
        // in blobs can only be cast to blobs.!
        // perhaps we should support TEXT fields???
        if (($to !== false) &&
            !($to & (DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME))) {
            return (new PEAR)->raiseError('Invalid Cast from a ' .
                ' DB_DataObject_Cast::dateTime to something other than a datetime!' .
                ' (try using native features)');
        }
        return "'{$this->year}-{$this->month}-{$this->day} {$this->hour}:{$this->minute}:{$this->second}'";
    }

    /**
     * get the string to use in the SQL statement for a time
     *
     *
     *
     * @param int $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     * @author   therion 5 at hotmail
     */

    public function toStringFromTime($to, $db)
    {
        // first weed out invalid casts..
        // in blobs can only be cast to blobs.!
        // perhaps we should support TEXT fields???
        if (($to !== false) && !($to & DB_DATAOBJECT_TIME)) {
            return (new PEAR)->raiseError('Invalid Cast from a' .
                ' DB_DataObject_Cast::time to something other than a time!' .
                ' (try using native features)');
        }
        return "'{$this->hour}:{$this->minute}:{$this->second}'";
    }

    /**
     * get the string to use in the SQL statement for a raw sql statement.
     *
     * @param int $to Type (DB_DATAOBJECT_*
     * @param object $db DB Connection Object
     *
     *
     * @return   string
     * @access   public
     */
    public function toStringFromSql($to, $db)
    {
        return $this->value;
    }
}

repo_name: ksuudilla/gnu

license: mit


code:
<?php
/**
 * DataObjects error handler, loaded on demand...
 *
 * DB_DataObject_Error is a quick wrapper around pear error, so you can distinguish the
 * error code source.
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.01 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   Database
 * @package    DB_DataObject
 * @author     Alan Knowles <alan@akbkhome.com>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
 * @version    CVS: $Id: Error.php 287158 2009-08-12 13:58:31Z alan_k $
 * @link       http://pear.php.net/package/DB_DataObject
 */

class DB_DataObject_Error extends PEAR_Error
{

    /**
     * DB_DataObject_Error constructor.
     *
     * @param string $message
     * @param mixed $code DB error code, or string with error message.
     * @param integer $mode what "error mode" to operate in
     * @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER
     * @access public
     *
     * @see PEAR_Error
     */
    public function __construct(
        $message = '',
        $code = DB_ERROR,
        $mode = PEAR_ERROR_RETURN,
        $level = E_USER_NOTICE
    )
    {
        $this->PEAR_Error('DB_DataObject Error: ' . $message, $code, $mode, $level);
    }

    // todo : - support code -> message handling, and translated error messages...
}


code:
<?php
/**
 * Generation tools for DB_DataObject
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.01 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   Database
 * @package    DB_DataObject
 * @author     Alan Knowles <alan@akbkhome.com>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
 * @version    CVS: $Id: Generator.php 336719 2015-05-05 10:37:33Z alan_k $
 * @link       http://pear.php.net/package/DB_DataObject
 */

/*
* Security Notes:
*   This class uses eval to create classes on the fly.
*   The table name and database name are used to check the database before writing the
*   class definitions, we now check for quotes and semi-colon's in both variables
*   so I cant see how it would be possible to generate code even if
*   for some crazy reason you took the classname and table name from User Input.
*
*   If you consider that wrong, or can prove it.. let me know!
*/

/**
 *
 * Config _$ptions
 * [DB_DataObject]
 * ; optional default = DB/DataObject.php
 * extends_location =
 * ; optional default = DB_DataObject
 * extends =
 * ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
 * generator_class_rewrite = ANY|specific_name   // default is DB_DataObject
 *
 */

/**
 * Needed classes
 * We lazy load here, due to problems with the tests not setting up include path correctly.
 * FIXME!
 */
class_exists('DB_DataObject') ? '' : /*require_once 'DB/DataObject.php'*/ require_once '../DataObject.php';
//require_once('Config.php');

/**
 * Generator class
 *
 * @package DB_DataObject
 */
class DB_DataObject_Generator extends DB_DataObject
{
    /* =========================================================== */
    /*  Utility functions - for building db config files           */
    /* =========================================================== */

    /**
     * Array of table names
     *
     * @var array
     * @access private
     */
    public $tables;

    /**
     * associative array table -> array of table row objects
     *
     * @var array
     * @access private
     */
    public $_definitions;

    /**
     * active table being output
     *
     * @var string
     * @access private
     */
    public $table; // active tablename

    /**
     * links (generated)
     *
     * @var array
     * @access private
     */
    public $_fkeys; // active tablename
    /**
     * Output File was config object, now just string
     * Used to generate the Tables
     *
     * @var    string outputbuffer for table definitions
     * @access private
     */
    public $_newConfig;
    /**
     * class being extended (can be overridden by [DB_DataObject] extends=xxxx
     *
     * @var    string
     * @access private
     */
    public $_extends = 'DB_DataObject';
    /**
     * line to use for require('DB/DataObject.php');
     *
     * @var    string
     * @access private
     */
    public $_extendsFile = "DB/DataObject.php";
    /**
     * class being generated
     *
     * @var    string
     * @access private
     */
    public $_className;

    /**
     * The 'starter' = call this to start the process
     *
     * @access  public
     * @return  none
     */
    public function start()
    {
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];

        $databases = array();
        foreach ($options as $k => $v) {
            if (substr($k, 0, 9) == 'database_') {
                $databases[substr($k, 9)] = $v;
            }
        }

        if (isset($options['database'])) {
            if ($db_driver == 'DB') {
                require_once 'DB.php';
                $dsn = DB::parseDSN($options['database']);
            } else {
                require_once 'MDB2.php';
                $dsn = MDB2::parseDSN($options['database']);
            }

            if (!isset($database[$dsn['database']])) {
                $databases[$dsn['database']] = $options['database'];
            }
        }

        foreach ($databases as $databasename => $database) {
            if (!$database) {
                continue;
            }
            $this->debug("CREATING FOR $databasename\n");
            $class = get_class($this);
            $t = new $class;
            $t->_database_dsn = $database;

            $t->_database = $databasename;
            if ($db_driver == 'DB') {
                require_once 'DB.php';
                $dsn = DB::parseDSN($database);
            } else {
                require_once 'MDB2.php';
                $dsn = MDB2::parseDSN($database);
            }

            if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) {
                $t->_database = basename($t->_database);
            }
            $t->_createTableList();
            $t->_createForiegnKeys();

            foreach (get_class_methods($class) as $method) {
                if (substr($method, 0, 8) != 'generate') {
                    continue;
                }
                $this->debug("calling $method");
                $t->$method();
            }
        }
        $this->debug("DONE\n\n");
        return null;
    }

    /**
     * Build a list of tables;
     * and store it in $this->tables and $this->_definitions[tablename];
     *
     * @access  private
     * @return none|object
     */
    public function _createTableList()
    {
        $this->_connect();

        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');

        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];

        $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
        $is_MDB2 = ($db_driver != 'DB') ? true : false;

        if (is_object($__DB) && is_a($__DB, 'PEAR_Error')) {
            return (new PEAR)->raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
        }

        if (!$is_MDB2) {
            // try getting a list of schema tables first. (postgres)
            $__DB->expectError(DB_ERROR_UNSUPPORTED);
            $this->tables = $__DB->getListOf('schema.tables');
            $__DB->popExpect();
        } else {
            /**
             * set portability and some modules to fetch the informations
             */
            $db_options = (new PEAR)->getStaticProperty('MDB2', 'options');
            if (empty($db_options)) {
                $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
            }

            $__DB->loadModule('Manager');
            $__DB->loadModule('Reverse');
        }

        if ((empty($this->tables) || (is_object($this->tables) && is_a($this->tables, 'PEAR_Error')))) {
            //if that fails fall back to clasic tables list.
            if (!$is_MDB2) {
                // try getting a list of schema tables first. (postgres)
                $__DB->expectError(DB_ERROR_UNSUPPORTED);
                $this->tables = $__DB->getListOf('tables');
                $__DB->popExpect();
            } else {
                $this->tables = $__DB->manager->listTables();
                $sequences = $__DB->manager->listSequences();
                foreach ($sequences as $k => $v) {
                    $this->tables[] = $__DB->getSequenceName($v);
                }
            }
        }

        if (is_object($this->tables) && is_a($this->tables, 'PEAR_Error')) {
            return (new PEAR)->raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
        }

        // build views as well if asked to.
        if (!empty($options['build_views'])) {
            if (!$is_MDB2) {
                $views = $__DB->getListOf(is_string($options['build_views']) ?
                    $options['build_views'] : 'views');
            } else {
                $views = $__DB->manager->listViews();
            }
            if (is_object($views) && is_a($views, 'PEAR_Error')) {
                return (new PEAR)->raiseError(
                    'Error getting Views (check the PEAR bug database for the fix to DB), ' .
                    $views->toString(),
                    null,
                    PEAR_ERROR_DIE
                );
            }
            $this->tables = array_merge($this->tables, $views);
        }

        // declare a temporary table to be filled with matching tables names
        $tmp_table = array();

        foreach ($this->tables as $table) {
            if (isset($options['generator_include_regex']) &&
                !preg_match($options['generator_include_regex'], $table)) {
                $this->debug("SKIPPING (generator_include_regex) : $table");
                continue;
            }

            if (isset($options['generator_exclude_regex']) &&
                preg_match($options['generator_exclude_regex'], $table)) {
                continue;
            }

            $strip = empty($options['generator_strip_schema']) ? false : $options['generator_strip_schema'];
            $strip = is_numeric($strip) ? (bool)$strip : $strip;
            $strip = (is_string($strip) && strtolower($strip) == 'true') ? true : $strip;

            // postgres strip the schema bit from the
            if (!empty($strip)) {
                if (!is_string($strip) || preg_match($strip, $table)) {
                    $bits = explode('.', $table, 2);
                    $table = $bits[0];
                    if (count($bits) > 1) {
                        $table = $bits[1];
                    }
                }
            }
            $this->debug("EXTRACTING : $table");

            $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
                $__DB->quoteIdentifier($table) : $table;

            if (!$is_MDB2) {
                $defs = $__DB->tableInfo($quotedTable);
            } else {
                $defs = $__DB->reverse->tableInfo($quotedTable);
                // rename the length value, so it matches db's return.
            }

            if (is_object($defs) && is_a($defs, 'PEAR_Error')) {
                // running in debug mode should pick this up as a big warning..
                $this->debug("Error reading tableInfo: $table");
                $this->raiseError('Error reading tableInfo, ' . $defs->toString());
                continue;
            }
            // cast all definitions to objects - as we deal with that better.

            foreach ($defs as $def) {
                if (!is_array($def)) {
                    continue;
                }
                // rename the length value, so it matches db's return.
                if (isset($def['length']) && !isset($def['len'])) {
                    $def['len'] = $def['length'];
                }
                $this->_definitions[$table][] = (object)$def;
            }
            // we find a matching table, just  store it into a temporary array
            $tmp_table[] = $table;
        }
        // the temporary table array is now the right one (tables names matching
        // with regex expressions have been removed)
        $this->tables = $tmp_table;

        //print_r($this->_definitions);
        return null;
    }

    /**
     * Auto generation of table data.
     *
     * it will output to db_oo_{database} the table definitions
     *
     * @access  private
     * @return none|void
     */
    public function generateDefinitions()
    {
        $this->debug("Generating Definitions file:        ");
        if (!$this->tables) {
            $this->debug("-- NO TABLES -- \n");
            return null;
        }

        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');

        //$this->_newConfig = new Config('IniFile');
        $this->_newConfig = '';
        foreach ($this->tables as $this->table) {
            $this->_generateDefinitionsTable();
        }
        $this->_connect();
        // dont generate a schema if location is not set
        // it's created on the fly!
        if (empty($options['schema_location']) && empty($options["ini_{$this->_database}"])) {
            return null;
        }
        if (!empty($options['generator_no_ini'])) { // built in ini files..
            return null;
        }
        $base = @$options['schema_location'];
        if (isset($options["ini_{$this->_database}"])) {
            $file = $options["ini_{$this->_database}"];
        } else {
            $file = "{$base}/{$this->_database}.ini";
        }

        if (!file_exists(dirname($file))) {
            require_once 'System.php';
            (new System)->mkdir(array('-p', '-m', 0755, dirname($file)));
        }
        $this->debug("Writing ini as {$file}\n");
        //touch($file);
        $tmpname = tempnam(session_save_path(), 'DataObject_');
        //print_r($this->_newConfig);
        $fh = fopen($tmpname, 'w');
        if (!$fh) {
            return (new PEAR)->raiseError(
                "Failed to create temporary file: $tmpname\n" .
                "make sure session.save_path is set and is writable\n",
                null,
                PEAR_ERROR_DIE
            );
        }
        fwrite($fh, $this->_newConfig);
        fclose($fh);
        $perms = file_exists($file) ? fileperms($file) : 0755;
        // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..

        if (!@rename($tmpname, $file)) {
            unlink($file);
            rename($tmpname, $file);
        }
        chmod($file, $perms);
        //$ret = $this->_newConfig->writeInput($file,false);

        //if ((new PEAR)->isError($ret) ) {
        //    return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
        // }
        return null;
    }

    /**
     * The table geneation part
     *
     * @access  private
     * @return array|tabledef
     */
    public function _generateDefinitionsTable()
    {
        global $_DB_DATAOBJECT;
        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $defs = $this->_definitions[$this->table];
        $this->_newConfig .= "\n[{$this->table}]\n";
        $keys_out = "\n[{$this->table}__keys]\n";
        $keys_out_primary = '';
        $keys_out_secondary = '';
        if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
            echo "TABLE STRUCTURE FOR {$this->table}\n";
            print_r($defs);
        }
        $DB = $this->getDatabaseConnection();
        $dbtype = $DB->phptype;

        $ret = array(
            'table' => array(),
            'keys' => array(),
        );

        $ret_keys_primary = array();
        $ret_keys_secondary = array();

        foreach ($defs as $t) {
            $n = 0;
            $write_ini = true;

            switch (strtoupper($t->type)) {

                case 'INT':
                case 'INT2':    // postgres
                case 'INT4':    // postgres
                case 'INT8':    // postgres
                case 'SERIAL4': // postgres
                case 'SERIAL8': // postgres
                case 'INTEGER':
                case 'TINYINT':
                case 'SMALLINT':
                case 'MEDIUMINT':
                case 'BIGINT':
                    $type = DB_DATAOBJECT_INT;
                    if ($t->len == 1) {
                        $type += DB_DATAOBJECT_BOOL;
                    }
                    break;

                case 'REAL':
                case 'DOUBLE':
                case 'DOUBLE PRECISION': // double precision (firebird)
                case 'FLOAT':
                case 'FLOAT4': // real (postgres)
                case 'FLOAT8': // double precision (postgres)
                case 'DECIMAL':
                case 'MONEY':  // mssql and maybe others
                case 'NUMERIC':
                case 'NUMBER': // oci8
                    $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
                    break;

                case 'YEAR':
                    $type = DB_DATAOBJECT_INT;
                    break;

                case 'BIT':
                case 'BOOL':
                case 'BOOLEAN':

                    $type = DB_DATAOBJECT_BOOL;
                    // postgres needs to quote '0'
                    if ($dbtype == 'pgsql') {
                        $type += DB_DATAOBJECT_STR;
                    }
                    break;

                case 'STRING':
                case 'CHAR':
                case 'VARCHAR':
                case 'VARCHAR2':
                case 'TINYTEXT':

                case 'ENUM':
                case 'SET':         // not really but oh well

                case 'POINT':       // mysql geometry stuff - not really string - but will do..

                case 'TIMESTAMPTZ': // postgres
                case 'BPCHAR':      // postgres
                case 'INTERVAL':    // postgres (eg. '12 days')

                case 'CIDR':        // postgres IP net spec
                case 'INET':        // postgres IP
                case 'MACADDR':     // postgress network Mac address.

                case 'INTEGER[]':   // postgres type
                case 'BOOLEAN[]':   // postgres type

                    $type = DB_DATAOBJECT_STR;
                    break;

                case 'TEXT':
                case 'MEDIUMTEXT':
                case 'LONGTEXT':
                case '_TEXT':   //postgres (?? view ??)

                    $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
                    break;

                case 'DATE':
                    $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
                    break;

                case 'TIME':
                    $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
                    break;

                case 'DATETIME':

                    $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
                    break;

                case 'TIMESTAMP': // do other databases use this???

                    $type = ($dbtype == 'mysql') ?
                        DB_DATAOBJECT_MYSQLTIMESTAMP :
                        DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
                    break;

                case 'BLOB':       /// these should really be ignored!!!???
                case 'TINYBLOB':
                case 'MEDIUMBLOB':
                case 'LONGBLOB':

                case 'CLOB': // oracle character lob support

                case 'BYTEA':   // postgres blob support..
                    $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
                    break;

                default:
                    echo "*****************************************************************\n" .
                        "**               WARNING UNKNOWN TYPE                          **\n" .
                        "** Found column '{$t->name}', of type  '{$t->type}'            **\n" .
                        "** Please submit a bug, describe what type you expect this     **\n" .
                        "** column  to be                                               **\n" .
                        "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n" .
                        "** Try using MDB2 as the backend - eg set the config option    **\n" .
                        "** db_driver = MDB2                                            **\n" .
                        "*****************************************************************\n";
                    $write_ini = false;
                    break;
            }

            if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
                echo "*****************************************************************\n" .
                    "**               WARNING COLUMN NAME UNUSABLE                  **\n" .
                    "** Found column '{$t->name}', of type  '{$t->type}'            **\n" .
                    "** Since this column name can't be converted to a php variable **\n" .
                    "** name, and the whole idea of mapping would result in a mess  **\n" .
                    "** This column has been ignored...                             **\n" .
                    "*****************************************************************\n";
                continue;
            }

            if (!strlen(trim($t->name))) {
                continue; // is this a bug?
            }

            if (preg_match('/not[ _]null/i', $t->flags)) {
                $type += DB_DATAOBJECT_NOTNULL;
            }

            if (in_array($t->name, array('null', 'yes', 'no', 'true', 'false'))) {
                echo "*****************************************************************\n" .
                    "**                             WARNING                         **\n" .
                    "** Found column '{$t->name}', which is invalid in an .ini file **\n" .
                    "** This line will not be writen to the file - you will have    **\n" .
                    "** define the keys()/method manually.                          **\n" .
                    "*****************************************************************\n";
                $write_ini = false;
            } else {
                $this->_newConfig .= "{$t->name} = $type\n";
            }

            $ret['table'][$t->name] = $type;
            // i've no idea if this will work well on other databases?
            // only use primary key or nextval(), cause the setFrom blocks you setting all key items...
            // if no keys exist fall back to using unique
            //echo "\n{$t->name} => {$t->flags}\n";
            $secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';

            $m = array();
            if (preg_match('/(auto_increment|nextval\(([^)]*))/i', rawurldecode($t->flags), $m)
                || (isset($t->autoincrement) && ($t->autoincrement === true))) {
                $sn = 'N';
                if ($DB->phptype == 'pgsql' && !empty($m[2])) {
                    $sn = preg_replace('/[("]+/', '', $m[2]);
                    //echo urldecode($t->flags) . "\n" ;
                }
                // native sequences = 2
                if ($write_ini) {
                    $keys_out_primary .= "{$t->name} = $sn\n";
                }
                $ret_keys_primary[$t->name] = $sn;
            } elseif ($secondary_key_match && preg_match('/(' . $secondary_key_match . ')/i', $t->flags)) {
                // keys.. = 1
                $key_type = 'K';
                if (!preg_match("/(primary)/i", $t->flags)) {
                    $key_type = 'U';
                }

                if ($write_ini) {
                    $keys_out_secondary .= "{$t->name} = {$key_type}\n";
                }
                $ret_keys_secondary[$t->name] = $key_type;
            }
        }

        $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
        $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;

        if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
            print_r(array("dump for {$this->table}", $ret));
        }

        return $ret;
    }

    /**
     * create the data for Foreign Keys (for links.ini)
     * Currenly only works with mysql / mysqli / posgtreas
     * to use, you must set option: generate_links=true
     *
     * @author Pascal Sch�ni
     */

    public function _createForiegnKeys()
    {
        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
        if (empty($options['generate_links'])) {
            return false;
        }
        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
        if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
            echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
            return null; // cant handle non-mysql introspection for defaults.
        }
        $this->debug("generateForeignKeys: Start");
        $DB = $this->getDatabaseConnection();

        $fk = array();

        switch ($DB->phptype) {

            case 'pgsql':
                foreach ($this->tables as $this->table) {
                    $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($this->table) : $this->table;
                    $res =& $DB->query("SELECT
                                pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
                            FROM pg_catalog.pg_constraint r,
                                 pg_catalog.pg_class c
                            WHERE c.oid=r.conrelid
                                  AND r.contype = 'f'
                                  AND c.relname = '" . $quotedTable . "'");
                    if ((new PEAR)->isError($res)) {
                        die($res->getMessage());
                    }

                    if ($db_driver == 'DB') {
                        $fetchmode = DB_FETCHMODE_ASSOC;
                    } else {
                        $fetchmode = MDB2_FETCHMODE_ASSOC;
                    }

                    while ($row = $res->fetchRow($fetchmode)) {
                        $treffer = array();
                        // this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
                        preg_match(
                            "/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
                            $row['condef'],
                            $treffer
                        );
                        if (!count($treffer)) {
                            continue;
                        }
                        $fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
                    }
                }
                break;

            case 'mysql':
            case 'mysqli':
            default:

                foreach ($this->tables as $this->table) {
                    $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($this->table) : $this->table;

                    $res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable);

                    if ((new PEAR)->isError($res)) {
                        die($res->getMessage());
                    }

                    if ($db_driver == 'DB') {
                        $text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
                    } else {
                        $text = $res->fetchRow(MDB2_FETCHMODE_DEFAULT, 0);
                    }
                    $treffer = array();
                    // Extract FOREIGN KEYS
                    preg_match_all(
                        "/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",
                        $text[1],
                        $treffer,
                        PREG_SET_ORDER
                    );

                    if (!count($treffer)) {
                        continue;
                    }
                    foreach ($treffer as $i => $tref) {
                        $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
                    }
                }

        }

        $this->_fkeys = $fk;
    return null;
    }

    /**
     * generate Foreign Keys (for links.ini)
     * Currenly only works with mysql / mysqli
     * to use, you must set option: generate_links=true
     *
     * @author Pascal Sch�ni
     */
    public function generateForeignKeys()
    {
        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
        if (empty($options['generate_links'])) {
            return false;
        }
        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
        if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
            echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
            return null; // cant handle non-mysql introspection for defaults.
        }
        $this->debug("generateForeignKeys: Start");

        $fk = $this->_fkeys;
        $links_ini = "";

        foreach ($fk as $table => $details) {
            $links_ini .= "[$table]\n";
            foreach ($details as $col => $ref) {
                $links_ini .= "$col = $ref\n";
            }
            $links_ini .= "\n";
        }

        // dont generate a schema if location is not set
        // it's created on the fly!
        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');

        if (!empty($options['schema_location'])) {
            $file = "{$options['schema_location']}/{$this->_database}.links.ini";
        } elseif (isset($options["ini_{$this->_database}"])) {
            $file = preg_replace('/\.ini/', '.links.ini', $options["ini_{$this->_database}"]);
        } else {
            $this->debug("generateForeignKeys: SKIP - schema_location or ini_{database} was not set");
            return null;
        }

        if (!file_exists(dirname($file))) {
            mkdir(dirname($file), 0755, true);
        }

        $this->debug("Writing ini as {$file}\n");

        //touch($file); // not sure why this is needed?
        $tmpname = tempnam(session_save_path(), 'DataObject_');

        $fh = fopen($tmpname, 'w');
        if (!$fh) {
            return (new PEAR)->raiseError(
                "Failed to create temporary file: $tmpname\n" .
                "make sure session.save_path is set and is writable\n",
                null,
                PEAR_ERROR_DIE
            );
        }
        fwrite($fh, $links_ini);
        fclose($fh);
        $perms = file_exists($file) ? fileperms($file) : 0755;
        // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
        if (!@rename($tmpname, $file)) {
            unlink($file);
            rename($tmpname, $file);
        }
        chmod($file, $perms);
        return null;
    }

    /*
     * building the class files
     * for each of the tables output a file!
     */

    public function generateClasses()
    {
        //echo "Generating Class files:        \n";
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');

        $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
        $this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];

        foreach ($this->tables as $this->table) {
            $this->table = trim($this->table);
            $this->classname = $this->getClassNameFromTableName($this->table);
            $i = '';
            $outfilename = $this->getFileNameFromTableName($this->table);

            $oldcontents = '';
            if (file_exists($outfilename)) {
                // file_get_contents???
                $oldcontents = implode('', file($outfilename));
            }

            $out = $this->_generateClassTable($oldcontents);
            $this->debug("writing $this->classname\n");
            $tmpname = tempnam(session_save_path(), 'DataObject_');

            $fh = fopen($tmpname, "w");
            if (!$fh) {
                return (new PEAR)->raiseError(
                    "Failed to create temporary file: $tmpname\n" .
                    "make sure session.save_path is set and is writable\n",
                    null,
                    PEAR_ERROR_DIE
                );
            }
            fputs($fh, $out);
            fclose($fh);
            $perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;

            // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
            if (!@rename($tmpname, $outfilename)) {
                unlink($outfilename);
                rename($tmpname, $outfilename);
            }

            chmod($outfilename, $perms);
        }
        //echo $out;
        return null;
    }

    /**
     * Convert a table name into a class name -> override this if you want a different mapping
     *
     * @access  public
     * @param $table
     * @return  string class name;
     */

    public function getClassNameFromTableName($table)
    {
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
        return $class_prefix . preg_replace('/[^A-Z0-9]/i', '_', ucfirst(trim($this->table)));
    }

    /**
     * Convert a table name into a file name -> override this if you want a different mapping
     *
     * @access  public
     * @param $table
     * @return  string file name;
     */

    public function getFileNameFromTableName($table)
    {
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $base = $options['class_location'];
        if (strpos($base, '%s') !== false) {
            $base = dirname($base);
        }
        if (!file_exists($base)) {
            require_once 'System.php';
            (new System)->mkdir(array('-p', $base));
        }
        if (strpos($options['class_location'], '%s') !== false) {
            $outfilename = sprintf(
                $options['class_location'],
                preg_replace('/[^A-Z0-9]/i', '_', ucfirst($this->table))
            );
        } else {
            $outfilename = "{$base}/" . preg_replace('/[^A-Z0-9]/i', '_', ucfirst($this->table)) . ".php";
        }
        return $outfilename;
    }

    /**
     * The table class geneation part - single file.
     *
     * @access  private
     * @param string $input
     * @return none|string
     */
    public function _generateClassTable($input = '')
    {
        // title = expand me!
        $foot = "";
        $head = "<?php\n/**\n * Table Definition for {$this->table}\n";
        $head .= $this->derivedHookPageLevelDocBlock();
        $head .= " */\n";
        $head .= $this->derivedHookExtendsDocBlock();

        // requires - if you set extends_location = (blank) then no require line will be set
        // this can be used if you have an autoloader

        if (!empty($this->_extendsFile)) {
            $head .= "require_once '{$this->_extendsFile}';\n\n";
        }
        // add dummy class header in...
        // class
        $head .= $this->derivedHookClassDocBlock();
        $head .= "class {$this->classname} extends {$this->_extends} \n{";

        $body = "\n    ###START_AUTOCODE\n";
        $body .= "    /* the code below is auto generated do not remove the above tag */\n\n";
        // table

        $p = str_repeat(' ', max(2, (18 - strlen($this->table))));

        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');

        $var = (substr(phpversion(), 0, 1) > 4) ? 'public' : 'var';
        $var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var;

        $body .= "    {$var} \$__table = '{$this->table}';  {$p}// table name\n";

        // if we are using the option database_{databasename} = dsn
        // then we should add var $_database = here
        // as database names may not always match..

        if (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {
            DB_DataObject::_loadConfig();
        }

        // Only include the $_database property if the omit_database_var is unset or false

        if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
            $p = str_repeat(' ', max(2, (16 - strlen($this->_database))));
            $body .= "    {$var} \$_database = '{$this->_database}';  {$p}// database name (used with database_{*} config)\n";
        }

        if (!empty($options['generator_novars'])) {
            $var = '//' . $var;
        }

        $defs = $this->_definitions[$this->table];

        // show nice information!
        $connections = array();
        $sets = array();

        foreach ($defs as $t) {
            if (!strlen(trim($t->name))) {
                continue;
            }
            if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
                echo "*****************************************************************\n" .
                    "**               WARNING COLUMN NAME UNUSABLE                  **\n" .
                    "** Found column '{$t->name}', of type  '{$t->type}'            **\n" .
                    "** Since this column name can't be converted to a php variable **\n" .
                    "** name, and the whole idea of mapping would result in a mess  **\n" .
                    "** This column has been ignored...                             **\n" .
                    "*****************************************************************\n";
                continue;
            }

            $pad = str_repeat(' ', max(2, (30 - strlen($t->name))));

            $length = empty($t->len) ? '' : '(' . $t->len . ')';
            $flags = strlen($t->flags) ? (' ' . trim($t->flags)) : '';
            $body .= "    {$var} \${$t->name}; {$pad}// {$t->type}{$length}{$flags}\n";

            // can not do set as PEAR::DB table info doesnt support it.
            //if (substr($t->Type,0,3) == "set")
            //    $sets[$t->Field] = "array".substr($t->Type,3);
            $body .= $this->derivedHookVar($t, strlen($p));
        }

        $body .= $this->derivedHookPostVar($defs);

        // THIS IS TOTALLY BORKED old FC creation
        // IT WILL BE REMOVED!!!!! in DataObjects 1.6
        // grep -r __clone * to find all it's uses
        // and replace them with $x = clone($y);
        // due to the change in the PHP5 clone design.
        $static = 'static';
        if (substr(phpversion(), 0, 1) < 5) {
            $body .= "\n";
            $body .= "    /* ZE2 compatibility trick*/\n";
            $body .= "    function __clone() { return \$this;}\n";
        }

        // depricated - in here for BC...
        if (!empty($options['static_get'])) {

            // simple creation tools ! (static stuff!)
            $body .= "\n";
            $body .= "    /* Static get */\n";
            $body .= "    $static  function staticGet(\$k,\$v=NULL) { " .
                "return DB_DataObject::staticGet('{$this->classname}',\$k,\$v = null); }\n";
        }
        // generate getter and setter methods
        $body .= $this->_generateGetters($input);
        $body .= $this->_generateSetters($input);
        $body .= $this->_generateLinkMethods($input);
        /*
        theoretically there is scope here to introduce 'list' methods
        based up 'xxxx_up' column!!! for heiracitcal trees..
        */

        // set methods
        //foreach ($sets as $k=>$v) {
        //    $kk = strtoupper($k);
        //    $body .="    function getSets{$k}() { return {$v}; }\n";
        //}

        if (!empty($options['generator_no_ini'])) {
            $def = $this->_generateDefinitionsTable();  // simplify this!?
            $body .= $this->_generateTableFunction($def['table']);
            $body .= $this->_generateKeysFunction($def['keys']);
            $body .= $this->_generateSequenceKeyFunction($def);
            $body .= $this->_generateDefaultsFunction($this->table, $def['table']);
        } elseif (!empty($options['generator_add_defaults'])) {
            // I dont really like doing it this way (adding another option)
            // but it helps on older projects.
            $def = $this->_generateDefinitionsTable();  // simplify this!?
            $body .= $this->_generateDefaultsFunction($this->table, $def['table']);
        }
        $body .= $this->derivedHookFunctions($input);

        $body .= "\n    /* the code above is auto generated do not remove the tag below */";
        $body .= "\n    ###END_AUTOCODE\n";

        // stubs..

        if (!empty($options['generator_add_validate_stubs'])) {
            foreach ($defs as $t) {
                if (!strlen(trim($t->name))) {
                    continue;
                }
                $validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name);
                // dont re-add it..
                if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
                    continue;
                }
                $body .= "\n    function {$validate_fname}()\n    {\n        return false;\n    }\n";
            }
        }

        $foot .= "}\n";
        $full = $head . $body . $foot;

        if (!$input) {
            return $full;
        }
        if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s', $input)) {
            return $full;
        }
        if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', $input)) {
            return $full;
        }

        /* this will only replace extends DB_DataObject by default,
            unless use set generator_class_rewrite to ANY or a name*/

        $class_rewrite = 'DB_DataObject';
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
            $class_rewrite = 'DB_DataObject';
        }
        if ($class_rewrite == 'ANY') {
            $class_rewrite = '[a-z_]+';
        }

        $input = preg_replace(
            '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' . $class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
            "\nclass {$this->classname} extends {$this->_extends} \n{\n",
            $input
        );

        $ret = preg_replace(
            '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
            $body,
            $input
        );

        if (!strlen($ret)) {
            return (new PEAR)->raiseError(
                "PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n" .
                "pcre.backtrack_limit=1000000\n" .
                "pcre.recursion_limit=1000000\n",
                null,
                PEAR_ERROR_DIE
            );
        }

        return $ret;
    }

    /**
     * hook to add extra page-level (in terms of phpDocumentor) DocBlock
     *
     * called once for each class, use it add extra page-level docs
     * @access public
     * @return string added to class eg. functions.
     */
    public function derivedHookPageLevelDocBlock()
    {
        return '';
    }

    /**
     * hook to add extra doc block (in terms of phpDocumentor) to extend string
     *
     * called once for each class, use it add extra comments to extends
     * string (require_once...)
     * @access public
     * @return string added to class eg. functions.
     */
    public function derivedHookExtendsDocBlock()
    {
        return '';
    }

    /**
     * hook to add extra class level DocBlock (in terms of phpDocumentor)
     *
     * called once for each class, use it add extra comments to class
     * string (require_once...)
     * @access public
     * @return string added to class eg. functions.
     */
    public function derivedHookClassDocBlock()
    {
        return '';
    }

    /**
     * hook for var lines
     * called each time a var line is generated, override to add extra var
     * lines
     *
     * @param object t containing type,len,flags etc. from tableInfo call
     * @param int padding number of spaces
     * @access   public
     * @return  string added to class eg. functions.
     */
    public function derivedHookVar(&$t, $padding)
    {
        // This is so derived generator classes can generate variabels
        // It MUST NOT be changed here!!!
        return "";
    }

    /**
     * hook for after var lines (
     * called at the end of the output of var line have generated, override to add extra var
     * lines
     *
     * @param array cols containing array of objects with type,len,flags etc. from tableInfo call
     * @access   public
     * @return  string added to class eg. functions.
     */
    public function derivedHookPostVar($t)
    {
        // This is so derived generator classes can generate variabels
        // It MUST NOT be changed here!!!
        return "";
    }

    /**
     * Generate getter methods for class definition
     *
     * @param string $input Existing class contents
     * @return   string
     * @access   public
     */
    public function _generateGetters($input)
    {
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $getters = '';

        // only generate if option is set to true
        if (empty($options['generate_getters'])) {
            return '';
        }

        // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
        $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);

        $getters .= "\n\n";
        $defs = $this->_definitions[$this->table];

        // loop through properties and create getter methods
        foreach ($defs as $t) {

            // build mehtod name
            $methodName = 'get' . $this->getMethodNameFromColumnName($t->name);

            if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
                continue;
            }

            $getters .= "   /**\n";
            $getters .= "    * Getter for \${$t->name}\n";
            $getters .= "    *\n";
            $getters .= (stristr($t->flags, 'multiple_key')) ? "    * @return   object\n"
                : "    * @return   {$t->type}\n";
            $getters .= "    * @access   public\n";
            $getters .= "    */\n";
            $getters .= (substr(phpversion(), 0, 1) > 4) ? '    public '
                : '    ';
            $getters .= "function $methodName() {\n";
            $getters .= "        return \$this->{$t->name};\n";
            $getters .= "    }\n\n";
        }

        return $getters;
    }

    /**
     * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
     *
     * @access  public
     * @param $col
     * @return  string method name;
     */

    public function getMethodNameFromColumnName($col)
    {
        return ucfirst($col);
    }

    /**
     * Generate setter methods for class definition
     *
     * @param string  Existing class contents
     * @return   string
     * @access   public
     */
    public function _generateSetters($input)
    {
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $setters = '';

        // only generate if option is set to true
        if (empty($options['generate_setters'])) {
            return '';
        }

        // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
        $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);

        $setters .= "\n";
        $defs = $this->_definitions[$this->table];

        // loop through properties and create setter methods
        foreach ($defs as $t) {

            // build mehtod name
            $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);

            if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
                continue;
            }

            $setters .= "   /**\n";
            $setters .= "    * Setter for \${$t->name}\n";
            $setters .= "    *\n";
            $setters .= "    * @param    mixed   input value\n";
            $setters .= "    * @access   public\n";
            $setters .= "    */\n";
            $setters .= (substr(phpversion(), 0, 1) > 4) ? '    public '
                : '    ';
            $setters .= "function $methodName(\$value) {\n";
            $setters .= "        \$this->{$t->name} = \$value;\n";
            $setters .= "    }\n\n";
        }

        return $setters;
    }

    /**
     * Generate link setter/getter methods for class definition
     *
     * @param string  Existing class contents
     * @return   string
     * @access   public
     */
    public function _generateLinkMethods($input)
    {
        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $setters = '';

        // only generate if option is set to true

        // generate_link_methods true::

        if (empty($options['generate_link_methods'])) {
            //echo "skip lm? - not set";
            return '';
        }

        if (empty($this->_fkeys)) {
            // echo "skip lm? - fkyes empty";
            return '';
        }
        if (empty($this->_fkeys[$this->table])) {
            //echo "skip lm? - no fkeys for {$this->table}";
            return '';
        }

        // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
        $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);

        $setters .= "\n";
        $defs = $this->_fkeys[$this->table];

        // $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];

        // loop through properties and create setter methods
        foreach ($defs as $k => $info) {

            // build mehtod name
            $methodName = is_callable($options['generate_link_methods']) ?
                $options['generate_link_methods']($k) : $k;

            if (!strlen(trim($k)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
                continue;
            }

            $setters .= "   /**\n";
            $setters .= "    * Getter / Setter for \${$k}\n";
            $setters .= "    *\n";
            $setters .= "    * @param    mixed   (optional) value to assign\n";
            $setters .= "    * @access   public\n";

            $setters .= "    */\n";
            $setters .= (substr(phpversion(), 0, 1) > 4) ? '    public '
                : '    ';
            $setters .= "function $methodName() {\n";
            $setters .= "        return \$this->link('$k', func_get_args());\n";
            $setters .= "    }\n\n";
        }

        return $setters;
    }

    /**
     * Generate table Function - used when generator_no_ini is set.
     *
     * @param array  table array.
     * @return   string
     * @access   public
     */
    public function _generateTableFunction($def)
    {
        $defines = explode(',', 'INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP');

        $ret = "\n" .
            "    function table()\n" .
            "    {\n" .
            "         return array(\n";

        foreach ($def as $k => $v) {
            $str = '0';
            foreach ($defines as $dn) {
                if ($v & constant('DB_DATAOBJECT_' . $dn)) {
                    $str .= ' + DB_DATAOBJECT_' . $dn;
                }
            }
            if (strlen($str) > 1) {
                $str = substr($str, 3); // strip the 0 +
            }
            // hopefully addslashes is good enough here!!!
            $ret .= '             \'' . addslashes($k) . '\' => ' . $str . ",\n";
        }
        return $ret . "         );\n" .
            "    }\n";
    }

    /**
     * Generate keys Function - used generator_no_ini is set.
     *
     * @param array  keys array.
     * @return   string
     * @access   public
     */
    public function _generateKeysFunction($def)
    {
        $ret = "\n" .
            "    function keys()\n" .
            "    {\n" .
            "         return array(";

        foreach ($def as $k => $type) {
            // hopefully addslashes is good enough here!!!
            $ret .= '\'' . addslashes($k) . '\', ';
        }
        $ret = preg_replace('#, $#', '', $ret);
        return $ret . ");\n" .
            "    }\n";
    }

    /**
     * Generate sequenceKey Function - used generator_no_ini is set.
     *
     * @param array  table and key definition.
     * @return   string
     * @access   public
     */
    public function _generateSequenceKeyFunction($def)
    {

        //print_r($def);
        // DB_DataObject::debugLevel(5);
        global $_DB_DATAOBJECT;
        // print_r($def);

        $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
        $realkeys = $def['keys'];
        $keys = array_keys($realkeys);
        $usekey = isset($keys[0]) ? $keys[0] : false;
        $table = $def['table'];

        $seqname = false;

        $ar = array(false, false, false);
        if ($usekey !== false) {
            if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_' . $this->__table])) {
                $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_' . $this->__table];
                if (strpos($usekey, ':') !== false) {
                    list($usekey, $seqname) = explode(':', $usekey);
                }
            }

            if (in_array($dbtype, array('mysql', 'mysqli', 'mssql', 'ifx')) &&
                ($table[$usekey] & DB_DATAOBJECT_INT) &&
                isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
            ) {
                // use native sequence keys.
                $ar = array($usekey, true, $seqname);
            } else {
                // use generated sequence keys..
                if ($table[$usekey] & DB_DATAOBJECT_INT) {
                    $ar = array($usekey, false, $seqname);
                }
            }
        }

        $ret = "\n" .
            "    function sequenceKey() // keyname, use native, native name\n" .
            "    {\n" .
            "         return array(";
        foreach ($ar as $v) {
            switch (gettype($v)) {
                case 'boolean':
                    $ret .= ($v ? 'true' : 'false') . ', ';
                    break;

                case 'string':
                    $ret .= "'" . $v . "', ";
                    break;

                default:    // eak
                    $ret .= "null, ";

            }
        }
        $ret = preg_replace('#, $#', '', $ret);
        return $ret . ");\n" .
            "    }\n";
    }

    /**
     * Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
     * Only supports mysql and mysqli ... welcome ideas for more..
     *
     *
     * @param $table
     * @param $defs
     * @return   string
     * @access   public
     */
    public function _generateDefaultsFunction($table, $defs)
    {
        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
        if (!in_array($__DB->phptype, array('mysql', 'mysqli'))) {
            return null; // cant handle non-mysql introspection for defaults.
        }
        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
        $method = $db_driver == 'DB' ? 'getAll' : 'queryAll';
        if ($db_driver == 'DB') {
            $res = $__DB->$method('DESCRIBE ' . $table, DB_FETCHMODE_ASSOC);
        } else {
            $res = $__DB->$method('DESCRIBE ' . $table, MDB2_FETCHMODE_ASSOC);
        }
        $defaults = array();
        foreach ($res as $ar) {
            // this is initially very dumb... -> and it may mess up..
            $type = $defs[$ar['Field']];

            switch (true) {

                case (is_null($ar['Default'])):
                    $defaults[$ar['Field']] = 'null';
                    break;

                case ($type & DB_DATAOBJECT_DATE):
                case ($type & DB_DATAOBJECT_TIME):
                case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet..
                    break;

                case ($type & DB_DATAOBJECT_BOOL):
                    $defaults[$ar['Field']] = (int)(boolean)$ar['Default'];
                    break;

                case ($type & DB_DATAOBJECT_STR):
                    $defaults[$ar['Field']] = "'" . addslashes($ar['Default']) . "'";
                    break;

                default:    // hopefully eveything else...  - numbers etc.
                    if (!strlen($ar['Default'])) {
                        continue;
                    }
                    if (is_numeric($ar['Default'])) {
                        $defaults[$ar['Field']] = $ar['Default'];
                    }
                    break;

            }
            //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
        }
        if (empty($defaults)) {
            return null;
        }

        $ret = "\n" .
            "    function defaults() // column default values \n" .
            "    {\n" .
            "         return array(\n";
        foreach ($defaults as $k => $v) {
            $ret .= '             \'' . addslashes($k) . '\' => ' . $v . ",\n";
        }
        return $ret . "         );\n" .
            "    }\n";
    }

    /**
     * hook to add extra methods to all classes
     *
     * called once for each class, use with $this->table and
     * $this->_definitions[$this->table], to get data out of the current table,
     * use it to add extra methods to the default classes.
     *
     * @access   public
     * @param string $input
     * @return  string added to class eg. functions.
     */
    public function derivedHookFunctions($input = "")
    {
        // This is so derived generator classes can generate functions
        // It MUST NOT be changed here!!!
        return "";
    }

    /**
     *
     * /**
     * getProxyFull - create a class definition on the fly and instantate it..
     *
     * similar to generated files - but also evals the class definitoin code.
     *
     *
     * @param string database name
     * @param string  table   name of table to create proxy for.
     *
     *
     * @return   object    Instance of class. or PEAR Error
     * @access   public
     */
    public function getProxyFull($database, $table)
    {
        if ($err = $this->fillTableSchema($database, $table)) {
            return $err;
        }

        $options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];

        $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
        $this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];

        $classname = $this->classname = $this->getClassNameFromTableName($this->table);

        $out = $this->_generateClassTable();
        //echo $out;
        eval('?>' . $out);
        return new $classname;
    }

    /**
     * fillTableSchema - set the database schema on the fly
     *
     *
     *
     * @param string database name
     * @param string  table   name of table to create schema info for
     *
     * @return none|object|PEAR
     * @access   public
     */
    public function fillTableSchema($database, $table)
    {
        global $_DB_DATAOBJECT;
        // a little bit of sanity testing.
        if ((false !== strpos($database, "'")) || (false !== strpos($database, ";"))) {
            return (new PEAR)->raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE);
        }

        $this->_database = $database;

        $this->_connect();
        $table = trim($table);

        // a little bit of sanity testing.
        if ((false !== strpos($table, "'")) || (false !== strpos($table, ";"))) {
            return (new PEAR)->raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
        }
        $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];

        $options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
        $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
        $is_MDB2 = ($db_driver != 'DB') ? true : false;

        if (!$is_MDB2) {
            // try getting a list of schema tables first. (postgres)
            $__DB->expectError(DB_ERROR_UNSUPPORTED);
            $this->tables = $__DB->getListOf('schema.tables');
            $__DB->popExpect();
        } else {
            /**
             * set portability and some modules to fetch the informations
             */
            $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
            $__DB->loadModule('Manager');
            $__DB->loadModule('Reverse');
        }
        $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
            $__DB->quoteIdentifier($table) : $table;

        if (!$is_MDB2) {
            $defs = $__DB->tableInfo($quotedTable);
        } else {
            $defs = $__DB->reverse->tableInfo($quotedTable);
            if ((new PEAR)->isError($defs)) {
                return $defs;
            }
            foreach ($defs as $k => $v) {
                if (!isset($defs[$k]['length'])) {
                    continue;
                }
                $defs[$k]['len'] = $defs[$k]['length'];
            }
        }

        if ((new PEAR)->isError($defs)) {
            return $defs;
        }

        if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
            $this->debug("getting def for $database/$table", 'fillTable');
            $this->debug(print_r($defs, true), 'defs');
        }
        // cast all definitions to objects - as we deal with that better.

        foreach ($defs as $def) {
            if (is_array($def)) {
                $this->_definitions[$table][] = (object)$def;
            }
        }

        $this->table = trim($table);
        $ret = $this->_generateDefinitionsTable();

        $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
        $_DB_DATAOBJECT['INI'][$database][$table . '__keys'] = $ret['keys'];
        return false;
    }
}


code:
<?php
/**
 * Link tool for DB_DataObject
 *
 * PHP versions 5
 *
 * LICENSE: This source file is subject to version 3.01 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   Database
 * @package    DB_DataObject
 * @author     Alan Knowles <alan@akbkhome.com>
 * @copyright  1997-2006 The PHP Group
 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
 * @version    : FIXME
 * @link       http://pear.php.net/package/DB_DataObject
 */

/**
 *
 * Example of how this could be used..
 *
 * The lind method are now in here.
 *
 * Currenly only supports existing methods, and new 'link()' method
 *
 */

/**
 * Links class
 *
 * @package DB_DataObject
 */
class DB_DataObject_Links
{
    /**
     * @property {DB_DataObject}      do   DataObject to apply this to.
     */
    public $do = false;

    /**
     * @property {Array|String} load    What to load, 'all' or an array of properties. (default all)
     */
    public $load = 'all';
    /**
     * @property {String|Boolean}       scanf   use part of column name as resulting
     *                                          property name. (default false)
     */
    public $scanf = false;
    /**
     * @property {String|Boolean}       printf  use column name as sprintf for resulting property name..
     *                                     (default %s_link if apply is true, otherwise it is %s)
     */
    public $printf = false;
    /**
     * @property {Boolean}      cached  cache the result, so future queries will use cache rather
     *                                  than running the expensive sql query.
     */
    public $cached = false;
    /**
     * @property {Boolean}      apply   apply the result to this object, (default true)
     */
    public $apply = true;

    //------------------------- RETURN ------------------------------------
    /**
     * @property {Array}      links    key value associative array of links.
     */
    public $links;

    /**
     * Constructor
     *   -- good ole style..
     * @param {DB_DataObject}           do  DataObject to apply to.
     * @param array $cfg
     */

    public function __construct($do, $cfg = array())
    {
        // check if do is set!!!?
        $this->do = $do;

        foreach ($cfg as $k => $v) {
            $this->$k = $v;
        }
    }

    /**
     *  a generic geter/setter provider..
     *
     *  provides a generic getter setter for the referenced object
     *  eg.
     *  $link->link('company_id') returns getLink for the object
     *  if nothing is linked (it will return an empty dataObject)
     *  $link->link('company_id', array(1)) - just sets the
     *
     *  also array as the field speck supports
     *      $link->link(array('company_id', 'company:id'))
     *
     *
     * @param string|array $field the field to fetch or link spec.
     * @param array $args
     * @return mixed true of false on set, the object on getter.
     * @params array          $args    the arguments sent to the getter setter
     */
    public function link($field, $args = array())
    {
        $info = $this->linkInfo($field);

        if (!$info) {
            $this->do->raiseError(
                "getLink:Could not find link for row $field",
                DB_DATAOBJECT_ERROR_INVALIDCONFIG
            );
            return false;
        }
        $field = $info[2];

        if (empty($args)) { // either an empty array or really empty....

            if (!isset($this->do->$field)) {
                return $info[0]; // empty dataobject.
            }

            $ret = $this->getLink($field);
            // nothing linked -- return new object..
            return ($ret === 0) ? $info[0] : $ret;
        }
        $assign = is_array($args) ? $args[0] : $args;

        // otherwise it's a set call..
        if (!is_a($assign, 'DB_DataObject')) {
            if (is_numeric($assign) && is_integer($assign * 1)) {
                if ($assign > 0) {
                    if (!$info) {
                        return false;
                    }
                    // check that record exists..
                    if (!$info[0]->get($info[1], $assign)) {
                        return false;
                    }
                }

                $this->do->$field = $assign;
                return true;
            }

            return false;
        }

        // otherwise we are assigning it ...

        $this->do->$field = $assign->{$info[1]};
        return true;
    }

    /**
     * get link information for a field or field specification
     *
     * alll link (and join methods accept the 'link' info ) in various ways
     * string : 'field' = which field to get (uses ???.links.ini to work out what)
     * array(2) : 'field', 'table:remote_col' << just like the links.ini def.
     * array(3) : 'field', $dataobject, 'remote_col'  (handy for joinAdd to do nested joins.)
     *
     * @param string|array $field or link spec to use.
     * @return array|bool (false|array) array of dataobject and linked field or false.
     *
     */

    public function linkInfo($field)
    {
        if (is_array($field)) {
            if (count($field) == 3) {
                // array with 3 args:
                // local_col , dataobject, remote_col
                return array(
                    $field[1],
                    $field[2],
                    $field[0]
                );
            }
            list($table, $link) = explode(':', $field[1]);

            return array(
                $this->do->factory($table),
                $link,
                $field[0]
            );
        }
        // work out the link.. (classic way)

        $links = $this->do->links();

        if (empty($links) || !is_array($links)) {
            return false;
        }

        if (!isset($links[$field])) {
            return false;
        }
        list($table, $link) = explode(':', $links[$field]);

        //??? needed???
        if ($p = strpos($field, ".")) {
            $field = substr($field, 0, $p);
        }

        return array(
            $this->do->factory($table),
            $link,
            $field
        );
    }

    /**
     * return name from related object
     *
     * The relies on  a <dbname>.links.ini file, unless you specify the arguments.
     *
     * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
     *
     *
     * @param string $field |array    either row or row.xxxxx or links spec.
     * @param bool $table (optional) name of table to look up value in
     * @param string $link (optional)  name of column in other table to match
     * @return mixed object on success false on failure or '0' when not linked
     * @author Tim White <tim@cyface.com>
     * @access public
     */
    public function getLink($field, $table = false, $link = '')
    {
        static $cache = array();

        // GUESS THE LINKED TABLE.. (if found - recursevly call self)

        if ($table == false) {
            $info = $this->linkInfo($field);

            if ($info) {
                return $this->getLink($field, $info[0], $link === false ? $info[1] : $link);
            }

            // no links defined.. - use borked BC method...
            // use the old _ method - this shouldnt happen if called via getLinks()
            if (!($p = strpos($field, '_'))) {
                return false;
            }
            $table = substr($field, 0, $p);
            return $this->getLink($field, $table);
        }

        $tn = is_string($table) ? $table : $table->tableName();

        if (!isset($this->do->$field)) {
            $this->do->raiseError("getLink: row not set $field", DB_DATAOBJECT_ERROR_NODATA);
            return false;
        }

        // check to see if we know anything about this table..

        if (empty($this->do->$field) || $this->do->$field < 0) {
            return 0; // no record.
        }

        if ($this->cached && isset($cache[$tn . ':' . $link . ':' . $this->do->$field])) {
            return $cache[$tn . ':' . $link . ':' . $this->do->$field];
        }

        $obj = is_string($table) ? $this->do->factory($tn) : $table;;

        if (!is_a($obj, 'DB_DataObject')) {
            $this->do->raiseError(
                "getLink:Could not find class for row $field, table $tn",
                DB_DATAOBJECT_ERROR_INVALIDCONFIG
            );
            return false;
        }
        // -1 or 0 -- no referenced record..

        $ret = false;
        if ($link) {
            if ($obj->get($link, $this->do->$field)) {
                $ret = $obj;
            }

            // this really only happens when no link config is set (old BC stuff)
        } elseif ($obj->get($this->do->$field)) {
            $ret = $obj;
        }
        if ($this->cached) {
            $cache[$tn . ':' . $link . ':' . $this->do->$field] = $ret;
        }
        return $ret;
    }

    /**
     * load related objects
     *
     * Generally not recommended to use this.
     * The generator should support creating getter_setter methods which are better suited.
     *
     * Relies on  <dbname>.links.ini
     *
     * Sets properties on the calling dataobject  you can change what
     * object vars the links are stored in by  changeing the format parameter
     *
     *
     * @param string format (default _%s) where %s is the table name.
     * @return boolean , true on success
     * @author Tim White <tim@cyface.com>
     * @access public
     */

    public function applyLinks($format = '_%s')
    {

        // get table will load the options.
        if ($this->do->_link_loaded) {
            return true;
        }

        $this->do->_link_loaded = false;
        $cols = $this->do->table();
        $links = $this->do->links();

        $loaded = array();

        if ($links) {
            foreach ($links as $key => $match) {
                list($table, $link) = explode(':', $match);
                $k = sprintf($format, str_replace('.', '_', $key));
                // makes sure that '.' is the end of the key;
                if ($p = strpos($key, '.')) {
                    $key = substr($key, 0, $p);
                }

                $this->do->$k = $this->getLink($key, $table, $link);

                if (is_object($this->do->$k)) {
                    $loaded[] = $k;
                }
            }
            $this->do->_link_loaded = $loaded;
            return true;
        }
        // this is the autonaming stuff..
        // it sends the column name down to getLink and lets that sort it out..
        // if there is a links file then it is not used!
        // IT IS DEPRECATED!!!! - DO NOT USE
        if (!is_null($links)) {
            return false;
        }

        foreach (array_keys($cols) as $key) {
            if (!($p = strpos($key, '_'))) {
                continue;
            }
            // does the table exist.
            $k = sprintf($format, $key);
            $this->do->$k = $this->getLink($key);
            if (is_object($this->do->$k)) {
                $loaded[] = $k;
            }
        }
        $this->do->_link_loaded = $loaded;
        return true;
    }

    /**
     * getLinkArray
     * Fetch an array of related objects. This should be used in conjunction with a
     * <dbname>.links.ini file configuration (see the introduction on linking for details on this).
     *
     * You may also use this with all parameters to specify, the column and related table.
     *
     * @access public
     * @param string $field - either column or column.xxxxx
     * @param string $table (optional) name of table to look up value in
     * @param bool $fkey (optional) fetchall key see DB_DataObject::fetchAll()
     * @param bool $fval (optional) fetchall method DB_DataObject::fetchAll()
     * @param bool $fmethod
     * @return array - array of results (empty array on failure)
     *
     * Example - Getting the related objects
     *
     * $person = new DataObjects_Person;
     * $person->get(12);
     * $children = $person->getLinkArray('children');
     *
     * echo 'There are ', count($children), ' descendant(s):
';
     * foreach ($children as $child) {
     *     echo $child->name, '
';
     * }
     */
    public function getLinkArray($field, $table = null, $fkey = false, $fval = false, $fmethod = false)
    {
        $ret = array();
        if (!$table) {
            $links = $this->do->links();

            if (is_array($links)) {
                if (!isset($links[$field])) {
                    // failed..
                    return $ret;
                }
                list($table, $link) = explode(':', $links[$field]);
                return $this->getLinkArray($field, $table);
            }
            if (!($p = strpos($field, '_'))) {
                return $ret;
            }
            return $this->getLinkArray($field, substr($field, 0, $p));
        }

        $c = $this->do->factory($table);

        if (!is_object($c) || !is_a($c, 'DB_DataObject')) {
            $this->do->raiseError(
                "getLinkArray:Could not find class for row $field, table $table",
                DB_DATAOBJECT_ERROR_INVALIDCONFIG
            );
            return $ret;
        }

        // if the user defined method list exists - use it...
        if (method_exists($c, 'listFind')) {
            $c->listFind($this->id);
            while ($c->fetch()) {
                $ret[] = clone($c);
            }
            return $ret;
        }
        return $c->fetchAll($fkey, $fval, $fmethod);
    }
}


code:
#!/usr/bin/php -q
<?php
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license,      |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Author:  Alan Knowles <alan@akbkhome.com>
// +----------------------------------------------------------------------+
//
// $Id: createTables.php 277015 2009-03-12 05:51:03Z alan_k $
//

// since this version doesnt use overload,
// and I assume anyone using custom generators should add this..

define('DB_DATAOBJECT_NO_OVERLOAD', 1);

//require_once 'DB/DataObject/Generator.php';
require_once 'Generator.php';

if (php_sapi_name() != 'cli') {
    (new PEAR)->raiseError("\nERROR: You must turn use the cli sapi to run this", null, PEAR_ERROR_DIE);
}

if (!ini_get('register_argc_argv')) {
    (new PEAR)->raiseError("\nERROR: You must turn register_argc_argv On in you php.ini file for this to work\neg.\n\nregister_argc_argv = On\n\n", null, PEAR_ERROR_DIE);
    exit;
}

if (!@$_SERVER['argv'][1]) {
    (new PEAR)->raiseError("\nERROR: createTable.php usage:\n\n" . $_SERVER['argv'][0] . " example.ini\n\n", null, PEAR_ERROR_DIE);
    exit;
}

$config = parse_ini_file($_SERVER['argv'][1], true);
foreach ($config as $class => $values) {
    $options = &(new PEAR)->getStaticProperty($class, 'options');
    $options = $values;
}

$options = &(new PEAR)->getStaticProperty('DB_DataObject', 'options');
if (empty($options)) {
    (new PEAR)->raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE);
    exit;
}
set_time_limit(0);

// use debug level from file if set..
DB_DataObject::debugLevel(isset($options['debug']) ? $options['debug'] : 1);

$generator = new DB_DataObject_Generator;
$generator->start();