Hello, Thank you for visiting my remember the code blog.

I am currently trying to learn how to use Zend Framework. It is a very good and flexible framework and easier to understand compared to CakePHP or Symfony frameworks. Well I was trying some example codes with the Eclipse 3.4 & PDT 2.0 package and found some issues that code completion does not work as I want it to.

One of the biggest problem was that the code completion does not work with Zend_Regsitry. When you retrieve a object from the registry and try to execute the code completion (you can force it by Ctrl + Space), it doesn’t work, since Eclipse doesn’t know what type of data it is.

I really like Eclipse but with partially working code completion, it is very annoying and I was really hoping for the better working code completion function.

So I was searching for the various website for any quick solutions and here they are. I spent the whole day yesterday, and it was very difficult for me to gather all these information. So please enjoy and leave some comments if you can. Thanks.

1. Use PHPDoc Style definition to define data type of your custom functions arguments and return value.

//Here is the PHPDoc defined by myself right above the custom function
/**
 *
 * @param  mixed $config Some configuration information for the DB connection 
 * @param  CustomClass $object Special Custom Class  
 * @return Zend_Db_Adapter_Abstract
 * @throws Zend_Db_Exception
 */
function getDbAdapter($config,$object)
{
	//In the below line, Code Completion will work since Eclipse knows the type from the 
	//PHPDoc @param entry.
	$object->  
 
	return $db;
}
 
 
$db=getDbAdapter($config,$object);    
//Code Completion will also work with $db variable below since the return type was defined 
// in the getDbAdapter function's PHPDoc.
$db->

2. Providing code completion hints for the Class member variable.
Still PHPDoc is the answer for the code completion to work with class member variables.

class HelloClass
{	
	/**
	 *  @var Zend_Db_Adapter_Abstract
	 */
	public $db2;
 
	public function __construct()
	{
		$this->db2 = Zend_Registry::get('dbAdapter');
 
		//Code Completion will work since @var PHPDoc was defined with the necessary class type 
		// in the class member definition above.
		$this->db2->
	}
}

3. Make code completion working with any variable
In this case, use standard comment format with @var. This method should be used only when the code completion does not automatically work.

/* @var $variable_name ClassName */

Working Example

$registry = Zend_Registry::getInstance();
 
/* @var $db3 Zend_Db_Adapter_Abstract */
$db3=$registry->dbAdapter;  //before using the variable for the code completion, the variable must be initialized.
$db3->

One thing to be careful with the third convention is that you need to initialize the variable first before testing code completion. Most of the time, it works wonderfully, but I found that it is not working with the direct call of Zend_Registry’s get method.

/* @var $db3 Zend_Db_Adapter_Abstract */
$db3 = Zend_Registry::get('dbadapter');
$db3-> //code completion would not work because...

The reason is that the the method called had PHPDoc with @return value ‘mixed’ or ‘unknown_type’. Here is the get method from the Registry.php. As you can see, the @return value is ‘mixed’.

/**
 * getter method, basically same as offsetGet().
 *
 * This method can be called from an object of type Zend_Registry, or it
 * can be called statically.  In the latter case, it uses the default
 * static instance stored in the class.
 *
 * @param string $index - get the value associated with $index
 * @return mixed
 * @throws Zend_Exception if no entry is registerd for $index.
 */
public static function get($index)
{
    $instance = self::getInstance();
 
    if (!$instance->offsetExists($index)) {
        require_once 'Zend/Exception.php';
        throw new Zend_Exception("No entry is registered for key '$index'");
    }
 
    return $instance->offsetGet($index);
}

Another non-working case is when the variable is assigned again after the first assignment.

/* @var $db3 Zend_Db_Adapter_Abstract */
$db3 = $abc;
$dbx-> //Code completion is working here.
$db3 = $def;
$db3-> //code completion is not working here because $db3 was assigned more than once

Then what do you do if you want to use Zend_Registry::get() without @var comment?
You can create a custom function or class method with PHPDoc with proper @return type defined. Inside the function, the Zend_Registry::get() is called .

class GlobalUtil
{
	/**
	 * @return Zend_Db_Adapter_Abstract
	 */
	public static function getDbAdapter($index='')
	{
		if ($index == '') $index='dbadapter';
		return Zend_Registry::get($index);
	}
}
 
 
$db=GlobalUtil::getDbAdapter();
$db-> //Code completion is working here.

There will not be much cases of using the third convention since most cases, the Eclipse will be able to handle the code completion automatically. However it’s good to know how to make it work.

Here is my last comment on the code completion. In order for the code completion to work properly, you have to have the Zend Framework files inside the project folder.

Zend Framework data files are about 20MB and I didn’t want to make duplicated copies for different php project, so I made a central location folder to store Zend library data and shared it with other PHP project by adding this folder to each project’s include path.

This idea seemed OK at first, but I realized that for some cases, the code completion didn’t work even if it was working OK when the Zend Library was located within the project folder!

The solution is very simple. You can just create a new folder within the project folder and use the advanced button to enable the ‘Link to folder in the file system’ option. Then you can browse and set the actual library folder where the actual Zend framework files are located. This will result the same effect as if the external folder is located within the project folder. Now we can have the desired code completion fully working.

So if you follow my above code completion guide , I believe that you will be able to enjoy the code completion for the most of the time. If you find the cases that the code completion is still not working , leave me a comment! I will gladly help you to resolve the problem.

Share and Enjoy:
  • DZone
  • Twitter
  • Technorati
  • Reddit
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Diigo


13 Responses to “Eclipse PDT Code Completion and Zend Framework”

  1.   thomas Says:

    Thank you for those hints.

    Do you have any idea on how to have code completion work with getters/setters ?

    I mean if I have an instance method of MyClass :

    getIsGuest() {}

    how do I have the isGuest ppty to be listed when I write :

    $a = new MyClass();
    $a->… (Ctrl+Space)

    Thanks

    thomas

  2.   HanaDaddy Says:

    I am not sure if I understand correctly, but you can specifically define member variable in the class and it will appear in the code completion list.

    class MyClass {
    public $isGuest;
    public function getIsGuest(){ return $this->isGuest ; }
    }

    $a = new MyClass();
    $a-> … (isGuest will appear in the code list)

    But if you trying to achieve this without defining member variable, I have no idea.

  3.   thomas Says:

    hi

    this would be a solution :)

    but I thought maybe there would be an automatic recognition of getters/setters which would allow for code hinting following the php ability to define getters like :

    function getIsGuest() {}

    and have isGuest appear in the hinted ppties list …

    do i make sense ?

    thanks

  4.   Free Text Editors « Hana & Sarah’s Freeware blog Says:

    [...] If you are looking for more professional programing editor, you may want to try Eclipse. It is a development IDE and the size is big (over 200MB). Here is the link to PHP version (Eclipse-PDT project). The greatest thing about Eclipse is that it’s a full development platform which is free and supports almost perfect Code Completion. Check out my article on the Eclipse PHP Code Completion. [...]

  5.   pyd Says:

    “The solution is very simple…” but i [only-first] found it here.
    Thanks a lot.

  6.   emeraldjava Says:

    any chance you can post your eclipse .proeject file?

  7.   HanaDaddy Says:

    Here is my .project file .

    <?xml version="1.0" encoding="UTF-8"?>
    <projectDescription>
    	<name>zendframe</name>
    	<comment></comment>
    	<projects>
    	</projects>
    	<buildSpec>
    		<buildCommand>
    			<name>org.eclipse.wst.validation.validationbuilder</name>
    			<arguments>
    			</arguments>
    		</buildCommand>
    		<buildCommand>
    			<name>org.eclipse.dltk.core.scriptbuilder</name>
    			<arguments>
    			</arguments>
    		</buildCommand>
    	</buildSpec>
    	<natures>
    		<nature>org.eclipse.php.core.PHPNature</nature>
    	</natures>
    	<linkedResources>
    		<link>
    			<name>library</name>
    			<type>2</type>
    			<location>D:/eclipse_project_php/library</location>
    		</link>
    	</linkedResources>
    </projectDescription>
  8.   Roy Says:

    nice review! thanks.

    Two more suggestions:
    1. use CamelCase format for fast code assist:

    $a = new ZeConAc|

    will assist you get this code:

    $a = new Zend_Controller_Action|

    2. use the @method or @property to declare magic fields and methods.

    more: http://www.slideshare.net/royganor/zend-studio-tips-and-tricks

  9.   HanaDaddy Says:

    That’s a great tip ! Thank you Roy.

  10.   ninos Says:

    Hi,
    How can you specify the object type within an array?

    say for example, a method returns an array of MyObject, how would you write that in the doc help comments?

    I tried
    /**
    * return a list of MyObjects
    *
    * @param int $int
    * @return Array[MyObject]
    */

  11.   HanaDaddy Says:

    How about this?

    class MyObject {
     
    	var $member;
     
    	public function MyObject() {
     
     
    	}
    }
     
     
     
    /**
    * return a list of MyObjects
    *
    * @param int $int
    * @return mixed $ret
    */
    function testf (){
     
    	$arr=array(new MyObject(),new MyObject());
     
    	return $arr;
    }
     
     
    $ret=testf();
     
    for ($i=1; $i < 2; $i++){
     
    	/* @var $obj MyObject */
    	$obj=$ret[$i];
     
    	$obj->  //Here the code completion works for me 
    }
  12.   ninos Says:

    Thanks, that worked :D

  13.   Anthony Says:

    Perfect! Just want to say thank you for finding this out. Your option 3 worked well for a project that was using WSDL generated classes and saved me a heap of time!

Leave a Reply