Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Extending PDT
Contents
Purpose
There are different purposes for extending PDT. One of them is adding support for specific PHP framework to the IDE features like: Code Assist, Navigation (CTRL + click), Presentation (Outline, PHP Explorer). In this document we'll describe how to achieve these goals using PDT extension points.
Extending
Code Assist
Type inference hinting
Suppose your framework uses the following language structure for object instantiation:
$myObject = ClassRegistry::init('MyClass');
In this case PDT type inference engine is unable to detect the type of $myObject variable, so we'll have to add a specific rule that helps him.
org.eclipse.php.core.goalEvaluatorFactories extension point allows to provide additional rules to the PHP type inference engine. For our example what we'll need to contribute is:
<extension point="org.eclipse.php.core.goalEvaluatorFactories"> <factory class="com.xyz.php.fmwrk.XYZGoalEvaluatorFactory" priority="100"> </factory> </extension>
Please note the priority is set to 100 in order to override the default PHP goal evaluator (its priority is 10).
public class XYZGoalEvaluatorFactory implements IGoalEvaluatorFactory { public GoalEvaluator createEvaluator(IGoal goal) { Class<?> goalClass = goal.getClass(); // We're overriding only the expression type goal: if (goalClass == ExpressionTypeGoal.class) { ASTNode expression = ((ExpressionTypeGoal) goal).getExpression(); // Check the expression AST node type if (expression instanceof StaticMethodInvocation) { StaticMethodInvocation inv = (StaticMethodInvocation) expression; ASTNode reciever = inv.getReceiver(); // Check that the class name is 'CallRegistry': if (reciever instanceof SimpleReference && "ClassRegistry".equals(((SimpleReference) reciever) .getName())) { // Check that the method name is 'init' if ("init".equals(inv.getCallName().getName())) { // Take the first call argument: List arguments = inv.getArgs().getChilds(); if (arguments.size() == 1) { Object first = arguments.get(0); if (first instanceof Scalar && ((Scalar) first).getScalarType() == Scalar.TYPE_STRING) { String className = ((Scalar) first).getValue(); // Return the evaluated type through dummy // evaluator return new DummyGoalEvaluator(goal, className); } } } } } } // Give the control to the default PHP goal evaluator return null; } }
public class DummyGoalEvaluator extends GoalEvaluator { private String className; public DummyGoalEvaluator(IGoal goal, String className) { super(goal); this.className = className; } public Object produceResult() { return new PHPClassType(className); } public IGoal[] init() { return null; } public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) { return null; } }
That's all. In case if 'MyClass' exists Code Assist and Navigation will work out of the box since they are both based on the type inference engine.
Code assist strategies
Some PHP frameworks provide class fields or methods that are not declared explicitly in code. For example, Zend Framework view helpers can be accessed from the view class using:
$this->helperName()
Behind the scenes, view loads the Zend_View_Helper_HelperName class (note the naming convention), creates an object instance of it, and calls its helperName() method.
In order to provide Code Assist for "helperName" after $this we'll need to extend the following extensions:
- org.eclipse.php.core.completionContextResolvers
- org.eclipse.php.core.completionStrategyFactories