ExtensionAttributesFactory.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Api;
  7. /**
  8. * Factory class for instantiation of extension attributes objects.
  9. */
  10. class ExtensionAttributesFactory
  11. {
  12. const EXTENSIBLE_INTERFACE_NAME = \Magento\Framework\Api\ExtensibleDataInterface::class;
  13. /**
  14. * Object Manager instance
  15. *
  16. * @var \Magento\Framework\ObjectManagerInterface
  17. */
  18. protected $objectManager;
  19. /**
  20. * Map is used for performance optimization.
  21. *
  22. * @var array
  23. */
  24. private $classInterfaceMap = [];
  25. /**
  26. * Factory constructor
  27. *
  28. * @param \Magento\Framework\ObjectManagerInterface $objectManager
  29. */
  30. public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager)
  31. {
  32. $this->objectManager = $objectManager;
  33. }
  34. /**
  35. * Create extension attributes object, custom for each extensible class.
  36. *
  37. * @param string $extensibleClassName
  38. * @param array $data
  39. * @return \Magento\Framework\Api\ExtensionAttributesInterface
  40. */
  41. public function create($extensibleClassName, $data = [])
  42. {
  43. $interfaceReflection = new \ReflectionClass($this->getExtensibleInterfaceName($extensibleClassName));
  44. $methodReflection = $interfaceReflection->getMethod('getExtensionAttributes');
  45. if ($methodReflection->getDeclaringClass()->getName() === self::EXTENSIBLE_INTERFACE_NAME) {
  46. throw new \LogicException(
  47. "Method 'getExtensionAttributes' must be overridden in the interfaces "
  48. . "which extend '" . self::EXTENSIBLE_INTERFACE_NAME . "'. "
  49. . "Concrete return type should be specified."
  50. );
  51. }
  52. $interfaceName = '\\' . $interfaceReflection->getName();
  53. $extensionClassName = substr($interfaceName, 0, -strlen('Interface')) . 'Extension';
  54. $extensionInterfaceName = $extensionClassName . 'Interface';
  55. /** Ensure that proper return type of getExtensionAttributes() method is specified */
  56. $methodDocBlock = $methodReflection->getDocComment();
  57. $pattern = "/@return\s+" . str_replace('\\', '\\\\', $extensionInterfaceName) . "/";
  58. if (!preg_match($pattern, $methodDocBlock)) {
  59. throw new \LogicException(
  60. "Method 'getExtensionAttributes' must be overridden in the interfaces "
  61. . "which extend '" . self::EXTENSIBLE_INTERFACE_NAME . "'. "
  62. . "Concrete return type must be specified. Please fix :" . $interfaceName
  63. );
  64. }
  65. $extensionFactoryName = $extensionClassName . 'Factory';
  66. $extensionFactory = $this->objectManager->create($extensionFactoryName);
  67. return $extensionFactory->create($data);
  68. }
  69. /**
  70. * Identify concrete extensible interface name based on the class name.
  71. *
  72. * @param string $extensibleClassName
  73. * @return string
  74. */
  75. public function getExtensibleInterfaceName($extensibleClassName)
  76. {
  77. $exceptionMessage = "Class '{$extensibleClassName}' must implement an interface, "
  78. . "which extends from '" . self::EXTENSIBLE_INTERFACE_NAME . "'";
  79. $notExtensibleClassFlag = '';
  80. if (isset($this->classInterfaceMap[$extensibleClassName])) {
  81. if ($notExtensibleClassFlag === $this->classInterfaceMap[$extensibleClassName]) {
  82. throw new \LogicException($exceptionMessage);
  83. } else {
  84. return $this->classInterfaceMap[$extensibleClassName];
  85. }
  86. }
  87. $modelReflection = new \ReflectionClass($extensibleClassName);
  88. if ($modelReflection->isInterface()
  89. && $modelReflection->isSubclassOf(self::EXTENSIBLE_INTERFACE_NAME)
  90. && $modelReflection->hasMethod('getExtensionAttributes')
  91. ) {
  92. $this->classInterfaceMap[$extensibleClassName] = $extensibleClassName;
  93. return $this->classInterfaceMap[$extensibleClassName];
  94. }
  95. foreach ($modelReflection->getInterfaces() as $interfaceReflection) {
  96. if ($interfaceReflection->isSubclassOf(self::EXTENSIBLE_INTERFACE_NAME)
  97. && $interfaceReflection->hasMethod('getExtensionAttributes')
  98. ) {
  99. $this->classInterfaceMap[$extensibleClassName] = $interfaceReflection->getName();
  100. return $this->classInterfaceMap[$extensibleClassName];
  101. }
  102. }
  103. $this->classInterfaceMap[$extensibleClassName] = $notExtensibleClassFlag;
  104. throw new \LogicException($exceptionMessage);
  105. }
  106. }