diff --git a/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php b/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php index 6f859e3353146..810a8c5242da8 100644 --- a/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php +++ b/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php @@ -59,7 +59,9 @@ public function aroundGetExtensionAttributes( $paymentToken = $paymentExtension->getVaultPaymentToken(); if ($paymentToken === null) { $paymentToken = $this->paymentTokenManagement->getByPaymentId($payment->getEntityId()); - $paymentExtension->setVaultPaymentToken($paymentToken); + if ($paymentToken instanceof \Magento\Vault\Api\Data\PaymentTokenInterface) { + $paymentExtension->setVaultPaymentToken($paymentToken); + } $payment->setExtensionAttributes($paymentExtension); } diff --git a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php index bcc3344e2adac..6fc7791c1dbe9 100644 --- a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php +++ b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php @@ -24,6 +24,11 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent */ protected $config; + /** + * @var \Magento\Framework\Reflection\TypeProcessor + */ + protected $typeProcessor; + /** * @var array */ @@ -33,6 +38,7 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent * Initialize dependencies. * * @param \Magento\Framework\Api\ExtensionAttribute\Config $config + * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor, * @param string|null $sourceClassName * @param string|null $resultClassName * @param Io $ioObject @@ -41,6 +47,7 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent */ public function __construct( \Magento\Framework\Api\ExtensionAttribute\Config $config, + \Magento\Framework\Reflection\TypeProcessor $typeProcessor, $sourceClassName = null, $resultClassName = null, Io $ioObject = null, @@ -49,6 +56,7 @@ public function __construct( ) { $sourceClassName .= 'Interface'; $this->config = $config; + $this->typeProcessor = $typeProcessor; parent::__construct( $sourceClassName, $resultClassName, @@ -90,9 +98,15 @@ protected function _getClassMethods() 'body' => "return \$this->_get('{$attributeName}');", 'docblock' => ['tags' => [['name' => 'return', 'description' => $attributeType . '|null']]], ]; + $parameters = ['name' => $propertyName]; + // If the attribute type is a valid type declaration (e.g., interface, class, array) then use it to enforce + // constraints on the generated setter methods + if ($this->typeProcessor->isValidTypeDeclaration($attributeType)) { + $parameters['type'] = $attributeType; + } $methods[] = [ 'name' => $setterName, - 'parameters' => [['name' => $propertyName]], + 'parameters' => [$parameters], 'body' => "\$this->setData('{$attributeName}', \${$propertyName});" . PHP_EOL . "return \$this;", 'docblock' => [ 'tags' => [ diff --git a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceGenerator.php b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceGenerator.php index 3a658da307f88..937a0b5112780 100644 --- a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceGenerator.php +++ b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceGenerator.php @@ -21,6 +21,7 @@ class ExtensionAttributesInterfaceGenerator extends \Magento\Framework\Api\Code\ * Initialize dependencies. * * @param \Magento\Framework\Api\ExtensionAttribute\Config $config + * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor * @param string|null $sourceClassName * @param string|null $resultClassName * @param Io $ioObject @@ -29,6 +30,7 @@ class ExtensionAttributesInterfaceGenerator extends \Magento\Framework\Api\Code\ */ public function __construct( \Magento\Framework\Api\ExtensionAttribute\Config $config, + \Magento\Framework\Reflection\TypeProcessor $typeProcessor, $sourceClassName = null, $resultClassName = null, Io $ioObject = null, @@ -40,6 +42,7 @@ public function __construct( } parent::__construct( $config, + $typeProcessor, $sourceClassName, $resultClassName, $ioObject, diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php index bfdb3a0a2e8ac..bc7443488db72 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php @@ -15,6 +15,11 @@ class ExtensionAttributesGeneratorTest extends \PHPUnit_Framework_TestCase */ protected $configMock; + /** + * @var \Magento\Framework\Reflection\TypeProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $typeProcessorMock; + /** * @var \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator|\PHPUnit_Framework_MockObject_MockObject */ @@ -26,11 +31,17 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->typeProcessorMock = $this->getMockBuilder('Magento\Framework\Reflection\TypeProcessor') + ->disableOriginalConstructor() + ->setMethods(null) + ->getMock(); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->model = $objectManager->getObject( 'Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator', [ 'config' => $this->configMock, + 'typeProcessor' => $this->typeProcessorMock, 'sourceClassName' => '\Magento\Catalog\Api\Data\Product', 'resultClassName' => '\Magento\Catalog\Api\Data\ProductExtension', 'classGenerator' => null @@ -55,6 +66,11 @@ public function testGenerate() Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\OptionInterface[]', Converter::RESOURCE_PERMISSIONS => [], ], + // Ensure type declaration is added to argument of setter + 'complex_object_attribute_with_type_declaration' => [ + Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\BundleOptionInterface', + Converter::RESOURCE_PERMISSIONS => [], + ], ], 'Magento\Catalog\Api\Data\Product' => [ 'should_not_include' => [ diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php index e01121b13881c..2173304760615 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php @@ -29,6 +29,11 @@ public function testGenerate() Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\OptionInterface[]', Converter::RESOURCE_PERMISSIONS => [], ], + // Ensure type declaration is added to argument of setter + 'complex_object_attribute_with_type_declaration' => [ + Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\BundleOptionInterface', + Converter::RESOURCE_PERMISSIONS => [], + ], ], 'Magento\Catalog\Api\Data\Product' => [ 'should_not_include' => [ @@ -38,12 +43,17 @@ public function testGenerate() ], ] ); + $typeProcessorMock = $this->getMockBuilder('Magento\Framework\Reflection\TypeProcessor') + ->disableOriginalConstructor() + ->setMethods(null) + ->getMock(); /** @var \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator $model */ $model = $objectManager->getObject( 'Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator', [ 'config' => $configMock, + 'typeProcessor' => $typeProcessorMock, 'sourceClassName' => '\Magento\Catalog\Api\Data\Product', 'resultClassName' => '\Magento\Catalog\Api\Data\ProductExtensionInterface', 'classGenerator' => null diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt index 0f9838bd8736c..0078deb937e8b 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt @@ -40,4 +40,23 @@ class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject imple $this->setData('complex_object_attribute', $complexObjectAttribute); return $this; } + + /** + * @return \Magento\Bundle\Api\Data\BundleOptionInterface|null + */ + public function getComplexObjectAttributeWithTypeDeclaration() + { + return $this->_get('complex_object_attribute_with_type_declaration'); + } + + /** + * @param \Magento\Bundle\Api\Data\BundleOptionInterface + * $complexObjectAttributeWithTypeDeclaration + * @return $this + */ + public function setComplexObjectAttributeWithTypeDeclaration(\Magento\Bundle\Api\Data\BundleOptionInterface $complexObjectAttributeWithTypeDeclaration) + { + $this->setData('complex_object_attribute_with_type_declaration', $complexObjectAttributeWithTypeDeclaration); + return $this; + } } diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt index 75dde39b21519..4d5a7336a8076 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt @@ -26,4 +26,16 @@ interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttr * @return $this */ public function setComplexObjectAttribute($complexObjectAttribute); + + /** + * @return \Magento\Bundle\Api\Data\BundleOptionInterface|null + */ + public function getComplexObjectAttributeWithTypeDeclaration(); + + /** + * @param \Magento\Bundle\Api\Data\BundleOptionInterface + * $complexObjectAttributeWithTypeDeclaration + * @return $this + */ + public function setComplexObjectAttributeWithTypeDeclaration(\Magento\Bundle\Api\Data\BundleOptionInterface $complexObjectAttributeWithTypeDeclaration); } diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php index 77b746e393297..6708b90525f52 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php @@ -115,6 +115,26 @@ public function testIsArrayType() $this->assertTrue($this->_typeProcessor->isArrayType('string[]')); } + public function testIsValidTypeDeclaration() + { + $this->assertTrue($this->_typeProcessor->isValidTypeDeclaration('Traversable')); // Interface + $this->assertTrue($this->_typeProcessor->isValidTypeDeclaration('stdObj')); // Class + $this->assertTrue($this->_typeProcessor->isValidTypeDeclaration('array')); + $this->assertTrue($this->_typeProcessor->isValidTypeDeclaration('callable')); + $this->assertTrue($this->_typeProcessor->isValidTypeDeclaration('self')); + $this->assertTrue($this->_typeProcessor->isValidTypeDeclaration('self')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('string')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('string[]')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('int')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('float')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('double')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('boolean')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('[]')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('mixed[]')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('stdObj[]')); + $this->assertFalse($this->_typeProcessor->isValidTypeDeclaration('Traversable[]')); + } + public function getArrayItemType() { $this->assertEquals('string', $this->_typeProcessor->getArrayItemType('str[]')); diff --git a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php index a9646443de11e..6dcbe7d992659 100644 --- a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php @@ -14,6 +14,7 @@ * Type processor of config reader properties * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class TypeProcessor { @@ -419,6 +420,18 @@ public function isArrayType($type) return (bool)preg_match('/(\[\]$|^ArrayOf)/', $type); } + /** + * Check if given type is valid to use as an argument type declaration + * + * @see http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration + * @param string $type + * @return bool + */ + public function isValidTypeDeclaration($type) + { + return !($this->isTypeSimple($type) || $this->isTypeAny($type) || $this->isArrayType($type)); + } + /** * Get item type of the array. *