PhpStorm and foreach elements type inference (problem in new 2016.2)

I was using the signature of the "array" elements in sentences like this:

foreach($array as $value){

     $value-> 

}

to get the correct type for $value inside my "php.typeProvider2" extension point (which I can see now there is a new php.typeProvider3).

But in PhpStorm 2016.2 the signature seems to be different, before I was getting something like this for $array signature:

"#[MYKEYHERE][MYSIGNATUREHERE]"

now I'm getting:

"#Varray"

which is really useless to detect what is inside "$array"

Do you have any clue about how to solve this? (because we cannot use resolve or indexes inside php.typeProvider2 getType method)

Also is there any important difference with the new php.typeProvider3? 

 

Thanks !!

8 comments
Comment actions Permalink
Official comment

Significant changes were made for iteration over SPL interfaces in 2016.2, might be some undesired side effects.

Can you please file a bug report with *fully self-contained example* to https://youtrack.jetbrains.com/issues/WI ?

Comment actions Permalink

Hi Alexey, I have fixed it using this:

((PhpTypedElement) element).getType().getTypes();  // this includes "#[MYKEYHERE][MYSIGNATUREHERE]"

instead of:


((Variable) element).getSignature();   // this returns "#Varray"

do you think is a safe solution? or that could make the plugin incompatible with previous PhpStorm version?

0
Comment actions Permalink

your solution is looks appropriate, but I'm unable to truly verify it w/o self contained example & extension sources.

0
Comment actions Permalink

Hi Alexey, sorry for the delay I was trying to solve the problem by myself, but I'm stuck at this point.

I was able to solve some situations, but there are some cases that I don't know what to do

What I'm trying to do is to return the correct type inside "foreach" loops, for every item

This is my code for typeProvider2 extension point (simplified):

// what I'm doing is to get the psiElement for $items when $item type is requested, and based on the type of $items I can deduct the type of every $item

if(e instanceof Variable)
{
ForeachStatement foreach = PsiTreeUtil.getParentOfType(e, ForeachStatement.class, false);
if(foreach != null)
{
String[] collectionTypes = {PsiPhpHelper.VARIABLE_DECLARATION, PsiPhpHelper.VARIABLE, PsiPhpHelper.METHOD_REFERENCE};
PsiElement collection = PsiPhpHelper.findPrevSiblingOfType(e, collectionTypes);
if (collection != null)
{
if(collection instanceof PhpTypedElement){
Set<String> types = ((PhpTypedElement) collection).getType().getTypes();

// ...

}
}
}
}

I was able to solve situations like this:

$items = Mage::getModel('sales/quote')->getAllItems();
foreach($items as $item)
{
$item->[AUTOCOMPLETE HERE WORKS FINE]
}

because what I'm doing is to get the psiElement for $items when $item type is requested, and based on the type of $items I can deduct the type of every $item

The previous PHP works fine because when I get the "types" of $items:

((PhpTypedElement) collection).getType().getTypes();

I get:

#M#M#C\Mage.getModel.getAllVisibleItems|#M#ÑMODEL=sales/quote.getAllVisibleItems

And using #M#ÑMODEL=sales/quote.getAllVisibleItems I can deduct the type of $item

But with this PHP code:

$quote = Mage::helper('checkout')->getQuote();
$items = $quote->getAllItems();
foreach($items as $item)
{
$item->[AUTOCOMPLETE DOES NOT WORK HERE]
}

I get these types for $items:

#M#M#M#C\Mage.helper.getQuote.getAllItems|#M#M#ÑHELPER=checkout.getQuote.getAllItems|?

And I can't deduct the correct type for $items from that, because it has two chained methods in what seems to be my signature:

"HELPER=checkout.getQuote.getAllItems"

 

Is there any way to get a more straightforward type for every psiElement? not a long chained signature?

Because

$items = $quote->getAllItems();

and

$quote = Mage::helper('checkout')->getQuote();

so the "type" for $items ends being:

"checkout.getQuote.getAllItems"

but PhpStorm already *knows* what type $quote is, is not possible to get the type similar to:

Mage_Sales_Model_Quote.getAllItems

Mage_Sales_Model_Quote is the final type for $quote, and PhpStorm already processed it, because $quote->AUTOCOMPLETE is working fine so PhpStorms know exactly what class is $quote... why then the signature for $quote->getAllItems() is not using the final type of $quote instead of chaining 3 method calls?

 

I hope you have some tip to solve this because I'm out of ideas right now...

Thanks!!

 

 

 

 

0
Comment actions Permalink

you shall provide dynamic types for both `getHelper()` and  `->getAllItems()`.

specifically getAllItems type as `yourDTO[]` or `yourClassWhichImplementsIterableOrArrayAccess`, dunno what is ther in magento.

As a bonus - you can DROP all your code for foreach, IDE knows how to get element type `yourDTO` it for you. 

 

Idea is that you provide best dynamic type (as detailed as possible) for every call that is not documented properly (eg mixed or array) to augment IDE's engine/poor library docs.

0
Comment actions Permalink

Hi Alexey, is not so easy with Magento, classes are very dynamic and can be overrided by config files.

Magento has a special type of class called "Collection", those classes are implementin IteratorAggregate and Countable, and PhpStorm doesn't understand what type the items of that class should be.

This code for example:

$collection = new Mage_Catalog_Model_Resource_Product_Collection(null, null);
foreach($collection as $product){
$product->[NO AUTOCOMPLETE HERE]
}

And the situation is even worst because in Magento you never instantiate a class like that, always using factories (that could return any type of class basically, because can be overriden by config files)

But with the plugin I was hable to make this work this scenario:

$collection = Mage::getModel('catalog/product')->getCollection();
foreach($collection as $product){
$product->[AUTOCOMPLETE WORKS HERE, BUT ONLY BECAUSE OF THE FOREACH EXTRA CODE IN TYPEPROVIDER]
}

Because the plugin now gives the correct type for $collection, and also because the code that detects $product is inside a foreach loop, so I can get the $collection, then get the "type" for $collection and deduct based on that the type for "$product"

The problem is that $collection is defined with multiple method calls, then the signature (type) for $collection is not useful anymore.

$collection = Mage::getModel('catalog/product')->getCollection();

returns these types:

#M#M#C\Mage.getModel.getCollection

#M#ÑMODEL=catalog/product.getCollection

(and the second one, from my signature is useful to deduct the type of items for that collection)

But this code:

$p = Mage::helper('catalog')->getProduct();
$collection = $p->getCollection();
foreach($collection as $product){
$product->
}

return these types:

#M#M#M#C\Mage.helper.getProduct.getCollection

#M#M#ÑHELPER=catalog.getProduct.getCollection

which is really hard to predict the type from that, without doing a lot of examination (which is not convenient inside the type provider method, and we cannot use indixes from there if I right remember)

The problem is that PhpStorm understand very well what type is $collection, I can have autocomplete for $collection, so PhpStorm knows exactly what "type", what class is $collection, but I cannot get that from the plugin, inside the type provider.

Is not possible to get the type for $collection like:

#M#C\Mage_Catalog_Model_Resource_Product_Collection

Because again, PhpStorm already knows that is the type, is using that class when I need autocomplete on $collection->HERE

 

I was thinking some hacks to make it work with foreach, but I feel must be an easier way... I feel the info about the final type of "$collection" should be accessible somehow

 

0
Comment actions Permalink

Do you have any tip or extra help to make this work better Alexey?

basically what I need is a way to get resolved signatures for chained methods.

I mean if we have:

Mage::helper('catalog')->getProduct()->getCollection()

to get something like:

#M#C\CLASS_NAME_HERE

instead of complex signatures like:

#M#M#M#C\Mage.helper.getProduct.getCollection

#M#M#ÑHELPER=catalog.getProduct.getCollection

I think it must be possible someway because PhpStorm already knows what "class" is returning that method call, the autocomplete works fine, but I can't deduct the class from my plugin using the signatures/types

 

 

0
Comment actions Permalink

I was trying to use 

PhpCaches.TYPE_COMPLETION_CACHE

but is very unstable

 

0

Please sign in to leave a comment.