Skip to content

Commit 53709eb

Browse files
author
Franciszek Wawrzak
committed
add creatuity compiled interceptors module
1 parent e0057b6 commit 53709eb

File tree

16 files changed

+1754
-0
lines changed

16 files changed

+1754
-0
lines changed
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Framework\CompiledInterception\Generator;
8+
9+
use Magento\Framework\App\ObjectManager;
10+
use Magento\Framework\Code\Generator\EntityAbstract;
11+
use Magento\Framework\Interception\Code\Generator\Interceptor;
12+
use Magento\Framework\Interception\DefinitionInterface;
13+
14+
class CompiledInterceptor extends Interceptor
15+
{
16+
17+
protected $plugins;
18+
19+
public function __construct(
20+
$sourceClassName = null,
21+
$resultClassName = null,
22+
\Magento\Framework\Code\Generator\Io $ioObject = null,
23+
\Magento\Framework\Code\Generator\CodeGeneratorInterface $classGenerator = null,
24+
\Magento\Framework\Code\Generator\DefinedClasses $definedClasses = null,
25+
$plugins = null
26+
)
27+
{
28+
parent::__construct($sourceClassName,
29+
$resultClassName ,
30+
$ioObject,
31+
$classGenerator,
32+
$definedClasses);
33+
34+
if ($plugins !== null) {
35+
$this->plugins = $plugins;
36+
} else {
37+
$this->plugins = [];
38+
foreach (['primary', 'frontend', 'adminhtml', 'crontab', 'webapi_rest', 'webapi_soap'] as $scope) {
39+
$this->plugins[$scope] = new CompiledPluginList($scope);
40+
}
41+
}
42+
}
43+
44+
public function setInterceptedMethods($interceptedMethods)
45+
{
46+
//DUMMY
47+
}
48+
49+
protected function _getClassProperties()
50+
{
51+
return [];
52+
}
53+
54+
protected function _generateCode()
55+
{
56+
$typeName = $this->getSourceClassName();
57+
$reflection = new \ReflectionClass($typeName);
58+
59+
if ($reflection->isInterface()) {
60+
$this->_classGenerator->setImplementedInterfaces([$typeName]);
61+
} else {
62+
$this->_classGenerator->setExtendedClass($typeName);
63+
}
64+
65+
$this->_classGenerator->addUse(ObjectManager::class);
66+
$this->_classGenerator->addUse(\Magento\Framework\Config\Scope::class);
67+
//return parent::_generateCode();
68+
return EntityAbstract::_generateCode();
69+
}
70+
71+
protected function _getClassMethods()
72+
{
73+
$reflectionClass = new \ReflectionClass($this->getSourceClassName());
74+
$publicMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);
75+
76+
$methods = [];
77+
$allPlugins = [];
78+
foreach ($publicMethods as $method) {
79+
if ($this->isInterceptedMethod($method)) {
80+
$config = $this->_getPluginsConfig($method, $allPlugins);
81+
if (!empty($config)) {
82+
$methods[] = $this->_getCompiledMethodInfo($method, $config);
83+
}
84+
}
85+
}
86+
if (!empty($methods) && !empty($allPlugins)) {
87+
foreach ($allPlugins as $key => $plugins) {
88+
foreach ($plugins as $plugin) {
89+
$methods[] = $this->_getPluginGetterInfo($plugin);
90+
}
91+
}
92+
}
93+
94+
return $methods;
95+
}
96+
97+
private function addCodeSubBlock(&$body, $sub, $indent = 1)
98+
{
99+
foreach ($sub as $line) {
100+
$body[] = str_repeat("\t", $indent) . $line;
101+
}
102+
}
103+
104+
/**
105+
* @param \ReflectionMethod $method
106+
* @param $conf
107+
* @param $parameters
108+
* @return array
109+
*/
110+
protected function _getMethodSourceFromConfig(\ReflectionMethod $method, $conf, $parameters, $returnVoid)
111+
{
112+
$body = [];
113+
$first = true;
114+
$capName = ucfirst($method->getName());
115+
$extraParams = empty($parameters) ? '' : (', ' . $this->_getParameterList($parameters));
116+
117+
if (isset($conf[DefinitionInterface::LISTENER_BEFORE])) {
118+
foreach ($conf[DefinitionInterface::LISTENER_BEFORE] as $plugin) {
119+
if ($first) $first = false; else $body[] = "";
120+
//$body[] = "/** @var \\" . "{$plugin['class']} \$plugin {$plugin['code']} */";
121+
//$body[] = "\$plugin = \$this->" . $this->getGetterName($plugin) . "();";
122+
123+
$call = "\$this->" . $this->getGetterName($plugin) . "()->before$capName(\$this$extraParams);";
124+
125+
if (!empty($parameters)) {
126+
$body[] = "\$beforeResult = " . $call;
127+
$body[] = "if (\$beforeResult !== null) list({$this->_getParameterList($parameters)}) = (array)\$beforeResult;";
128+
} else {
129+
$body[] = $call;
130+
}
131+
}
132+
}
133+
134+
135+
$chain = [];
136+
$main = [];
137+
if (isset($conf[DefinitionInterface::LISTENER_AROUND])) {
138+
$plugin = $conf[DefinitionInterface::LISTENER_AROUND];
139+
//$body[] = "/** @var \\" . "{$plugin['class']} \$plugin {$plugin['code']} */";
140+
//$body[] = "\$plugin = \$this->" . $this->getGetterName($plugin) . "();";
141+
$main[] = "\$this->" . $this->getGetterName($plugin) . "()->around$capName(\$this, function({$this->_getParameterListForNextCallback($parameters)}){";
142+
$this->addCodeSubBlock($main, $this->_getMethodSourceFromConfig($method, $plugin['next'] ?: [], $parameters, $returnVoid));
143+
//$body[] = "\treturn \$result;";
144+
$main[] = "}$extraParams);";
145+
} else {
146+
$main[] = "parent::{$method->getName()}({$this->_getParameterList($parameters)});";
147+
}
148+
$chain[] = $main;
149+
150+
if (isset($conf[DefinitionInterface::LISTENER_AFTER])) {
151+
foreach ($conf[DefinitionInterface::LISTENER_AFTER] as $plugin) {
152+
//$body[] = "/** @var \\" . "{$plugin['class']} \$plugin {$plugin['code']} */";
153+
//$body[] = "\$plugin = \$this->" . $this->getGetterName($plugin) . "();";
154+
if ($returnVoid) {
155+
$chain[] = ["((\$tmp = \$this->" . $this->getGetterName($plugin) . "()->after$capName(\$this, \$result$extraParams)) !== null) ? \$tmp : \$result;"];
156+
} else {
157+
$chain[] = ["\$this->" . $this->getGetterName($plugin) . "()->after$capName(\$this, \$result$extraParams);"];
158+
}
159+
}
160+
}
161+
foreach ($chain as $lp => $piece) {
162+
//if ($first) $first = false; else $body[] = "";
163+
if (!$returnVoid) {
164+
$piece[0] = (($lp + 1 == count($chain)) ? "return " : "\$result = ") . $piece[0];
165+
}
166+
foreach ($piece as $line) {
167+
$body[] = $line;
168+
}
169+
}
170+
171+
return $body;
172+
}
173+
174+
/**
175+
* @param \ReflectionParameter[] $parameters
176+
* @return string
177+
*/
178+
protected function _getParameterListForNextCallback(array $parameters)
179+
{
180+
$ret = [];
181+
foreach ($parameters as $parameter) {
182+
$ret [] =
183+
($parameter->isPassedByReference() ? '&' : '') .
184+
"\${$parameter->getName()}" .
185+
($parameter->isDefaultValueAvailable() ?
186+
' = ' . ($parameter->isDefaultValueConstant() ?
187+
$parameter->getDefaultValueConstantName() :
188+
str_replace("\n", '', var_export($parameter->getDefaultValue(), true))) :
189+
'');
190+
}
191+
return implode(', ', $ret);
192+
}
193+
194+
/**
195+
* @param \ReflectionParameter[] $parameters
196+
* @return string
197+
*/
198+
protected function _getParameterList(array $parameters)
199+
{
200+
$ret = [];
201+
foreach ($parameters as $parameter) {
202+
$ret [] = "\${$parameter->getName()}";
203+
}
204+
return implode(', ', $ret);
205+
}
206+
207+
protected function getGetterName($plugin)
208+
{
209+
return '_get_plugin_' . preg_replace("/[^A-Za-z0-9_]/", '_', $plugin['code'] . $plugin['suffix']);
210+
}
211+
212+
protected function _getPluginGetterInfo($plugin)
213+
{
214+
$body = [];
215+
216+
$body[] = "static \$cache = null;";
217+
$body[] = "if (\$cache === null) \$cache = ObjectManager::getInstance()->get(\\" . "{$plugin['class']}::class);";
218+
$body[] = "return \$cache;";
219+
220+
return [
221+
'name' => $this->getGetterName($plugin),
222+
'parameters' => [],
223+
'body' => implode("\n", $body),
224+
//'returnType' => $class,
225+
'docblock' => [
226+
'shortDescription' => 'plugin "' . $plugin['code'] . '"' . "\n" . '@return \\' . $plugin['class']
227+
],
228+
];
229+
}
230+
231+
protected function _getCompiledMethodInfo(\ReflectionMethod $method, $config)
232+
{
233+
$parameters = $method->getParameters();
234+
$returnsVoid = ($method->hasReturnType() && $method->getReturnType()->getName() == 'void');
235+
236+
$body = [
237+
'switch(ObjectManager::getInstance()->get(Scope::class)->getCurrentScope()){'
238+
];
239+
240+
$cases = [];
241+
//group cases by config
242+
foreach ($config as $scope => $conf) {
243+
$key = md5(serialize($conf));
244+
if (!isset($cases[$key])) $cases[$key] = ['cases'=>[], 'conf'=>$conf];
245+
$cases[$key]['cases'][] = "\tcase '$scope':";
246+
}
247+
//call parent method for scopes with no plugins (or when no scope is set)
248+
$cases[] = ['cases'=>["\tdefault:"], 'conf'=>[]];
249+
250+
foreach($cases as $case) {
251+
$body = array_merge($body, $case['cases']);
252+
$this->addCodeSubBlock($body, $this->_getMethodSourceFromConfig($method, $case['conf'], $parameters, $returnsVoid), 2);
253+
//$body[] = "\t\tbreak;";
254+
}
255+
256+
$body[] = "}";
257+
258+
return [
259+
'name' => ($method->returnsReference() ? '& ' : '') . $method->getName(),
260+
'parameters' =>array_map(array($this, '_getMethodParameterInfo'), $parameters),
261+
'body' => implode("\n", $body),
262+
'returnType' => $method->getReturnType(),
263+
'docblock' => ['shortDescription' => '{@inheritdoc}'],
264+
];
265+
}
266+
267+
protected function _getPluginInfo(CompiledPluginList $plugins, $code, $className, &$allPlugins, $next = null)
268+
{
269+
$className = $plugins->getPluginType($className, $code);
270+
if (!isset($allPlugins[$code])) $allPlugins[$code] = [];
271+
if (empty($allPlugins[$code][$className])) {
272+
$allPlugins[$code][$className] = [
273+
'code' => $code,
274+
'class' => $className,
275+
'suffix' => count($allPlugins[$code]) ? count($allPlugins[$code]) + 1 : ''
276+
];
277+
}
278+
$ret = $allPlugins[$code][$className];
279+
$ret['next'] = $next;
280+
return $ret;
281+
282+
}
283+
284+
protected function _getPluginsChain(CompiledPluginList $plugins, $className, $method, &$allPlugins, $next = '__self')
285+
{
286+
$ret = $plugins->getNext($className, $method, $next);
287+
if(!empty($ret[DefinitionInterface::LISTENER_BEFORE])) {
288+
foreach ($ret[DefinitionInterface::LISTENER_BEFORE] as $k => $code) {
289+
$ret[DefinitionInterface::LISTENER_BEFORE][$k] = $this->_getPluginInfo($plugins, $code, $className, $allPlugins);
290+
}
291+
}
292+
if(!empty($ret[DefinitionInterface::LISTENER_AFTER])) {
293+
foreach ($ret[DefinitionInterface::LISTENER_AFTER] as $k => $code) {
294+
$ret[DefinitionInterface::LISTENER_AFTER][$k] = $this->_getPluginInfo($plugins, $code, $className, $allPlugins);
295+
}
296+
}
297+
if (isset($ret[DefinitionInterface::LISTENER_AROUND])) {
298+
$ret[DefinitionInterface::LISTENER_AROUND] = $this->_getPluginInfo($plugins, $ret[DefinitionInterface::LISTENER_AROUND], $className, $allPlugins,
299+
$this->_getPluginsChain($plugins, $className, $method, $allPlugins, $ret[DefinitionInterface::LISTENER_AROUND]));
300+
}
301+
return $ret;
302+
}
303+
304+
protected function _getPluginsConfig(\ReflectionMethod $method, &$allPlugins)
305+
{
306+
$className = ltrim($this->getSourceClassName(), '\\');
307+
308+
$ret = array();
309+
foreach ($this->plugins as $scope => $pluginsList) {
310+
$p = $this->_getPluginsChain($pluginsList, $className, $method->getName(), $allPlugins);
311+
if ($p) {
312+
$ret[$scope] = $p;
313+
}
314+
315+
}
316+
return $ret;
317+
}
318+
319+
}

0 commit comments

Comments
 (0)