RSS
 

Posts Tagged ‘Code Completion’

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