123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\Interception\Code;
- use Magento\Framework\Exception\ValidatorException;
- use Magento\Framework\Phrase;
- class InterfaceValidator
- {
- const METHOD_BEFORE = 'before';
- const METHOD_AROUND = 'around';
- const METHOD_AFTER = 'after';
- /**
- * Arguments reader model
- *
- * @var \Magento\Framework\Code\Reader\ArgumentsReader
- */
- protected $_argumentsReader;
- /**
- * @param \Magento\Framework\Code\Reader\ArgumentsReader $argumentsReader
- */
- public function __construct(\Magento\Framework\Code\Reader\ArgumentsReader $argumentsReader = null)
- {
- $this->_argumentsReader = $argumentsReader ?: new \Magento\Framework\Code\Reader\ArgumentsReader();
- }
- /**
- * Validate plugin interface
- *
- * @param string $pluginClass
- * @param string $interceptedType
- *
- * @return void
- * @throws ValidatorException
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.UnusedLocalVariable)
- */
- public function validate($pluginClass, $interceptedType)
- {
- $interceptedType = '\\' . trim($interceptedType, '\\');
- $pluginClass = '\\' . trim($pluginClass, '\\');
- $plugin = new \ReflectionClass($pluginClass);
- $type = new \ReflectionClass($interceptedType);
- foreach ($plugin->getMethods(\ReflectionMethod::IS_PUBLIC) as $pluginMethod) {
- /** @var $pluginMethod \ReflectionMethod */
- $originMethodName = $this->getOriginMethodName($pluginMethod->getName());
- if ($originMethodName === null) {
- continue;
- }
- if (!$type->hasMethod($originMethodName)) {
- throw new ValidatorException(
- new Phrase(
- 'Incorrect interface in %1. There is no method [ %2 ] in %3 interface',
- [$pluginClass, $originMethodName, $interceptedType]
- )
- );
- }
- $originMethod = $type->getMethod($originMethodName);
- $pluginMethodParameters = $this->getMethodParameters($pluginMethod);
- $originMethodParameters = $this->getMethodParameters($originMethod);
- $methodType = $this->getMethodType($pluginMethod->getName());
- $subject = array_shift($pluginMethodParameters);
- if (!$this->_argumentsReader->isCompatibleType(
- $subject['type'],
- $interceptedType
- ) || $subject['type'] === null
- ) {
- throw new ValidatorException(
- new Phrase(
- 'Invalid [%1] $%2 type in %3::%4. It must be compatible with %5',
- [$subject['type'], $subject['name'], $pluginClass, $pluginMethod->getName(), $interceptedType]
- )
- );
- }
- switch ($methodType) {
- case self::METHOD_BEFORE:
- $this->validateMethodsParameters(
- $pluginMethodParameters,
- $originMethodParameters,
- $pluginClass,
- $pluginMethod->getName()
- );
- break;
- case self::METHOD_AROUND:
- $proceed = array_shift($pluginMethodParameters);
- if (!$this->_argumentsReader->isCompatibleType($proceed['type'], '\\Closure')) {
- throw new ValidatorException(
- new Phrase(
- 'Invalid [%1] $%2 type in %3::%4. It must be compatible with \\Closure',
- [$proceed['type'], $proceed['name'], $pluginClass, $pluginMethod->getName()]
- )
- );
- }
- $this->validateMethodsParameters(
- $pluginMethodParameters,
- $originMethodParameters,
- $pluginClass,
- $pluginMethod->getName()
- );
- break;
- case self::METHOD_AFTER:
- if (count($pluginMethodParameters) > 1) {
- // remove result
- array_shift($pluginMethodParameters);
- $matchedParameters = array_intersect_key($originMethodParameters, $pluginMethodParameters);
- $this->validateMethodsParameters(
- $pluginMethodParameters,
- $matchedParameters,
- $pluginClass,
- $pluginMethod->getName()
- );
- }
- break;
- }
- }
- }
- /**
- * Validate methods parameters compatibility
- *
- * @param array $pluginParameters
- * @param array $originParameters
- * @param string $class
- * @param string $method
- *
- * @return void
- * @throws ValidatorException
- */
- protected function validateMethodsParameters(array $pluginParameters, array $originParameters, $class, $method)
- {
- if (count($pluginParameters) != count($originParameters)) {
- throw new ValidatorException(
- new Phrase(
- 'Invalid method signature. Invalid method parameters count in %1::%2',
- [$class, $method]
- )
- );
- }
- foreach ($pluginParameters as $position => $data) {
- if (!$this->_argumentsReader->isCompatibleType($data['type'], $originParameters[$position]['type'])) {
- throw new ValidatorException(
- new Phrase(
- 'Incompatible parameter type [%1 $%2] in %3::%4. It must be compatible with %5',
- [$data['type'], $data['name'], $class, $method, $originParameters[$position]['type']]
- )
- );
- }
- }
- }
- /**
- * Get parameters type
- *
- * @param \ReflectionParameter $parameter
- *
- * @return string
- */
- protected function getParametersType(\ReflectionParameter $parameter)
- {
- $parameterClass = $parameter->getClass();
- $type = $parameterClass ? '\\' . $parameterClass->getName() : ($parameter->isArray() ? 'array' : null);
- return $type;
- }
- /**
- * Get intercepted method name
- *
- * @param string $pluginMethodName
- *
- * @return string|null
- */
- protected function getOriginMethodName($pluginMethodName)
- {
- switch ($this->getMethodType($pluginMethodName)) {
- case self::METHOD_BEFORE:
- case self::METHOD_AROUND:
- return lcfirst(substr($pluginMethodName, 6));
- case self::METHOD_AFTER:
- return lcfirst(substr($pluginMethodName, 5));
- default:
- return null;
- }
- }
- /**
- * Get method type
- *
- * @param string $pluginMethodName
- *
- * @return null|string
- */
- protected function getMethodType($pluginMethodName)
- {
- if (substr($pluginMethodName, 0, 6) == self::METHOD_BEFORE) {
- return self::METHOD_BEFORE;
- } elseif (substr($pluginMethodName, 0, 6) == self::METHOD_AROUND) {
- return self::METHOD_AROUND;
- } elseif (substr($pluginMethodName, 0, 5) == self::METHOD_AFTER) {
- return self::METHOD_AFTER;
- }
- return null;
- }
- /**
- * Get method parameters
- *
- * @param \ReflectionMethod $method
- *
- * @return array
- */
- protected function getMethodParameters(\ReflectionMethod $method)
- {
- $output = [];
- foreach ($method->getParameters() as $parameter) {
- $output[$parameter->getPosition()] = [
- 'name' => $parameter->getName(),
- 'type' => $this->getParametersType($parameter),
- ];
- }
- return $output;
- }
- }
|