ConstructorIntegrity.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php
  2. /**
  3. * Class constructor validator. Validates call of parent construct
  4. *
  5. * Copyright © Magento, Inc. All rights reserved.
  6. * See COPYING.txt for license details.
  7. */
  8. namespace Magento\Framework\Code\Validator;
  9. use Magento\Framework\Code\ValidatorInterface;
  10. use Magento\Framework\Phrase;
  11. class ConstructorIntegrity implements ValidatorInterface
  12. {
  13. /**
  14. * @var \Magento\Framework\Code\Reader\ArgumentsReader
  15. */
  16. protected $_argumentsReader;
  17. /**
  18. * @param \Magento\Framework\Code\Reader\ArgumentsReader $argumentsReader
  19. */
  20. public function __construct(\Magento\Framework\Code\Reader\ArgumentsReader $argumentsReader = null)
  21. {
  22. $this->_argumentsReader = $argumentsReader ?: new \Magento\Framework\Code\Reader\ArgumentsReader();
  23. }
  24. /**
  25. * Validate class
  26. *
  27. * @param string $className
  28. * @return bool
  29. * @throws \Magento\Framework\Exception\ValidatorException
  30. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  31. * @SuppressWarnings(PHPMD.NPathComplexity)
  32. */
  33. public function validate($className)
  34. {
  35. $class = new \ReflectionClass($className);
  36. $parent = $class->getParentClass();
  37. /** Check whether parent class exists and has __construct method */
  38. if (!$parent) {
  39. return true;
  40. }
  41. /** Get parent class __construct arguments */
  42. $parentArguments = $this->_argumentsReader->getConstructorArguments($parent, true, true);
  43. if (empty($parentArguments)) {
  44. return true;
  45. }
  46. /** Check whether class has __construct */
  47. $classArguments = $this->_argumentsReader->getConstructorArguments($class);
  48. if (null === $classArguments) {
  49. return true;
  50. }
  51. /** Check whether class has parent::__construct call */
  52. $callArguments = $this->_argumentsReader->getParentCall($class, $classArguments);
  53. if (null === $callArguments) {
  54. return true;
  55. }
  56. /** Get parent class __construct arguments */
  57. $parentArguments = $this->_argumentsReader->getConstructorArguments($parent, true, true);
  58. foreach ($parentArguments as $index => $requiredArgument) {
  59. if (isset($callArguments[$index])) {
  60. $actualArgument = $callArguments[$index];
  61. } else {
  62. if ($requiredArgument['isOptional']) {
  63. continue;
  64. }
  65. $classPath = str_replace('\\', '/', $class->getFileName());
  66. throw new \Magento\Framework\Exception\ValidatorException(
  67. new Phrase(
  68. 'Missed required argument %1 in parent::__construct call. File: %2',
  69. [$requiredArgument['name'], $classPath]
  70. )
  71. );
  72. }
  73. $isCompatibleTypes = $this->_argumentsReader->isCompatibleType(
  74. $requiredArgument['type'],
  75. $actualArgument['type']
  76. );
  77. if (false == $isCompatibleTypes) {
  78. $classPath = str_replace('\\', '/', $class->getFileName());
  79. throw new \Magento\Framework\Exception\ValidatorException(
  80. new Phrase(
  81. 'Incompatible argument type: Required type: %1. Actual type: %2; File: %3%4%5',
  82. [$requiredArgument['type'], $actualArgument['type'], PHP_EOL, $classPath, PHP_EOL]
  83. )
  84. );
  85. }
  86. }
  87. /**
  88. * Try to detect unused arguments
  89. * Check whether count of passed parameters less or equal that count of count parent class arguments
  90. */
  91. if (count($callArguments) > count($parentArguments)) {
  92. $extraParameters = array_slice($callArguments, count($parentArguments));
  93. $names = [];
  94. foreach ($extraParameters as $param) {
  95. $names[] = '$' . $param['name'];
  96. }
  97. $classPath = str_replace('\\', '/', $class->getFileName());
  98. throw new \Magento\Framework\Exception\ValidatorException(
  99. new Phrase(
  100. 'Extra parameters passed to parent construct: %1. File: %2',
  101. [implode(', ', $names), $classPath]
  102. )
  103. );
  104. }
  105. return true;
  106. }
  107. }