Created
August 11, 2016 11:11
-
-
Save AydinHassan/064f4bbd33fc118f9fa655811df6a660 to your computer and use it in GitHub Desktop.
Magento 2 Interceptor with return type hints patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From d5951cd822ae615c8ae56e1c92aae0a921ad611c Mon Sep 17 00:00:00 2001 | |
From: Aydin Hassan <[email protected]> | |
Date: Thu, 11 Aug 2016 13:00:42 +0200 | |
Subject: [PATCH] Patch interceptor generator to add return types, bring in | |
some zend-code 3 features | |
--- | |
Generation/ClassGenerator.php | 37 +++++++++++++ | |
Generation/Interceptor.php | 42 +++++++++++++++ | |
Generation/MethodGenerator.php | 112 ++++++++++++++++++++++++++++++++++++++ | |
Generation/TypeGenerator.php | 118 +++++++++++++++++++++++++++++++++++++++++ | |
etc/di.xml | 7 +++ | |
5 files changed, 316 insertions(+) | |
create mode 100644 Generation/ClassGenerator.php | |
create mode 100644 Generation/Interceptor.php | |
create mode 100644 Generation/MethodGenerator.php | |
create mode 100644 Generation/TypeGenerator.php | |
diff --git a/Generation/ClassGenerator.php b/Generation/ClassGenerator.php | |
new file mode 100644 | |
index 0000000..dd978cf | |
--- /dev/null | |
+++ b/Generation/ClassGenerator.php | |
@@ -0,0 +1,37 @@ | |
+<?php | |
+ | |
+namespace Jh\Toolkit\Generation; | |
+ | |
+use Magento\Framework\Code\Generator\ClassGenerator as MagentoClassGenerator; | |
+ | |
+/** | |
+ * @author Aydin Hassan <[email protected]> | |
+ */ | |
+class ClassGenerator extends MagentoClassGenerator | |
+{ | |
+ /** | |
+ * Possible class method options | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_methodOptions = [ | |
+ 'name' => 'setName', | |
+ 'final' => 'setFinal', | |
+ 'static' => 'setStatic', | |
+ 'abstract' => 'setAbstract', | |
+ 'visibility' => 'setVisibility', | |
+ 'body' => 'setBody', | |
+ 'returnType' => 'setReturnType' | |
+ ]; | |
+ | |
+ | |
+ /** | |
+ * Instantiate method generator object. | |
+ * | |
+ * @return MethodGenerator | |
+ */ | |
+ protected function createMethodGenerator() | |
+ { | |
+ return new MethodGenerator(); | |
+ } | |
+} | |
diff --git a/Generation/Interceptor.php b/Generation/Interceptor.php | |
new file mode 100644 | |
index 0000000..bb3c214 | |
--- /dev/null | |
+++ b/Generation/Interceptor.php | |
@@ -0,0 +1,42 @@ | |
+<?php | |
+ | |
+namespace Jh\Toolkit\Generation; | |
+ | |
+use Magento\Framework\Interception\Code\Generator\Interceptor as MagentoInterceptor; | |
+ | |
+/** | |
+ * @author Aydin Hassan <[email protected]> | |
+ */ | |
+class Interceptor extends MagentoInterceptor | |
+{ | |
+ /** | |
+ * Retrieve method info | |
+ * | |
+ * @param \ReflectionMethod $method | |
+ * @return array | |
+ */ | |
+ protected function _getMethodInfo(\ReflectionMethod $method) | |
+ { | |
+ $parameters = []; | |
+ foreach ($method->getParameters() as $parameter) { | |
+ $parameters[] = $this->_getMethodParameterInfo($parameter); | |
+ } | |
+ | |
+ $methodInfo = [ | |
+ 'name' => $method->getName(), | |
+ 'parameters' => $parameters, | |
+ 'body' => "\$pluginInfo = \$this->pluginList->getNext(\$this->subjectType, '{$method->getName()}');\n" . | |
+ "if (!\$pluginInfo) {\n" . | |
+ " return parent::{$method->getName()}({$this->_getParameterList( | |
+ $parameters | |
+ )});\n" . | |
+ "} else {\n" . | |
+ " return \$this->___callPlugins('{$method->getName()}', func_get_args(), \$pluginInfo);\n" . | |
+ "}", | |
+ 'docblock' => ['shortDescription' => '{@inheritdoc}'], | |
+ 'returnType' => $method->getReturnType() | |
+ ]; | |
+ | |
+ return $methodInfo; | |
+ } | |
+} | |
diff --git a/Generation/MethodGenerator.php b/Generation/MethodGenerator.php | |
new file mode 100644 | |
index 0000000..0fa61ce | |
--- /dev/null | |
+++ b/Generation/MethodGenerator.php | |
@@ -0,0 +1,112 @@ | |
+<?php | |
+ | |
+namespace Jh\Toolkit\Generation; | |
+ | |
+use Zend\Code\Generator\MethodGenerator as ZendMethodGenerator; | |
+ | |
+/** | |
+ * @author Aydin Hassan <[email protected]> | |
+ */ | |
+class MethodGenerator extends ZendMethodGenerator | |
+{ | |
+ /** | |
+ * @var null|TypeGenerator | |
+ */ | |
+ private $returnType; | |
+ | |
+ /** | |
+ * @var bool | |
+ */ | |
+ private $returnsReference = false; | |
+ | |
+ /** | |
+ * @param bool $returnsReference | |
+ * | |
+ * @return MethodGenerator | |
+ */ | |
+ public function setReturnsReference($returnsReference) | |
+ { | |
+ $this->returnsReference = (bool) $returnsReference; | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * @param string|null | |
+ * | |
+ * @return MethodGenerator | |
+ */ | |
+ public function setReturnType($returnType = null) | |
+ { | |
+ $this->returnType = null === $returnType | |
+ ? null | |
+ : TypeGenerator::fromTypeString($returnType); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * @return string | |
+ */ | |
+ /** | |
+ * @return string | |
+ */ | |
+ public function generate() | |
+ { | |
+ $output = ''; | |
+ | |
+ $indent = $this->getIndentation(); | |
+ | |
+ if (($docBlock = $this->getDocBlock()) !== null) { | |
+ $docBlock->setIndentation($indent); | |
+ $output .= $docBlock->generate(); | |
+ } | |
+ | |
+ $output .= $indent; | |
+ | |
+ if ($this->isAbstract()) { | |
+ $output .= 'abstract '; | |
+ } else { | |
+ $output .= (($this->isFinal()) ? 'final ' : ''); | |
+ } | |
+ | |
+ $output .= $this->getVisibility() | |
+ . (($this->isStatic()) ? ' static' : '') | |
+ . ' function ' | |
+ . ($this->returnsReference ? '& ' : '') | |
+ . $this->getName() . '('; | |
+ | |
+ $parameters = $this->getParameters(); | |
+ if (!empty($parameters)) { | |
+ foreach ($parameters as $parameter) { | |
+ $parameterOutput[] = $parameter->generate(); | |
+ } | |
+ | |
+ $output .= implode(', ', $parameterOutput); | |
+ } | |
+ | |
+ $output .= ')'; | |
+ | |
+ if ($this->returnType) { | |
+ $output .= ' : ' . $this->returnType->generate(); | |
+ } | |
+ | |
+ if ($this->isAbstract()) { | |
+ return $output . ';'; | |
+ } | |
+ | |
+// not supporting interfaces | |
+// if ($this->isInterface()) { | |
+// return $output . ';'; | |
+// } | |
+ | |
+ $output .= self::LINE_FEED . $indent . '{' . self::LINE_FEED; | |
+ | |
+ if ($this->body) { | |
+ $output .= preg_replace('#^((?![a-zA-Z0-9_-]+;).+?)$#m', $indent . $indent . '$1', trim($this->body)) | |
+ . self::LINE_FEED; | |
+ } | |
+ | |
+ $output .= $indent . '}' . self::LINE_FEED; | |
+ | |
+ return $output; | |
+ } | |
+} | |
diff --git a/Generation/TypeGenerator.php b/Generation/TypeGenerator.php | |
new file mode 100644 | |
index 0000000..3cf736b | |
--- /dev/null | |
+++ b/Generation/TypeGenerator.php | |
@@ -0,0 +1,118 @@ | |
+<?php | |
+ | |
+namespace Jh\Toolkit\Generation; | |
+ | |
+use Zend\Code\Generator\Exception\InvalidArgumentException; | |
+use Zend\Code\Generator\GeneratorInterface; | |
+ | |
+final class TypeGenerator implements GeneratorInterface | |
+{ | |
+ /** | |
+ * @var bool | |
+ */ | |
+ private $isInternalPhpType; | |
+ | |
+ /** | |
+ * @var string | |
+ */ | |
+ private $type; | |
+ | |
+ /** | |
+ * @var string[] | |
+ * | |
+ * @link http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration | |
+ */ | |
+ private static $internalPhpTypes = ['int', 'float', 'string', 'bool', 'array', 'callable']; | |
+ | |
+ // @codingStandardsIgnoreStart | |
+ /** | |
+ * @var string a regex pattern to match valid class names or types | |
+ */ | |
+ private static $validIdentifierMatcher = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*$/'; | |
+ // @codingStandardsIgnoreEnd | |
+ | |
+ /** | |
+ * @param string $type | |
+ * | |
+ * @return TypeGenerator | |
+ * | |
+ * @throws InvalidArgumentException | |
+ */ | |
+ public static function fromTypeString($type) | |
+ { | |
+ list($wasTrimmed, $trimmedType) = self::trimType($type); | |
+ | |
+ if (! preg_match(self::$validIdentifierMatcher, $trimmedType)) { | |
+ throw new InvalidArgumentException(sprintf( | |
+ 'Provided type "%s" is invalid: must conform "%s"', | |
+ $type, | |
+ self::$validIdentifierMatcher | |
+ )); | |
+ } | |
+ | |
+ $isInternalPhpType = self::isInternalPhpType($trimmedType); | |
+ | |
+ if ($wasTrimmed && $isInternalPhpType) { | |
+ throw new InvalidArgumentException(sprintf( | |
+ 'Provided type "%s" is an internal PHP type, but was provided with a namespace separator prefix', | |
+ $type | |
+ )); | |
+ } | |
+ | |
+ $instance = new self(); | |
+ | |
+ $instance->type = $trimmedType; | |
+ $instance->isInternalPhpType = self::isInternalPhpType($trimmedType); | |
+ | |
+ return $instance; | |
+ } | |
+ | |
+ private function __construct() | |
+ { | |
+ } | |
+ | |
+ /** | |
+ * {@inheritDoc} | |
+ */ | |
+ public function generate() | |
+ { | |
+ if ($this->isInternalPhpType) { | |
+ return strtolower($this->type); | |
+ } | |
+ | |
+ return '\\' . $this->type; | |
+ } | |
+ | |
+ /** | |
+ * @return string the cleaned type string | |
+ */ | |
+ public function __toString() | |
+ { | |
+ return ltrim($this->generate(), '\\'); | |
+ } | |
+ | |
+ /** | |
+ * @param string $type | |
+ * | |
+ * @return bool[]|int[] ordered tuple, first key represents whether the values was trimmed, second is the | |
+ * trimmed string | |
+ */ | |
+ private static function trimType($type) | |
+ { | |
+ if (0 === strpos($type, '\\')) { | |
+ return [true, substr($type, 1)]; | |
+ } | |
+ | |
+ return [false, $type]; | |
+ } | |
+ | |
+ /** | |
+ * @param string $type | |
+ * | |
+ * @return bool | |
+ */ | |
+ private static function isInternalPhpType($type) | |
+ { | |
+ return in_array(strtolower($type), self::$internalPhpTypes, true); | |
+ } | |
+} | |
\ No newline at end of file | |
diff --git a/etc/di.xml b/etc/di.xml | |
index e98b83d..6675a45 100644 | |
--- a/etc/di.xml | |
+++ b/etc/di.xml | |
@@ -7,4 +7,11 @@ | |
</argument> | |
</arguments> | |
</type> | |
+ <type name="Magento\Framework\Interception\Code\Generator\Interceptor"> | |
+ <arguments> | |
+ <argument name="classGenerator" xsi:type="object" shared="false">Jh\Toolkit\Generation\ClassGenerator</argument> | |
+ </arguments> | |
+ </type> | |
+ <preference for="\Magento\Framework\Interception\Code\Generator\Interceptor" | |
+ type="Jh\Toolkit\Generation\Interceptor" /> | |
</config> | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment