Need help with DynamicReturnTypePlugin?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

pbyrne84
131 Stars 9 Forks 294 Commits 25 Opened issues

Services available

!
?

Need anything else?

Contributors list

No Data

DynamicReturnTypePlugin

Version Downloads Downloads last month

( Jetbrains url https://plugins.jetbrains.com/plugin/7251 )

Intellij/Phpstorm PHP plugin to allow a dynamic return type from method/function calls. It can use either the instance type of the passed parameter or a string lookup. Note: Use fully qualified names.

Example project https://github.com/pbyrne84/DynamicReturnTypePluginTestEnvironment

Note about PhpTypeProvider3

Currently the the PhpTypeProvider used has been deprecated. PhpTypeProvider3 has changed behaviour significantly and it
looks like for general use it will be a lot better for other plugins, but it will require re-engineering the parts these changes have effected, such as interop with Symfony plugin.

I am keeping an eye on this as I do not like having integration tests broken. This is also why I have not released any further changes as currently I cannot guarantee quality of release.

Anyway apologies if anyone has been effected and it seems like I am ignoring them :)

Installing from zip in deploy

download zip from https://github.com/pbyrne84/DynamicReturnTypePlugin/blob/master/deploy/DynamicReturnTypePlugin.zip

Delete original DynamicReturnTypePlugin folder in plugin directory and unzip new one in same location.

Example usage, if done right no errors will be evident and things like methods such appendChild are automated refactor safe across tests.


  1. Generic like calls to simulate Phockito ( PHP Mockito) verify calls that return the object passed in. eg. Phockito php example ```php class DynamicReturnTypeTest extends PHPUnitFrameworkTestCase { /** @var Phockito */ private $phockito;

    protected function setUp() { $this->phockito = new Phockito(); }

    public function test_failsAsCallIsNotMade() { $domDocument = mock('\DomDocument'); $newNode = mock('\DomElement');

    verify($domDocument)->appendChild($newNode);
    

    }

    public function test_succeedsAsCallIsMade() { $domDocument = mock('\DomDocument'); $newNode = mock('\DomElement');

    $domDocument->appendChild($newNode);
    
    

    verify($domDocument)->appendChild($newNode);

    }

    public function test_staticPhockitoCall() { $domDocument = Phockito::mock('\DomDocument'); $newNode = Phockito::mock('\DomElement');

    $domDocument->appendChild($newNode);
    
    

    Phockito::verify( $domDocument )->appendChild($newNode);

    }

    public function test_instancePhockitoCall() { $domDocument = $this->phockito->mock('\DomDocument'); $newNode = $this->phockito->mock('\DomElement');

    $domDocument->appendChild($newNode);
    
    

    $this->phockito->verify( $domDocument )->appendChild($newNode);

    } } ```

Configuration

Currently it looks for a files called dynamicReturnTypeMeta.json anywhere in the project. It also internally combines them to make a single project config. An example json file for the above test is

{
    "methodCalls": [
        {
            "class": "\\Phockito",
            "method": "mock",
            "position": 0
        },
        {
            "class": "\\Phockito",
            "method": "verify",
            "position": 0
        }
    ],
    "functionCalls": [
        {
            "function": "\\verify",
            "position": 0
        },
        {
            "function": "\\mock",
            "position": 0
        }
    ]
}

Position is the parameter index that decides what will be the return type.

Optional Configuration

There are currently 2 return type manipulation strategies.

1.Masks

These were the original replacement strategy but it has been found to be a rather limiting approach to the problem of frameworks containers expecting strings as parameters that do not easily match the final result without some string reduction/manipulation.

It simply works by using String.format on the the result.

For example the following configuration.

json
{
"class": "\\Phockito",
"method": "maskExample",
"position": 0,
"mask": "Test%sModel"
}

Would cause the following to return TestUserModel

php
Phockito::maskExample('User');

There was no easy way to solve changing 'Entity\User' into 'MyNameSpace\User' without going down the path of writing a strategy for each custom user/framework request due to designs I have no knowledge of so cannot preempt. I would view this method as deprecated due to the limitations. But it will not be removed.

2.Script engine calls

NOTE: Script engine jar paths can now be set using IDEAGROOVYJARPATH and IDEAJAVASCRIPTJARPATH environment variables. IDEAJAVASCRIPTJAR_PATH is for the path to the nashorn.jar file for javascript in JDK8.

This allows custom code to be executed within the vm designed per consumer/framework. The 2 languages on offer are javascript via rhino and groovy if the groovy-all-2.2.1.jar is present in the ides lib folder. For interoperability between rhino and java the following reading is quite useful.

https://developer.mozilla.org/en-US/docs/Rhino/Scripting_Java

The benefit of using groovy is intellij idea(either edition) has better editing capabilities, though this requires setting up the IDEAGROOVYJAR_PATH path to groovy-all-2.2.1.jar (I set mine to point to the intellij ultimate jar).

To use groovy instead of javascript just use the .groovy extension (rhino is the default behaviour).

An example configuration

{
    "class"                        : "\\Phockito",
    "method"                       : "javascriptMaskMock",
    "position"                     : 0,
    "fileReturnTypeReplacementCall": ["JavaScriptReplacementCallback.js", "replaceWithJavaScriptAliasing"]
}

Would cause the following to return TestFooModel

php
\\Phockito::javascriptMaskMock('Entity\User');

This manipulation causes the function 'replaceWithJavaScriptAliasing' to be called in JavaScriptReplacementCallback.js. The function needs to return a string and is formatted like the following example.

function replaceWithJavaScriptAliasing( returnTypeNameSpace, returnTypeClass ){
    if( returnTypeNameSpace == 'Entity' ) {
        if( returnTypeClass == 'User' ) {
            return 'Test_Foo_Model';
        }else if( returnTypeClass == 'Test' ){
            return 'DynamicReturnTypePluginTestEnvironment\\TestClasses\\TestEntity';
        }
    }

if( returnTypeNameSpace == '' ) {
    return returnTypeClass;

}

return returnTypeNameSpace + "\\" + returnTypeClass;

}

returnTypeNameSpace and returnTypeClass are separated before hand to ease manipulation within the function call. The returnTypeNameSpace trims the leading slash so \DomDocument is DomDocument.

Replacement callback file handling/compilation.

The only restriction is the script file must be in the the same folder as its related dynamicReturnTypeMeta.json. This restriction may be lifted at some point but it adds complexity.

Recompilation is triggered when a dynamicReturnTypeMeta.json is altered (say adding a space and pressing enter). This restriction will be removed now I can write compilation errors to the event log versus file log( basically compile on save will be offered).

Api

A variable call api is injected into the script which allows communication back to the ide. This can be expanded on request. A javascript file that will offer completion can be found here : https://github.com/pbyrne84/DynamicReturnTypePluginTestEnvironment/blob/master/ExecutingScriptApi.js

Example initialisation can be seen here at the top : https://github.com/pbyrne84/DynamicReturnTypePluginTestEnvironment/blob/master/JavaScriptReplacementCallback.js

Debugging

api.writeToEventLog("your message")

Will write a message to the event log in the ide. Errors in your script will also appear here.

Notes/Todos

Unfortunately I cannot offer array access as the open api would have to alias the key to the offsetGet call internally which would trigger this plugin to be called. It was mentioned here in the task that started it all :- http://youtrack.jetbrains.com/issue/WI-6027

I do plan to add file handling to the api so you can talk to the virtual file system for basic tasks and I will also see if I can open up some completion provider methods through the javascript api.

Currently IDEAJAVASCRIPTJAR_PATH is only for nashorn but rhino will be added for historical reasons.

Any probs just give me a shout.

Easiest way to set up a the ide to play with the source code/develop a plugin relying on internal plugins.

When a plugin JDK is setup it only includes default libraries of idea. A php plugin requires some of the following to compile.

(I include all of them as they are in the same folder so proabably they are all needed) + php-openapi.jar + php.jar + resources_en.jar

On my machine I go to my plugin sdk classpaths and manually add the following

  • /opt/jetbrains/IntellijIdea15/config/plugins/php/lib/php-openapi.jar
  • /opt/jetbrains/IntellijIdea15/config/plugins/php/lib/php.jar
  • /opt/jetbrains/IntellijIdea15/config/plugins/php/lib/resources_en.jar

As actually adding them to the lib directory in github will cause major problems with the internal class loader/pico container stuff. I put settings in non normal places so just do a file search.

I also use gson and junit which on my machine is located at + /opt/jetbrains/idea-16/lib/junit-4.12.jar + /opt/jetbrains/idea-16/lib/gson-2.5.jar

These are part of the default idea install. There are quite a few goodies floating around there not included by default. These are also pretty standard libraries so small chance of them being dropped.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.