SitePoint Sponsor

User Tag List

Results 1 to 21 of 21
  1. #1
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Scaffolding / auto code generation

    Hi,

    I was just wondering what is the best way to implement scaffolding or auto code generation. How are some of you guys doing it currently.

    For example I would like to implement something like the following:

    PHP Code:
    // Create a default directory structure etc.
    framework init

    // Create the base/barebones CRUD stuff
    cd models/
    framework create crud DATABASE
    // or
    framework create crud DATABASE TABLENAME

    // Create a base form controller
    cd actions/
    framework create controller NAMEOFCONTROLLER

    etc

    Many thanks,

    --
    lv

  2. #2
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Basically you'll want to create a shellscript in php - it's not that complicated. Try having a look at the PEAR installer and the Console package.
    Some reading at : http://www.zend.com/pear/tutorials/Console-Getopt.php

  3. #3
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks, Kyber I have looked at getopt and seems like the option for working with a cli script. I would have prefered not to worry about install PEAR, anything else you know about?

    However I was also just wondering what would be the best way to physically create the code. The following sort of things feels very clumsy.

    PHP Code:
    function createController($name)
    {
        $controller =  "
    <?php 

    class $name extends Controller {
        
        public function 
    __construct()
        {
            
    parent::__construct();
        }

        public function 
    exexcute()
        {

        }
    }

    ?>";
        // create a file called $name.php in the actions directory
    }
    --
    lv

  4. #4
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lvismer
    However I was also just wondering what would be the best way to physically create the code.
    How about assigning some values and using a simple PHP template:
    Code:
          class <?=$this->class_name?> {
          
          	<?php foreach ($this->vars as $var) { ?>
          	<?=$var['access']?> $<?=$var['name']?> = <?=$var['default']?>;
          	<?php } ?>
          	
          	<?php foreach ($this->methods as $method { ?>
          	<?=$method['access']?> function <?=$method['name']?>()
          	{
          
          	}
          	<?php } ?>
          
          }

    Last edited by michel; Nov 18, 2005 at 06:38.

  5. #5
    SitePoint Guru BerislavLopac's Avatar
    Join Date
    Sep 2004
    Location
    Zagreb, Croatia
    Posts
    830
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wouldn't a better approach be to create generic classes which will config themselves when instantiated? Then you would need only to create some ini files (e.g. using parse_ini_file()), which have an easier and simpler syntax -- the class' constructor would load the file, parse it and fill the required slots with its values. Alternatively, you could have a Factory that does all the hard work.

    PHP5 is especially suited for that kind of approach, thanks to its automagical methods __set, __get, __call, __isset and __unset.

    A side note: I've just realized that there is no built-in function to create ini files, which would convert an associative array (of no more than two levels) into an ini-file. It shouldn't be too hard to write, though, and it would be extremely useful in this kind of setup.

  6. #6
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lvismer
    Thanks, Kyber I have looked at getopt and seems like the option for working with a cli script. I would have prefered not to worry about install PEAR, anything else you know about?
    You don't have to actually rely on PEAR - you can just pillage the codebase. Take what you need and create your own library. That's a strategy I have used many a time.

    Quote Originally Posted by lvismer
    However I was also just wondering what would be the best way to physically create the code. The following sort of things feels very clumsy.
    Some sort of template-system would be helpful. You probably won't want to use php-as-a-template-system in this case, to avoid confusion. There're plenty of template-engines around, just pick one.

    BerislavLopac reaises a question, which you need to atleast consider - Why code-generation. Php is a rather agile language, and esp. with php5, you can do most of the meta-mappings at runtime without difficulty.

  7. #7
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    BerislavLopac reaises a question, which you need to atleast consider - Why code-generation. Php is a rather agile language, and esp. with php5, you can do most of the meta-mappings at runtime without difficulty.
    I find myself doing alot of the initial setup of class definitions over and over when say creating a new ActiveRecord. With code-generation I figure I can save myself from typing the normal stuff when initially creating the class, and basically get into the business logic immediately without the tedious initial work. Am I missing the plot here?

    --
    lv

  8. #8
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lvismer
    With code-generation I figure I can save myself from typing the normal stuff when initially creating the class, and basically get into the business logic immediately without the tedious initial work. Am I missing the plot here?
    Nothing wrong with generating stubs imo. Kyber and Berislav might be referring to generating full-blown classes, like some ORM tools do. Up until now I've failed to see the value in that in PHP.
    You might want to have a look at Rails::Generator or how the guys at Symfony implement their shell scripts and code generation.

  9. #9
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by michel
    Nothing wrong with generating stubs imo. Kyber and Berislav might be referring to generating full-blown classes, like some ORM tools do. Up until now I've failed to see the value in that in PHP.
    You might want to have a look at Rails::Generator or how the guys at Symfony implement their shell scripts and code generation.
    Thats where I got the idea (symfony), I was just being lazy and wanted to save myself from going through their code making sense of it

    --
    lv

  10. #10
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lvismer
    I find myself doing alot of the initial setup of class definitions over and over when say creating a new ActiveRecord. With code-generation I figure I can save myself from typing the normal stuff when initially creating the class, and basically get into the business logic immediately without the tedious initial work. Am I missing the plot here?

    --
    lv
    Not at all. The way I'm approaching it is to have the skeleton code in an external text file, and the generating script simply reads it, does a string_replace with the variable names, and spits it out in the appropriate spot. That way I can modify the skeleton code directly in my ide, without having to touch the generating class.

  11. #11
    SitePoint Guru BerislavLopac's Avatar
    Join Date
    Sep 2004
    Location
    Zagreb, Croatia
    Posts
    830
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by michel
    Up until now I've failed to see the value in that in PHP.
    Here's one: how do you unit-test generated classes?

  12. #12
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    Not at all. The way I'm approaching it is to have the skeleton code in an external text file, and the generating script simply reads it, does a string_replace with the variable names, and spits it out in the appropriate spot. That way I can modify the skeleton code directly in my ide, without having to touch the generating class.
    I like this approach, sounds promising to me, and this way one will easily be able to generate stub code.

    Thanks,

    --
    lv

  13. #13
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by BerislavLopac
    Here's one: how do you unit-test generated classes?
    The test cases covering the generated code would need to be generated and synched every time the generated code is updated. Not something I look forward to implementing Maybe it's my lack of experience, but I tend to refactor/tweak a lot while writing (unit) tests. It's actually the tweaking that leads to better and simpler APIs.

  14. #14
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would suggest that you use the Reflection API if you have access to PHP5 to automatically generate scripts dynamically.

    You could just as well use XSL-T as well I suppose, but that is more complex, and you lose a lot of control during the generation phase. For example, you cannot moniter the progress duing the process.

    You can do just that - and more besides - with the Reflective route.

  15. #15
    As the name suggests... trickie's Avatar
    Join Date
    Jul 2002
    Location
    Melbourne, Australia
    Posts
    678
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Unfortunately i cannot show you code at this time, but this is a API description of an ActiveRecord object created using the Reflection API.

    You see (there are some small examples in the method descriptions) that you no longer have to maintain a deep and large class hierachy.

    Sorry a cannot show you an implementation, but what you are describing you want is very similar to how Ruby on Rails implements the active record pattern. That is what we based our implementation on.

  16. #16
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    I would suggest that you use the Reflection API if you have access to PHP5 to automatically generate scripts dynamically.
    I have some reading to do here. By scripts I presume you are refering to the generated stub code?

    --
    lv

  17. #17
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by trickie
    Unfortunately i cannot show you code at this time, but this is a API description of an ActiveRecord object created using the Reflection API.
    Rather impressive, many thanks

    --
    lv

  18. #18
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by BerislavLopac
    Here's one: how do you unit-test generated classes?
    I did post an example for the "changes" code. Basically I created a bunch of test examples that were then unit tested. Once the mechanics of generation have been done, the source file is declarative. You cannot sensibly test declarative stuff, so you hav eto push testing of that up to the next level. Probably the application level (acceptance testing).

    There is a lot of set up stuff in the test, but you only have to do it once. And if any code ever needed testing, it has to be the code generated stuff. Inspecting code like that is a pig .

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  19. #19
    SitePoint Guru BerislavLopac's Avatar
    Join Date
    Sep 2004
    Location
    Zagreb, Croatia
    Posts
    830
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    OK, perhaps I should rephrase my question: what are advantages of generated code + unit-testing over the generic classes + unit-testing?

  20. #20
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by BerislavLopac
    OK, perhaps I should rephrase my question: what are advantages of generated code + unit-testing over the generic classes + unit-testing?
    I am sure I am going to miss a few, but here is my guess when compared with dynamic solutions:

    1) Easier to debug and pick apart.
    2) Preserves reflection.
    3) Faster at run time.

    The downsides are:

    1) A compile step is added to the coding cycle.
    2) You get lot's of head scratching when you forget the compile step.
    3) More compicated deployment.
    4) You need to learn another syntax, typically XSLT or Ruby.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  21. #21
    SitePoint Zealot
    Join Date
    Aug 2005
    Location
    South Africa
    Posts
    185
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    DL, Trickie,

    I spent some time looking at reflection. To recap, I am looking to create some stub code for things I find myself using often. Am I on track here?

    My example creates the following active record stub code, as a start. One basically registers helpers and additional parameters for methods. The next step would be to complete the code to generate abstract function stubs, obviously registring helpers to achieve this.

    I would like to be able to reuse the stub stuff so I went a slightly longer route.

    PHP Code:
    // Stub code created
    class User extends ActiveRecord
    {
        public function 
    __construct($id$table)
        {
            
    parent::__construct($table)
            if ( 
    $id ) {
                
    $this->set($this->primary[0], $id);
                
    $this->fetch();
            }
        }

        
    // Create abstract methods goes here...
    }

    // Using the following
    try {
        
    $class = new AbstractCodeStub('User', new ReflectionClass('ActiveRecord'));
        
    $class->setHelper('__construct', new ARConstruct());
        
    $class->setExtraParam('__construct''$id');
        
    $class->generate();
    } catch ( 
    Exception $e ) {
        echo 
    $e->getMessage() ."\n";
    }

    // Active.php
    abstract class ActiveRecord //Example
    {
        private 
    $table;

        public function 
    __construct($table)
        {
            
    $this->initTable();
        }
        abstract public function 
    initTable();

    Then the stub code, apologies for the many lines. Obviously the AbstractCodeStub class is the one that kicks off creation of stubs for abstract classes.

    PHP Code:
    define('TAB''    ');
    define('NL'"\n");

    abstract class 
    Stub
    {
        protected 
    $class;
        protected 
    $name;
        protected 
    $methods;
        public static 
    $helpers      = array();
        public static 
    $params       = array();

        public function 
    __construct($nameReflectionClass $class)
        {
            
    $this->name $name;
            
    $this->class $class;
            
    $this->methods $this->class->getMethods();
        }

        public function 
    hasMethod($name)
        {
            foreach ( 
    $this->methods as $method ) {
                if ( 
    $method->name == $name ) {
                    return 
    true;
                }
            }
            return 
    false;
        }

        public function 
    setExtraParam($method$param)
        {
            
    self::$params[$method] = $param;
        }

        public function 
    getParam($method$extra true)
        {
            
    $current = array();
            
    $m $this->class->getMethod($method);
            
    $p $m->getParameters();
            foreach ( 
    $p as $parameter )
                
    $current[] = '$'.$parameter->name;
            if ( 
    $extra )
                
    $param array_merge(self::$params$current);
            else
                
    $param $current;
            return 
    implode(', '$param);
        }

        public function 
    setHelper($method$object)
        {
            
    self::$helpers[$method] = $object;
        }

        public function 
    executeHelper($method)
        {
            
    $helper self::$helpers[$method];
            if ( 
    $helper )
                
    $helper->generate();
        }

        abstract public function 
    generate();
    }

    class 
    AbstractCodeStub extends Stub
    {
        public function 
    __construct($name$class)
        {
            
    parent::__construct($name$class);
        }

        public function 
    generate()
        {
            if ( 
    $this->class->isAbstract() ) {
                
    $stub = new ClassStub(
                            array (
                                new 
    ClassConstruct($this->name$this->class),
                                new 
    ClassAbstract($this->name$this->class)
                             ),
                        
    $this->name$this->class);
                
    $stub->generate();
            } else {
                throw new 
    Exception($this->class->getName().' is not an abstract class');
            }
        }
    }

    class 
    ClassStub extends Stub
    {
        public 
    $decorated;
        public function 
    __construct($decorated$name$class)
        {
            
    parent::__construct($name$class);
            
    $this->decorated $decorated;
        }

        public function 
    generate()
        {
            echo 
    "class {$this->name} extends {$this->class->getName()}".NL.'{'.NL;
            foreach ( 
    $this->decorated as $decorate ) {
                
    $decorate->generate();
            }
            echo 
    NL.'}'.NL;
        }
    }

    class 
    ClassConstruct extends Stub
    {
        public function 
    __construct($name$class)
        {
            
    parent::__construct($name$class);
        }

        public function 
    generate()
        {
            echo 
    TAB.'public function __construct('.$this->getParam('__construct'true).')'.NL.TAB.'{'.NL;
            if ( 
    $this->hasMethod('__construct') ) {
                echo 
    TAB.TAB.'parent::__construct('.$this->getParam('__construct'false).')'.NL;
            }
            
    $this->executeHelper('__construct');
            echo 
    TAB.'}'.NL;
        }
    }

    class 
    ClassAbstract extends Stub
    {
        public function 
    __construct($name$class)
        {
            
    parent::__construct($name$class);
        }

        public function 
    generate()
        {
            echo 
    "\n\n//Created abstract methods does here...\n";
        }
    }

    class 
    ARConstruct
    {
        public function 
    generate()
        {
            echo 
    TAB.TAB.'if ( $id ) {'.NL;
            echo 
    TAB.TAB.TAB.'$this->set($this->primary[0], $id);'.NL;
            echo 
    TAB.TAB.TAB.'$this->fetch();'.NL;
            echo 
    TAB.TAB.'}'.NL;
        }

    Thanks for your time and input,

    --
    lv


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •