phpstan-magento icon indicating copy to clipboard operation
phpstan-magento copied to clipboard

Generated class ProductExtension is passed on incorrectly to phpstan

Open t-heuser opened this issue 2 years ago • 6 comments

Hi there! Just updated from v0.23.1 (and phpstan 1.7) to v0.24.0 (and phpstan 1.8). Since the update I'm getting the following error when running phpstan:

Child process error (exit code 255): PHP Fatal error:  Declaration of      
     Magento\Catalog\Api\Data\ProductExtension::setWebsiteIds(?array            
     $websiteIds) must be compatible with                                       
     Magento\Catalog\Api\Data\ProductExtensionInterface::setWebsiteIds($websit  
     eIds) in                                                                   
     /tmp/phpstan/cache/PHPStan/95/c3/95c3cd23c340083ec99b41a2ad173115a2e32d1c  
     .php on line 109                                                           
     Fatal error: Declaration of                                                
     Magento\Catalog\Api\Data\ProductExtension::setWebsiteIds(?array            
     $websiteIds) must be compatible with                                       
     Magento\Catalog\Api\Data\ProductExtensionInterface::setWebsiteIds($websit  
     eIds) in                                                                   
     /tmp/phpstan/cache/PHPStan/95/c3/95c3cd23c340083ec99b41a2ad173115a2e32d1c  
     .php on line 109             

Those are two generated classes from Magento: ProductExtensionInterface

<?php
namespace Magento\Catalog\Api\Data;

/**
 * ExtensionInterface class for @see \Magento\Catalog\Api\Data\ProductInterface
 */
interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
    /**
     * @return int[]|null
     */
    public function getWebsiteIds();

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds($websiteIds);

ProductExtension

<?php
namespace Magento\Catalog\Api\Data;

/**
 * Extension class for @see \Magento\Catalog\Api\Data\ProductInterface
 */
class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements ProductExtensionInterface
{
    /**
     * @return int[]|null
     */
    public function getWebsiteIds()
    {
        return $this->_get('website_ids');
    }

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds($websiteIds)
    {
        $this->setData('website_ids', $websiteIds);
        return $this;
    }

As we can see both classes are generated correctly. In the error message it says that Magento\Catalog\Api\Data\ProductExtension::setWebsiteIds(?array $websiteIds) must be compatible with its interface declaration but in the actual code there is no typed parameter ?array so it has to be somehow generated and passed to phpstan virtually.

FYI: I deleted the phpstan cache before running phpstan.

t-heuser avatar Sep 02 '22 05:09 t-heuser

Looks like #266 is the source of the issue. In wonder why it seemed to work in my simple test case. Maybe I messed something up.

The error appears because the classes generated by this extension take precedence over Magento's generated classes.

Which version of Magento are you using?

shochdoerfer avatar Sep 02 '22 07:09 shochdoerfer

I'm using Magento 2.4.5.

First of all I also thought that #266 is the source of the issue but #266 was introduced in v0.23.1 and there it's working fine. The issue only occurs on v0.24.0.

I'll go for a quick test with v0.24.0 and Magento 2.4.4.

t-heuser avatar Sep 02 '22 07:09 t-heuser

Ok, good to know. Then I would assume the format of the generated classes changed slightly. That would be a total bummer and a bit tricky to fix.

shochdoerfer avatar Sep 02 '22 07:09 shochdoerfer

Magento 2.4.4 is working fine v0.24.0 of this repository. The issue only occurs with Magento 2.4.5 and v0.24.0.

t-heuser avatar Sep 02 '22 07:09 t-heuser

@oneserv-heuser thx a lot for the investigation! Need to figure out a way how to fix this properly.

Any chance you could attach the generated ProductExtensionInterface and ProductExtension files from 2.4.4 and 2.4.5?

shochdoerfer avatar Sep 02 '22 08:09 shochdoerfer

Here are the two classes ProductExtension and ProductExtensionInterface as generated as of Magento 2.4.4 and 2.4.5:

Magento 2.4.4

ProductExtensionInterface
<?php
namespace Magento\Catalog\Api\Data;

/**
* ExtensionInterface class for @see \Magento\Catalog\Api\Data\ProductInterface
*/
interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
  /**
   * @return int[]|null
   */
  public function getWebsiteIds();

  /**
   * @param int[] $websiteIds
   * @return $this
   */
  public function setWebsiteIds($websiteIds);

  /**
   * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
   */
  public function getCategoryLinks();

  /**
   * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
   * @return $this
   */
  public function setCategoryLinks($categoryLinks);

  /**
   * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
   */
  public function getStockItem();

  /**
   * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
   * @return $this
   */
  public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem);

  /**
   * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
   */
  public function getBundleProductOptions();

  /**
   * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
   * @return $this
   */
  public function setBundleProductOptions($bundleProductOptions);

  /**
   * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
   */
  public function getDownloadableProductLinks();

  /**
   * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
   * @return $this
   */
  public function setDownloadableProductLinks($downloadableProductLinks);

  /**
   * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
   */
  public function getDownloadableProductSamples();

  /**
   * @param \Magento\Downloadable\Api\Data\SampleInterface[] $downloadableProductSamples
   * @return $this
   */
  public function setDownloadableProductSamples($downloadableProductSamples);

  /**
   * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
   */
  public function getConfigurableProductOptions();

  /**
   * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $configurableProductOptions
   * @return $this
   */
  public function setConfigurableProductOptions($configurableProductOptions);

  /**
   * @return int[]|null
   */
  public function getConfigurableProductLinks();

  /**
   * @param int[] $configurableProductLinks
   * @return $this
   */
  public function setConfigurableProductLinks($configurableProductLinks);

  /**
   * @return int|null
   */
  public function getCalculationSchemaId();

  /**
   * @param int $calculationSchemaId
   * @return $this
   */
  public function setCalculationSchemaId($calculationSchemaId);
}

ProductExtension
<?php
namespace Magento\Catalog\Api\Data;

/**
* Extension class for @see \Magento\Catalog\Api\Data\ProductInterface
*/
class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements ProductExtensionInterface
{
  /**
   * @return int[]|null
   */
  public function getWebsiteIds()
  {
      return $this->_get('website_ids');
  }

  /**
   * @param int[] $websiteIds
   * @return $this
   */
  public function setWebsiteIds($websiteIds)
  {
      $this->setData('website_ids', $websiteIds);
      return $this;
  }

  /**
   * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
   */
  public function getCategoryLinks()
  {
      return $this->_get('category_links');
  }

  /**
   * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
   * @return $this
   */
  public function setCategoryLinks($categoryLinks)
  {
      $this->setData('category_links', $categoryLinks);
      return $this;
  }

  /**
   * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
   */
  public function getStockItem()
  {
      return $this->_get('stock_item');
  }

  /**
   * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
   * @return $this
   */
  public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
  {
      $this->setData('stock_item', $stockItem);
      return $this;
  }

  /**
   * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
   */
  public function getBundleProductOptions()
  {
      return $this->_get('bundle_product_options');
  }

  /**
   * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
   * @return $this
   */
  public function setBundleProductOptions($bundleProductOptions)
  {
      $this->setData('bundle_product_options', $bundleProductOptions);
      return $this;
  }

  /**
   * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
   */
  public function getDownloadableProductLinks()
  {
      return $this->_get('downloadable_product_links');
  }

  /**
   * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
   * @return $this
   */
  public function setDownloadableProductLinks($downloadableProductLinks)
  {
      $this->setData('downloadable_product_links', $downloadableProductLinks);
      return $this;
  }

  /**
   * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
   */
  public function getDownloadableProductSamples()
  {
      return $this->_get('downloadable_product_samples');
  }

  /**
   * @param \Magento\Downloadable\Api\Data\SampleInterface[] $downloadableProductSamples
   * @return $this
   */
  public function setDownloadableProductSamples($downloadableProductSamples)
  {
      $this->setData('downloadable_product_samples', $downloadableProductSamples);
      return $this;
  }

  /**
   * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
   */
  public function getConfigurableProductOptions()
  {
      return $this->_get('configurable_product_options');
  }

  /**
   * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $configurableProductOptions
   * @return $this
   */
  public function setConfigurableProductOptions($configurableProductOptions)
  {
      $this->setData('configurable_product_options', $configurableProductOptions);
      return $this;
  }

  /**
   * @return int[]|null
   */
  public function getConfigurableProductLinks()
  {
      return $this->_get('configurable_product_links');
  }

  /**
   * @param int[] $configurableProductLinks
   * @return $this
   */
  public function setConfigurableProductLinks($configurableProductLinks)
  {
      $this->setData('configurable_product_links', $configurableProductLinks);
      return $this;
  }

  /**
   * @return int|null
   */
  public function getCalculationSchemaId()
  {
      return $this->_get('calculation_schema_id');
  }

  /**
   * @param int $calculationSchemaId
   * @return $this
   */
  public function setCalculationSchemaId($calculationSchemaId)
  {
      $this->setData('calculation_schema_id', $calculationSchemaId);
      return $this;
  }
}

Magento 2.4.5

ProductExtensionInterface
<?php
namespace Magento\Catalog\Api\Data;

/**
* ExtensionInterface class for @see \Magento\Catalog\Api\Data\ProductInterface
*/
interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
  /**
   * @return int[]|null
   */
  public function getWebsiteIds();

  /**
   * @param int[] $websiteIds
   * @return $this
   */
  public function setWebsiteIds($websiteIds);

  /**
   * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
   */
  public function getCategoryLinks();

  /**
   * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
   * @return $this
   */
  public function setCategoryLinks($categoryLinks);

  /**
   * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
   */
  public function getStockItem();

  /**
   * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
   * @return $this
   */
  public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem);

  /**
   * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
   */
  public function getBundleProductOptions();

  /**
   * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
   * @return $this
   */
  public function setBundleProductOptions($bundleProductOptions);

  /**
   * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
   */
  public function getDownloadableProductLinks();

  /**
   * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
   * @return $this
   */
  public function setDownloadableProductLinks($downloadableProductLinks);

  /**
   * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
   */
  public function getDownloadableProductSamples();

  /**
   * @param \Magento\Downloadable\Api\Data\SampleInterface[] $downloadableProductSamples
   * @return $this
   */
  public function setDownloadableProductSamples($downloadableProductSamples);

  /**
   * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
   */
  public function getConfigurableProductOptions();

  /**
   * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $configurableProductOptions
   * @return $this
   */
  public function setConfigurableProductOptions($configurableProductOptions);

  /**
   * @return int[]|null
   */
  public function getConfigurableProductLinks();

  /**
   * @param int[] $configurableProductLinks
   * @return $this
   */
  public function setConfigurableProductLinks($configurableProductLinks);

  /**
   * @return \Magento\SalesRule\Api\Data\RuleDiscountInterface[]|null
   */
  public function getDiscounts();

  /**
   * @param \Magento\SalesRule\Api\Data\RuleDiscountInterface[] $discounts
   * @return $this
   */
  public function setDiscounts($discounts);

  /**
   * @return int|null
   */
  public function getCalculationSchemaId();

  /**
   * @param int $calculationSchemaId
   * @return $this
   */
  public function setCalculationSchemaId($calculationSchemaId);
}

ProductExtension
<?php
namespace Magento\Catalog\Api\Data;

/**
* Extension class for @see \Magento\Catalog\Api\Data\ProductInterface
*/
class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements ProductExtensionInterface
{
  /**
   * @return int[]|null
   */
  public function getWebsiteIds()
  {
      return $this->_get('website_ids');
  }

  /**
   * @param int[] $websiteIds
   * @return $this
   */
  public function setWebsiteIds($websiteIds)
  {
      $this->setData('website_ids', $websiteIds);
      return $this;
  }

  /**
   * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
   */
  public function getCategoryLinks()
  {
      return $this->_get('category_links');
  }

  /**
   * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
   * @return $this
   */
  public function setCategoryLinks($categoryLinks)
  {
      $this->setData('category_links', $categoryLinks);
      return $this;
  }

  /**
   * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
   */
  public function getStockItem()
  {
      return $this->_get('stock_item');
  }

  /**
   * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
   * @return $this
   */
  public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
  {
      $this->setData('stock_item', $stockItem);
      return $this;
  }

  /**
   * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
   */
  public function getBundleProductOptions()
  {
      return $this->_get('bundle_product_options');
  }

  /**
   * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
   * @return $this
   */
  public function setBundleProductOptions($bundleProductOptions)
  {
      $this->setData('bundle_product_options', $bundleProductOptions);
      return $this;
  }

  /**
   * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
   */
  public function getDownloadableProductLinks()
  {
      return $this->_get('downloadable_product_links');
  }

  /**
   * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
   * @return $this
   */
  public function setDownloadableProductLinks($downloadableProductLinks)
  {
      $this->setData('downloadable_product_links', $downloadableProductLinks);
      return $this;
  }

  /**
   * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
   */
  public function getDownloadableProductSamples()
  {
      return $this->_get('downloadable_product_samples');
  }

  /**
   * @param \Magento\Downloadable\Api\Data\SampleInterface[] $downloadableProductSamples
   * @return $this
   */
  public function setDownloadableProductSamples($downloadableProductSamples)
  {
      $this->setData('downloadable_product_samples', $downloadableProductSamples);
      return $this;
  }

  /**
   * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
   */
  public function getConfigurableProductOptions()
  {
      return $this->_get('configurable_product_options');
  }

  /**
   * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $configurableProductOptions
   * @return $this
   */
  public function setConfigurableProductOptions($configurableProductOptions)
  {
      $this->setData('configurable_product_options', $configurableProductOptions);
      return $this;
  }

  /**
   * @return int[]|null
   */
  public function getConfigurableProductLinks()
  {
      return $this->_get('configurable_product_links');
  }

  /**
   * @param int[] $configurableProductLinks
   * @return $this
   */
  public function setConfigurableProductLinks($configurableProductLinks)
  {
      $this->setData('configurable_product_links', $configurableProductLinks);
      return $this;
  }

  /**
   * @return \Magento\SalesRule\Api\Data\RuleDiscountInterface[]|null
   */
  public function getDiscounts()
  {
      return $this->_get('discounts');
  }

  /**
   * @param \Magento\SalesRule\Api\Data\RuleDiscountInterface[] $discounts
   * @return $this
   */
  public function setDiscounts($discounts)
  {
      $this->setData('discounts', $discounts);
      return $this;
  }

  /**
   * @return int|null
   */
  public function getCalculationSchemaId()
  {
      return $this->_get('calculation_schema_id');
  }

  /**
   * @param int $calculationSchemaId
   * @return $this
   */
  public function setCalculationSchemaId($calculationSchemaId)
  {
      $this->setData('calculation_schema_id', $calculationSchemaId);
      return $this;
  }
}

FYI: The functions getCalculationSchemaId and setCalculationSchemaId are added by us via ExtensionAttribute functionality.

Edit: Just did a text-compare, they are exactly the same, only the functions getDiscounts and setDiscounts are new, but they appear correctly in the Interface and the concrete class

t-heuser avatar Sep 02 '22 08:09 t-heuser

Pfew, it gets even better. It seems like it's one of those problems that happens sometimes. Just executed it again and now everything works, even 2 times. Afterwards I cleaned the cache and then it failed. Executed it again and then it worked.

Just searched around in the cache files and found the problem: The class ProductExtension gets generated and put into the cache in the following way:

ProductExtension
<?php

namespace Magento\Catalog\Api\Data;

class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements ProductExtensionInterface
{
    /**
     * @return int|null
     */
    public function getCalculationSchemaId()
    {
    }

    /**
     * @param int $calculationSchemaId
     * @return $this
     */
    public function setCalculationSchemaId(int $calculationSchemaId)
    {
    }

    /**
     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
     */
    public function getStockItem()
    {
    }

    /**
     * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
     * @return $this
     */
    public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
    {
    }

    /**
     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
     */
    public function getTestStockItem()
    {
    }

    /**
     * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $testStockItem
     * @return $this
     */
    public function setTestStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $testStockItem)
    {
    }

    /**
     * @return string|null
     */
    public function getTestStockItemQty()
    {
    }

    /**
     * @param string $testStockItemQty
     * @return $this
     */
    public function setTestStockItemQty(string $testStockItemQty)
    {
    }

    /**
     * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
     */
    public function getBundleProductOptions()
    {
    }

    /**
     * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
     * @return $this
     */
    public function setBundleProductOptions(?array $bundleProductOptions)
    {
    }

    /**
     * @return int[]|null
     */
    public function getWebsiteIds()
    {
    }

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds(?array $websiteIds)
    {
    }

    /**
     * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
     */
    public function getCategoryLinks()
    {
    }

    /**
     * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
     * @return $this
     */
    public function setCategoryLinks(?array $categoryLinks)
    {
    }

    /**
     * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
     */
    public function getConfigurableProductOptions()
    {
    }

    /**
     * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[]
     * $configurableProductOptions
     * @return $this
     */
    public function setConfigurableProductOptions(?array $configurableProductOptions)
    {
    }

    /**
     * @return int[]|null
     */
    public function getConfigurableProductLinks()
    {
    }

    /**
     * @param int[] $configurableProductLinks
     * @return $this
     */
    public function setConfigurableProductLinks(?array $configurableProductLinks)
    {
    }

    /**
     * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
     */
    public function getDownloadableProductLinks()
    {
    }

    /**
     * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
     * @return $this
     */
    public function setDownloadableProductLinks(?array $downloadableProductLinks)
    {
    }

    /**
     * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
     */
    public function getDownloadableProductSamples()
    {
    }

    /**
     * @param \Magento\Downloadable\Api\Data\SampleInterface[]
     * $downloadableProductSamples
     * @return $this
     */
    public function setDownloadableProductSamples(?array $downloadableProductSamples)
    {
    }

    /**
     * @return \Magento\SalesRule\Api\Data\RuleDiscountInterface[]|null
     */
    public function getDiscounts()
    {
    }

    /**
     * @param \Magento\SalesRule\Api\Data\RuleDiscountInterface[] $discounts
     * @return $this
     */
    public function setDiscounts(?array $discounts)
    {
    }
}

There you can see the problem:

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds(?array $websiteIds)
    {
    }

t-heuser avatar Sep 02 '22 12:09 t-heuser

I got the ?array syntax from #248. And since @torhoehn reported that patch would fix his issue, I thought everything is fine ;)

Comparing with your code, I feel that sometimes just the array typehint is used and sometimes ?array. This needs some more investigation how to figure out which syntax needs to be used.

shochdoerfer avatar Sep 02 '22 12:09 shochdoerfer

Wouldn't it be an easy fix to also add the type hint to the interface? Then everything should be fine, no? Because at the moment the typehint is not added to the interface, only to the implementation.

t-heuser avatar Sep 02 '22 12:09 t-heuser

Will need to do some testing. The logic for the extension attributes class and interfaces are identical. Not sure what's going on here.

shochdoerfer avatar Sep 02 '22 17:09 shochdoerfer

Thanks for opening that issue @oneserv-heuser. I was running into the same issue again and again and could not figure out, what happens here, as I did not find the mentioned code snippet at all in the code.

norgeindian avatar Sep 14 '22 13:09 norgeindian

I haven't forgotten about this, need to find the time for a more intense debugging session.

shochdoerfer avatar Sep 14 '22 14:09 shochdoerfer

@shochdoerfer , awesome, thanks. Just a hint from my side as well. We're running on 2.4.2-p2. So it's definitely not an issue with a specific Magento version. Hope, that helps you a bit during your debugging.

norgeindian avatar Sep 15 '22 06:09 norgeindian

@oneserv-heuser @norgeindian could you people please test #274 and report back it that works for your use-cases?

@torhoehn I might need to roll back the array extension modification, your code example looked different than reported here. Do you remember which Magento & PHP version you were using? Mind also checking if the fix in #274 works for you?

shochdoerfer avatar Sep 25 '22 18:09 shochdoerfer

@shochdoerfer Unfortunately the fix doesn't work, but atleast it's another error 😀

 Error                                                                      
 -- --------------------------------------------------------------------------- 
     Child process error (exit code 255): PHP Fatal error:  Declaration of      
     Magento\Catalog\Api\Data\ProductExtension::setCalculationSchemaId(int      
     $calculationSchemaId) must be compatible with                              
     Magento\Catalog\Api\Data\ProductExtensionInterface::setCalculationSchemaI  
     d($calculationSchemaId) in                                                 
     /tmp/phpstan/cache/PHPStan/95/c3/95c3cd23c340083ec99b41a2ad173115a2e32d1c  
     .php on line 185                                                           
     Fatal error: Declaration of                                                
     Magento\Catalog\Api\Data\ProductExtension::setCalculationSchemaId(int      
     $calculationSchemaId) must be compatible with                              
     Magento\Catalog\Api\Data\ProductExtensionInterface::setCalculationSchemaI  
     d($calculationSchemaId) in                                                 
     /tmp/phpstan/cache/PHPStan/95/c3/95c3cd23c340083ec99b41a2ad173115a2e32d1c  
     .php on line 185         

The error with the ?array type hint is now gone and this error now seems to be kinda weird as I cant see any difference in the declaration.

This is the file from the cache:

ProductExtension from the cache
<?php

namespace Magento\Catalog\Api\Data;

class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements ProductExtensionInterface
{
    /**
     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
     */
    public function getTestStockItem()
    {
    }

    /**
     * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $testStockItem
     * @return $this
     */
    public function setTestStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $testStockItem)
    {
    }

    /**
     * @return string|null
     */
    public function getTestStockItemQty()
    {
    }

    /**
     * @param string $testStockItemQty
     * @return $this
     */
    public function setTestStockItemQty(string $testStockItemQty)
    {
    }

    /**
     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
     */
    public function getStockItem()
    {
    }

    /**
     * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
     * @return $this
     */
    public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
    {
    }

    /**
     * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
     */
    public function getDownloadableProductLinks()
    {
    }

    /**
     * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
     * @return $this
     */
    public function setDownloadableProductLinks($downloadableProductLinks)
    {
    }

    /**
     * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
     */
    public function getDownloadableProductSamples()
    {
    }

    /**
     * @param \Magento\Downloadable\Api\Data\SampleInterface[]
     * $downloadableProductSamples
     * @return $this
     */
    public function setDownloadableProductSamples($downloadableProductSamples)
    {
    }

    /**
     * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
     */
    public function getBundleProductOptions()
    {
    }

    /**
     * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
     * @return $this
     */
    public function setBundleProductOptions($bundleProductOptions)
    {
    }

    /**
     * @return int[]|null
     */
    public function getWebsiteIds()
    {
    }

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds($websiteIds)
    {
    }

    /**
     * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
     */
    public function getCategoryLinks()
    {
    }

    /**
     * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
     * @return $this
     */
    public function setCategoryLinks($categoryLinks)
    {
    }

    /**
     * @return \Magento\SalesRule\Api\Data\RuleDiscountInterface[]|null
     */
    public function getDiscounts()
    {
    }

    /**
     * @param \Magento\SalesRule\Api\Data\RuleDiscountInterface[] $discounts
     * @return $this
     */
    public function setDiscounts($discounts)
    {
    }

    /**
     * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
     */
    public function getConfigurableProductOptions()
    {
    }

    /**
     * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[]
     * $configurableProductOptions
     * @return $this
     */
    public function setConfigurableProductOptions($configurableProductOptions)
    {
    }

    /**
     * @return int[]|null
     */
    public function getConfigurableProductLinks()
    {
    }

    /**
     * @param int[] $configurableProductLinks
     * @return $this
     */
    public function setConfigurableProductLinks($configurableProductLinks)
    {
    }

    /**
     * @return int|null
     */
    public function getCalculationSchemaId()
    {
    }

    /**
     * @param int $calculationSchemaId
     * @return $this
     */
    public function setCalculationSchemaId(int $calculationSchemaId)
    {
    }
}

Here are the files from generated/code:

ProductExtensionInterface from generated/code
<?php
namespace Magento\Catalog\Api\Data;

/**
 * ExtensionInterface class for @see \Magento\Catalog\Api\Data\ProductInterface
 */
interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
    /**
     * @return int[]|null
     */
    public function getWebsiteIds();

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds($websiteIds);

    /**
     * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
     */
    public function getCategoryLinks();

    /**
     * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
     * @return $this
     */
    public function setCategoryLinks($categoryLinks);

    /**
     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
     */
    public function getStockItem();

    /**
     * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
     * @return $this
     */
    public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem);

    /**
     * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
     */
    public function getBundleProductOptions();

    /**
     * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
     * @return $this
     */
    public function setBundleProductOptions($bundleProductOptions);

    /**
     * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
     */
    public function getDownloadableProductLinks();

    /**
     * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
     * @return $this
     */
    public function setDownloadableProductLinks($downloadableProductLinks);

    /**
     * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
     */
    public function getDownloadableProductSamples();

    /**
     * @param \Magento\Downloadable\Api\Data\SampleInterface[] $downloadableProductSamples
     * @return $this
     */
    public function setDownloadableProductSamples($downloadableProductSamples);

    /**
     * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
     */
    public function getConfigurableProductOptions();

    /**
     * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $configurableProductOptions
     * @return $this
     */
    public function setConfigurableProductOptions($configurableProductOptions);

    /**
     * @return int[]|null
     */
    public function getConfigurableProductLinks();

    /**
     * @param int[] $configurableProductLinks
     * @return $this
     */
    public function setConfigurableProductLinks($configurableProductLinks);

    /**
     * @return \Magento\SalesRule\Api\Data\RuleDiscountInterface[]|null
     */
    public function getDiscounts();

    /**
     * @param \Magento\SalesRule\Api\Data\RuleDiscountInterface[] $discounts
     * @return $this
     */
    public function setDiscounts($discounts);

    /**
     * @return int|null
     */
    public function getCalculationSchemaId();

    /**
     * @param int $calculationSchemaId
     * @return $this
     */
    public function setCalculationSchemaId($calculationSchemaId);
}

ProductExtension from generated/code
<?php
namespace Magento\Catalog\Api\Data;

/**
 * Extension class for @see \Magento\Catalog\Api\Data\ProductInterface
 */
class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements ProductExtensionInterface
{
    /**
     * @return int[]|null
     */
    public function getWebsiteIds()
    {
        return $this->_get('website_ids');
    }

    /**
     * @param int[] $websiteIds
     * @return $this
     */
    public function setWebsiteIds($websiteIds)
    {
        $this->setData('website_ids', $websiteIds);
        return $this;
    }

    /**
     * @return \Magento\Catalog\Api\Data\CategoryLinkInterface[]|null
     */
    public function getCategoryLinks()
    {
        return $this->_get('category_links');
    }

    /**
     * @param \Magento\Catalog\Api\Data\CategoryLinkInterface[] $categoryLinks
     * @return $this
     */
    public function setCategoryLinks($categoryLinks)
    {
        $this->setData('category_links', $categoryLinks);
        return $this;
    }

    /**
     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
     */
    public function getStockItem()
    {
        return $this->_get('stock_item');
    }

    /**
     * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem
     * @return $this
     */
    public function setStockItem(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
    {
        $this->setData('stock_item', $stockItem);
        return $this;
    }

    /**
     * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
     */
    public function getBundleProductOptions()
    {
        return $this->_get('bundle_product_options');
    }

    /**
     * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions
     * @return $this
     */
    public function setBundleProductOptions($bundleProductOptions)
    {
        $this->setData('bundle_product_options', $bundleProductOptions);
        return $this;
    }

    /**
     * @return \Magento\Downloadable\Api\Data\LinkInterface[]|null
     */
    public function getDownloadableProductLinks()
    {
        return $this->_get('downloadable_product_links');
    }

    /**
     * @param \Magento\Downloadable\Api\Data\LinkInterface[] $downloadableProductLinks
     * @return $this
     */
    public function setDownloadableProductLinks($downloadableProductLinks)
    {
        $this->setData('downloadable_product_links', $downloadableProductLinks);
        return $this;
    }

    /**
     * @return \Magento\Downloadable\Api\Data\SampleInterface[]|null
     */
    public function getDownloadableProductSamples()
    {
        return $this->_get('downloadable_product_samples');
    }

    /**
     * @param \Magento\Downloadable\Api\Data\SampleInterface[] $downloadableProductSamples
     * @return $this
     */
    public function setDownloadableProductSamples($downloadableProductSamples)
    {
        $this->setData('downloadable_product_samples', $downloadableProductSamples);
        return $this;
    }

    /**
     * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]|null
     */
    public function getConfigurableProductOptions()
    {
        return $this->_get('configurable_product_options');
    }

    /**
     * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $configurableProductOptions
     * @return $this
     */
    public function setConfigurableProductOptions($configurableProductOptions)
    {
        $this->setData('configurable_product_options', $configurableProductOptions);
        return $this;
    }

    /**
     * @return int[]|null
     */
    public function getConfigurableProductLinks()
    {
        return $this->_get('configurable_product_links');
    }

    /**
     * @param int[] $configurableProductLinks
     * @return $this
     */
    public function setConfigurableProductLinks($configurableProductLinks)
    {
        $this->setData('configurable_product_links', $configurableProductLinks);
        return $this;
    }

    /**
     * @return \Magento\SalesRule\Api\Data\RuleDiscountInterface[]|null
     */
    public function getDiscounts()
    {
        return $this->_get('discounts');
    }

    /**
     * @param \Magento\SalesRule\Api\Data\RuleDiscountInterface[] $discounts
     * @return $this
     */
    public function setDiscounts($discounts)
    {
        $this->setData('discounts', $discounts);
        return $this;
    }

    /**
     * @return int|null
     */
    public function getCalculationSchemaId()
    {
        return $this->_get('calculation_schema_id');
    }

    /**
     * @param int $calculationSchemaId
     * @return $this
     */
    public function setCalculationSchemaId($calculationSchemaId)
    {
        $this->setData('calculation_schema_id', $calculationSchemaId);
        return $this;
    }
}

(Yes I cleared the cache before running phpstan.)

t-heuser avatar Sep 26 '22 06:09 t-heuser

@oneserv-heuser sorry man, not exactly sure what is going on. Are you at mageuc22 by any chance? Maybe we can have a chat or you could even show me your code.

shochdoerfer avatar Sep 28 '22 14:09 shochdoerfer

@shochdoerfer Unfortunately not.

@norgeindian Did #274 fixes the error for you? Maybe it's something very specific in our code. Are you adding any extension attribute to Products?

t-heuser avatar Sep 29 '22 04:09 t-heuser

Unfortunately, the issue is still the same in my case, when trying it with your fix. @oneserv-heuser , yes, indeed, we do. Maybe that's the reason? @shochdoerfer , I can not come this weekend, but @sprankhub will be there and will be happy to show you the code. Do you have any idea, what we could check, to make it easier to debug for you?

norgeindian avatar Sep 29 '22 08:09 norgeindian

@norgeindian @shochdoerfer Yes, the reason is definitly the addition of extension attributes. Just removed that part from our code and everything is working fine again. As I can only reproduce the error with Magento 2.4.5 and not 2.4.4, did they change anything in that part? Not sure about that. And also the generated classes are the same (see my comment). Weird

t-heuser avatar Sep 29 '22 12:09 t-heuser

Getting closer to the real issue. Looks like with #248 I went a bit too far with adding the type hints. Will do some more tests and hopefully come up with a fix soonish.

shochdoerfer avatar Sep 30 '22 15:09 shochdoerfer

@oneserv-heuser @norgeindian I found the source of the issue. I went a bit far when adding type hints to the generated classes. The PR reverted this. To simplify your testing effort, I merged the changes already in dev-master. Could you guys please check if things are working for you again?

@torhoehn could you also check if your issue in regards of the array handling still does work with dev-master?

shochdoerfer avatar Oct 03 '22 07:10 shochdoerfer

@shochdoerfer Everything works fine now, thank you!

t-heuser avatar Oct 04 '22 12:10 t-heuser

I've just published version 0.25.0 of the extension.

shochdoerfer avatar Oct 04 '22 17:10 shochdoerfer

@shochdoerfer , thanks a lot. Indeed, everything seems to work now.

norgeindian avatar Oct 05 '22 08:10 norgeindian

Hello there,

I installed version 0.25.0, but unfortunately still getting similar issue:

 Child process error (exit code 255): PHP Fatal error:  Declaration of      
 Magento\InventoryApi\Api\Data\SourceExtension::setIsPickupLocationActive(  
 boolean $isPickupLocationActive) must be compatible with                   
 Magento\InventoryApi\Api\Data\SourceExtensionInterface::setIsPickupLocati  
 onActive($isPickupLocationActive) in                                       
 /tmp/phpstan/cache/PHPStan/b1/77/b177d8ee07a46464a792cb583eab2bd146ae8f42  
 .php on line 18                                                            
 Fatal error: Declaration of                                                
 Magento\InventoryApi\Api\Data\SourceExtension::setIsPickupLocationActive(  
 boolean $isPickupLocationActive) must be compatible with                   
 Magento\InventoryApi\Api\Data\SourceExtensionInterface::setIsPickupLocati  
 onActive($isPickupLocationActive) in                                       
 /tmp/phpstan/cache/PHPStan/b1/77/b177d8ee07a46464a792cb583eab2bd146ae8f42  
 .php on line 18

FYI: I deleted the phpstan cache before running phpstan.

tszmyt-creatuity avatar Oct 26 '22 09:10 tszmyt-creatuity

@tszmyt apologies for this mess. I realize that the naive approach to simply generate the files based on the XML configuration is not enough. If extension interfaces already exist, we need to inspect them to see if typehints are used in the method declarations or not.

shochdoerfer avatar Oct 26 '22 19:10 shochdoerfer

@tszmyt mind checking if the dev-master version works better now? I fixed a few things in #276. Looks much better now in my local sandbox environment.

shochdoerfer avatar Nov 01 '22 13:11 shochdoerfer

Thanks for the update @shochdoerfer

It fixed the issue for me. After upgrading to dev-master + clearing phpstan cache, error is gone.

tszmyt-creatuity avatar Nov 08 '22 10:11 tszmyt-creatuity

Finally ;) I'll create a new release soonish.

shochdoerfer avatar Nov 08 '22 14:11 shochdoerfer

@tszmyt released the fix as 0.26.0 compatible with PHPStan 1.8 & 0.27.0 compatible with PHPStan 1.9 respectively

shochdoerfer avatar Nov 12 '22 19:11 shochdoerfer