protected function buildConstructor(TypeInfoInterface $type, InstrumentationInterface $instrumentation)
 {
     $methods = $type->getMethods();
     if ($type->hasMethod('__construct')) {
         // Instance creation checker...
         $code = 'if(static::$K2TypeName == __CLASS__) { ';
         $code .= $instrumentation->getInitializationCode();
         $code .= ' }';
         return $instrumentation->appendMethodCode($type->getMethod('__construct'), $code);
     }
     $con = NULL;
     foreach ($type->getSupertypes($instrumentation->getTypeInfoManager(), true) as $supertype) {
         if ($supertype->isClass() && $supertype->hasMethod('__construct')) {
             $con = $supertype->getMethod('__construct');
             if ($con->isAbstract()) {
                 continue;
             }
             if ($con->isFinal()) {
                 throw new \RuntimeException(sprintf('Unable to compile initializer into type with inherited final constructor: %s', $type->getName()));
             }
             break;
         }
     }
     // No inherited constructor found, introduce standard constructor:
     if ($con === NULL) {
         if (empty($methods['__construct'])) {
             $code = 'public function __construct() { ';
             $code .= 'if(static::$K2TypeName == __CLASS__) { ';
             $code .= $instrumentation->getInitializationCode();
             $code .= ' }';
             $code .= '}';
             return $instrumentation->introduceCode($code);
         }
     }
     // Inherited constructor found, enhance it by overloading:
     $code = $instrumentation->buildMethodSignature($con) . ' { ';
     $code .= 'if(func_num_args() <= ' . count($con->getParameters()) . ') { ';
     $code .= 'parent::__construct(';
     foreach ($con->getParameters() as $i => $param) {
         if ($i != 0) {
             $code .= ', ';
         }
         $code .= '$' . $param->getName();
     }
     $code .= '); } else { ';
     $code .= 'forward_static_call_array([\'parent\', __FUNCTION__], func_get_args());';
     $code .= ' } ';
     $code .= 'if(static::$K2TypeName == __CLASS__) { ';
     $code .= $instrumentation->getInitializationCode();
     $code .= ' }';
     $code .= ' }';
     $instrumentation->introduceCode($code);
 }
Exemple #2
0
 protected function interceptMethod(TypeInfoInterface $type, MethodInfoInterface $method, InstrumentationInterface $instrumentation)
 {
     $manager = $instrumentation->getTypeInfoManager();
     $advices = [];
     foreach ($this->getAdvisors(Advisor::TARGET_ELEMENT, $manager) as $advisor) {
         $match = $advisor->matches($manager, $type, $method);
         if ($match !== NULL) {
             $advice = clone $advisor->getAdvice();
             $interceptor = $advice->getInterceptor();
             if ($interceptor instanceof AbstractInterceptor) {
                 $interceptor->setSubject($match->getTarget());
                 $interceptor->setArgs($match->getArgs());
                 $interceptor->setReturns($match->getReturns());
             }
             $advices[] = $advice;
         }
     }
     if (empty($advices)) {
         return;
     }
     $code = '';
     if (!$type->is($method->getDeclaringType())) {
         if ($method->isFinal() || $method->isPrivate()) {
             return;
         }
         $code = $instrumentation->buildMethodSignature($method) . ' { ';
     }
     $code .= 'if(static::class == __CLASS__ && \\KoolKode\\Aop\\Advice::isAdviceEnabled()) { ';
     $code .= 'static $__i; ';
     $code .= 'if($__i === NULL) { $__i = [';
     $sorter = function (Advice $a, Advice $b) {
         $o1 = $a->getOrder();
         $o2 = $b->getOrder();
         if ($o1 === $o2) {
             return 0;
         }
         return $o1 > $o2 ? -1 : 1;
     };
     $merger = function (Advice $advice) use($manager, $method) {
         $interceptor = $advice->getInterceptor();
         if ($interceptor instanceof ContainerInterceptor) {
             return $interceptor->generateMethodInterceptionCode($manager, $method);
         }
         return $interceptor->exportCode();
     };
     $before = [];
     $around = [];
     $after = [];
     foreach ($advices as $advice) {
         if ($advice instanceof BeforeAdvice) {
             $before[] = $advice;
         } elseif ($advice instanceof AroundAdvice) {
             $around[] = $advice;
         } elseif ($advice instanceof AfterAdvice) {
             $after[] = $advice;
         }
     }
     usort($before, $sorter);
     usort($around, $sorter);
     usort($after, function (Advice $a, Advice $b) use($sorter) {
         return $sorter($b, $a);
     });
     $code .= '[' . implode(', ', array_map($merger, $before)) . '], ';
     $code .= '[' . implode(', ', array_map($merger, $after)) . '], ';
     if (empty($around)) {
         $code .= '[]';
     } else {
         $code .= '[1 => ' . implode(', ', array_map($merger, $around)) . ']';
     }
     $code .= ']; } ';
     if ($method->isReturnReference()) {
         $code .= 'unset($__r); $__r = ';
     } else {
         $code .= 'return ';
     }
     $code .= '(new \\KoolKode\\Aop\\MethodInvocation(\\KoolKode\\K2\\Kernel::getContainer(), ';
     $code .= '$this, __CLASS__ , __FUNCTION__, ';
     $code .= '\\func_get_args(), $__i))->proceed();';
     if ($method->isReturnReference()) {
         $code .= ' return $__r;';
     }
     $code .= ' }';
     if (!$type->is($method->getDeclaringType())) {
         if ($method->isReturnReference()) {
             $code .= ' unset($__r); $__r = call_user_func_array([';
             $code .= var_export($method->getDeclaringType()->getName(), true);
             $code .= ', __FUNCTION__], func_get_args()); return $__r;';
         } else {
             $code .= ' return call_user_func_array([';
             $code .= var_export($method->getDeclaringType()->getName(), true);
             $code .= ', __FUNCTION__], func_get_args());';
         }
         $instrumentation->introduceCode($code . ' }');
     } else {
         $instrumentation->prependMethodCode($method, $code);
     }
     return true;
 }