RSS
 

Archive for June 28th, 2010

Code-Completion Console Script

28 Jun

Many good IDEs for Webdevelopment cannot automatically understand the structure of a framework and its classes/objects. They need a small file initializing the objects so that they know in which context they appear.

E.g.: $this->Session (Session Helper) its impossible for the IDE to know that the the helpers are inside the View class. Same goes for controller components and model behaviors

I wrote a little script which produces mockup code. My IDE “PHPDesigner” works very well with it.

Usage

Drop this script into the /shells folder (either vendors or app). Now just type cake cc inside the cake shell.

Code

<?php
 
App::import('Core', 'Folder');
App::import('Core', 'File');
 
/**
 * Code Completion
 * 2009-12-26 ms
 */
class CcShell extends Shell {
    var $uses = array();
 
    private $content = '';
 
    function main() {
        $this->out('AutoComplete Dump');
 
        //TODO: ask for version (1.2 etc - defaults to 1.3!)
 
        $this->filename = APP.'code_completion__.php';
 
        # get classes
     $this->models();
        $this->components();
        $this->helpers();
        //TODO: behaviors
 
        # write to file
     $this->_dump();
 
        $this->out('...done');
    }
 
    /**
     * @deprecated
     * now use: Configure::listObjects()
     */
    function __getFiles($folder) {
        $handle = new Folder($folder);
        $handleFiles = $handle->read(true, true);
        $files = $handleFiles[1];
        foreach ($files as $key => $file) {
            $file = extractPathInfo('file', $file);
 
            if (mb_strrpos($file, '_') === mb_strlen($file) - 1) { # ending with _ like test_.php
             unset($files[$key]);
            } else {
                $files[$key] = Inflector::camelize($file);
            }
        }
        return $files;
    }
 
 
    public function _getFiles($type) {
    $files = App::objects($type);
    # lib
    $paths = (array)App::path($type.'s');
    $libFiles = App::objects($type, $paths[0] . 'lib' . DS, false);
 
    $plugins = App::objects('plugin');
    if (!empty($plugins)) {
      foreach ($plugins as $plugin) {
         $pluginFiles = App::objects($type, App::pluginPath($plugin) . $type.'s' . DS, false);
          if (!empty($pluginFiles)) {
              foreach ($pluginFiles as $t) {
                  $files[] = $t; //"$plugin.$type";
              }
          }
      }
    }
    $files = array_merge($files, $libFiles);
    $files = array_unique($files);
 
        $appIndex = array_search('App', $files);
        if ($appIndex !== false) {
            unset($files[$appIndex]);
        }
 
        # no test/tmp files etc (helper.test.php or helper.OLD.php)
    foreach ($files as $key => $file) {
            if (strpos($file, '.') !== false || !preg_match('/^[\da-zA-Z_]+$/', $file)) {
                unset($files[$key]);
            }
        }
    return $files;
    }
 
 
    function models() {
        //$files = App::objects('component', null, false);
        $files = $this->_getFiles('model');
        //$files = $this->_getFiles(COMPONENTS);
 
        $content = LF.'<?php'.LF;
        $content .= '/*** model start ***/'.LF;
        $content .= 'class AppModel extends Model {'.LF;
        if (!empty($files)) {
            $content .= $this->_prepModels($files);
        }
        $content .= '}'.LF;
        $content .= '/*** model end ***/'.LF;
        $content .= '?>';
 
        $this->content .= $content;
    }
 
    function components() {
        $files = $this->_getFiles('component');
 
        $content = LF.'<?php'.LF;
        $content .= '/*** component start ***/'.LF;
        $content .= 'class AppController extends Controller {'.LF;
        if (!empty($files)) {
            $content .= $this->_prepComponents($files);
        }
        $content .= '}'.LF;
        $content .= '/*** component end ***/'.LF;
        $content .= '?>';
 
        $this->content .= $content;
    }
 
    function helpers() {
        $files = $this->_getFiles('helper');
 
        $content = LF.'<?php'.LF;
        $content .= '/*** helper start ***/'.LF;
        $content .= 'class AppHelper extends Helper {'.LF;
        if (!empty($files)) {
            $content .= $this->_prepHelpers($files);
        }
        $content .= '}'.LF;
        $content .= '/*** helper end ***/'.LF;
        $content .= '?>';
 
        $this->content .= $content;
    }
 
    function _prepModels($files) {
        $res = '';
        foreach ($files as $name) {
            $res .= '
    /**
    * '.$name.'
    */
    public $'.$name.';
'.LF;
        }
 
        $res .= '   function __construct() {';
 
        foreach ($files as $name) {
            $res .= '
        $this->'.$name.' = new '.$name.'();';
        }
 
        $res .= '}'.LF;
        return $res;
    }
 
    function _prepComponents($files) {
        $res = '';
        foreach ($files as $name) {
            $res .= '
    /**
    * '.$name.'Component
    */
    public $'.$name.';
'.LF;
        }
 
        $res .= '   function __construct() {';
 
        foreach ($files as $name) {
            $res .= '
        $this->'.$name.' = new '.$name.'Component();';
        }
 
        $res .= '}'.LF;
        return $res;
    }
 
    function _prepHelpers($files) {
        # new ones
     $res = '';
 
        foreach ($files as $name) {
            $res .= '
    /**
    * '.$name.'Helper
    */
    public $'.$name.';
'.LF;
        }
 
        $res .= '   function __construct() {';
 
        foreach ($files as $name) {
            $res .= '
        $this->'.$name.' = new '.$name.'Helper();';
        }
 
        # old ones
     $res .= ''.LF;
        /*
        foreach ($files as $name) {
        $res .= '
        $'.lcfirst($name).' = new '.$name.'Helper();
        ';
        }
        $res .= LF;
        */
 
        $res .= '   }'.LF;
 
        return $res;
    }
 
 
    function _dump() {
        $file = new File($this->filename, true);
 
        $content = '<?php exit();'.LF;
        $content .= '//Add in some helpers so the code assist works much better'.LF;
        $content .= '//Printed: '.date('d.m.Y, H:i:s').LF;
        $content .= '?>'.LF;
        $content .= $this->content;
        return $file->write($content);
    }
}
 
?>

Result

The file will look like:

class AppModel extends Model {
    /**
    * Address
    */
    public $Address;
    ...
 
    function __construct() {
        $this->Address = new Address();
        ...
    }
}
class AppController extends Controller {
    /**
    * AclComponent
    */
    public $Acl;
    ...
    function __construct() {
        $this->Acl = new AclComponent();
        ...
    }
}
class AppHelper extends Helper {
    /**
    * AjaxHelper
    */
    public $Ajax;
    ...
    function __construct() {
        $this->Ajax = new AjaxHelper();
        ...
    }
}

Final notes

Feel free to update the TODOs and send me the improved file. A test file would be awesome, too, i guess :)

For LF constants see my article about “bootstrap goodies”.

 
3 Comments

Posted in CakePHP