예제 #1
0
 /**
  * Will generate the skeleton code for the passed function definition.
  * Will result in a string resembling the following example:
  *
  *      <FUNCTION_DOCBLOCK>
  *      <FUNCTION_MODIFIERS> function <FUNCTION_NAME>(<FUNCTION_PARAMS>)
  *      {
  *          $dgStartLine = <FUNCTION_START_LINE>;
  *          $dgEndLine = <FUNCTION_END_LINE>;
  *          / DOPPELGAENGER_FUNCTION_BEGIN_PLACEHOLDER <FUNCTION_NAME> /
  *          / DOPPELGAENGER_BEFORE_JOINPOINT <FUNCTION_NAME> /
  *          $dgOngoingContract = \AppserverIo\Doppelgaenger\ContractContext::open();
  *          / DOPPELGAENGER_INVARIANT_PLACEHOLDER /
  *          / DOPPELGAENGER_PRECONDITION_PLACEHOLDER <FUNCTION_NAME> /
  *          / DOPPELGAENGER_OLD_SETUP_PLACEHOLDER <FUNCTION_NAME> /
  *          $dgResult = null;
  *          try {
  *              / DOPPELGAENGER_AROUND_JOINPOINT <FUNCTION_NAME> /
  *
  *          } catch (\Exception $dgThrownExceptionObject) {
  *              / DOPPELGAENGER_AFTERTHROWING_JOINPOINT <FUNCTION_NAME> /
  *
  *              // rethrow the exception
  *              throw $dgThrownExceptionObject;
  *
  *          } finally {
  *              / DOPPELGAENGER_AFTER_JOINPOINT <FUNCTION_NAME> /
  *
  *          }
  *          / DOPPELGAENGER_POSTCONDITION_PLACEHOLDER <FUNCTION_NAME> /
  *          / DOPPELGAENGER_INVARIANT_PLACEHOLDER /
  *          if ($dgOngoingContract) {
  *              \AppserverIo\Doppelgaenger\ContractContext::close();
  *          } / DOPPELGAENGER_AFTERRETURNING_JOINPOINT <FUNCTION_NAME> /
  *
  *          return $dgResult;
  *      }
  *
  * @param boolean            $injectNeeded       Determine if we have to use a try...catch block
  * @param FunctionDefinition $functionDefinition The function definition object
  *
  * @return string
  */
 protected function generateSkeletonCode($injectNeeded, FunctionDefinition $functionDefinition)
 {
     // first of all: the docblock
     $code = '
     ' . $functionDefinition->getDocBlock() . '
     ' . $functionDefinition->getHeader('definition') . '
     {
         ' . ReservedKeywords::START_LINE_VARIABLE . ' = ' . (int) $functionDefinition->getStartLine() . ';
         ' . ReservedKeywords::END_LINE_VARIABLE . ' = ' . (int) $functionDefinition->getEndLine() . ';
         ' . Placeholders::FUNCTION_BEGIN . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ';
     // right after: the "before" join-point
     $code .= Placeholders::BEFORE_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ';
     // open the contract context so we are able to avoid endless recursion
     $code .= ReservedKeywords::CONTRACT_CONTEXT . ' = \\AppserverIo\\Doppelgaenger\\ContractContext::open();
         ';
     // Invariant is not needed in private or static functions.
     // Also make sure that there is none in front of the constructor check
     if ($functionDefinition->getVisibility() !== 'private' && !$functionDefinition->isStatic() && $functionDefinition->getName() !== '__construct') {
         $code .= Placeholders::INVARIANT_CALL_START . '
         ';
     }
     $code .= Placeholders::PRECONDITION . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ' . Placeholders::OLD_SETUP . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ';
     // we will wrap code execution in order to provide a "finally" and "after throwing" placeholder hook.
     // we will also predefine the result as NULL to avoid warnings
     $code .= ReservedKeywords::RESULT . ' = null;
         try {
             ' . Placeholders::AROUND_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
     ';
     // add the second part of the try/catch/finally block
     $code .= '
         } catch (\\Exception ' . ReservedKeywords::THROWN_EXCEPTION_OBJECT . ') {
             ' . Placeholders::AFTERTHROWING_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
             // rethrow the exception
             throw ' . ReservedKeywords::THROWN_EXCEPTION_OBJECT . ';
         } finally {
     ';
     // if we have to inject additional code, we might do so here
     if ($injectNeeded === true) {
         $code .= Placeholders::METHOD_INJECT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE;
     }
     // finish of the block
     $code .= '        ' . Placeholders::AFTER_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         }
     ';
     // now just place all the other placeholder for other filters to come
     $code .= '    ' . Placeholders::POSTCONDITION . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE;
     // Invariant is not needed in private or static functions
     if ($functionDefinition->getVisibility() !== 'private' && !$functionDefinition->isStatic()) {
         $code .= '
         ' . Placeholders::INVARIANT_CALL_END . '
         ';
     }
     // close of the contract context
     $code .= 'if (' . ReservedKeywords::CONTRACT_CONTEXT . ') {
             \\AppserverIo\\Doppelgaenger\\ContractContext::close();
         }
     ';
     // last of all: the "after returning" join-point and the final return from the proxy
     $code .= '    ' . Placeholders::AFTERRETURNING_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         return ' . ReservedKeywords::RESULT . ';
     }
     ';
     return $code;
 }
 /**
  * Will change code to create an entry for the old object state.
  *
  * @param string                                                             $bucketData         Payload of the currently
  *                                                                                       filtered bucket
  * @param \AppserverIo\Doppelgaenger\Entities\Definitions\FunctionDefinition $functionDefinition Currently handled function
  *
  * @throws \AppserverIo\Doppelgaenger\Exceptions\GeneratorException
  *
  * @return boolean
  */
 protected function injectOldCode(&$bucketData, FunctionDefinition &$functionDefinition)
 {
     // Do we even need to do anything?
     if ($functionDefinition->usesOld() !== true) {
         return false;
     }
     // If the function is static it should not use the dgOld keyword as there is no state to the class!
     if ($functionDefinition->isStatic() === true) {
         throw new GeneratorException(sprintf('Cannot clone class state in static method %s.', $functionDefinition->getName()));
     }
     // Still here? Then inject the clone statement to preserve an instance of the object prior to our call.
     $bucketData = str_replace(Placeholders::OLD_SETUP . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE, ReservedKeywords::OLD . ' = clone $this;', $bucketData);
     // Still here? We encountered no error then.
     return true;
 }
예제 #3
0
 /**
  * Will inject invocation code for a given function into a given piece of code.
  * Invocation code will be the instantiation of a \AppserverIo\Doppelgaenger\Entities\MethodInvocation object
  * as a basic representation of the given function
  *
  * @param string             $bucketData         Reference on the current bucket's data
  * @param FunctionDefinition $functionDefinition Definition of the function to inject invocation code into
  * @param array              $callbackChain      Chain of callbacks which is used to recursively chain calls
  *
  * @return boolean
  */
 protected function injectInvocationCode(&$bucketData, FunctionDefinition $functionDefinition, array $callbackChain)
 {
     // start building up the code
     $code = '
         ' . ReservedKeywords::METHOD_INVOCATION_OBJECT . ' = new \\AppserverIo\\Doppelgaenger\\Entities\\MethodInvocation(
         ';
     // add the original method call to the callback chain so it can be integrated, add it and add the context
     if ($functionDefinition->isStatic()) {
         $contextCode = '__CLASS__';
     } else {
         $contextCode = '$this';
     }
     // iterate the callback chain and build up the code but pop the first element as we will invoke it initially
     unset($callbackChain[0]);
     // empty chain? Add the original function at least
     if (empty($callbackChain)) {
         $callbackChain[] = array($functionDefinition->getStructureName(), $functionDefinition->getName());
     }
     $code .= '    array(';
     foreach ($callbackChain as $callback) {
         // do some brushing up for the structure
         $structure = $callback[0];
         if ($structure === $functionDefinition->getStructureName()) {
             $structure = $contextCode;
         }
         // also brush up the function call to direct to the original
         if ($callback[1] === $functionDefinition->getName()) {
             $callback[1] = $functionDefinition->getName() . ReservedKeywords::ORIGINAL_FUNCTION_SUFFIX;
         }
         $code .= 'array(' . $structure . ', \'' . $callback[1] . '\'),';
     }
     $code .= '),
     ';
     // continue with the access modifiers
     $code .= '        ' . $contextCode . ',
             ' . ($functionDefinition->isAbstract() ? 'true' : 'false') . ',
             ' . ($functionDefinition->isFinal() ? 'true' : 'false') . ',
             ' . ($functionDefinition->isStatic() ? 'true' : 'false') . ',
         ';
     // we have to build up manual parameter collection as func_get_args() only returns copies
     // @see http://php.net/manual/en/function.func-get-args.php
     $parametersCode = '    array(';
     foreach ($functionDefinition->getParameterDefinitions() as $parameterDefinition) {
         $name = $parameterDefinition->name;
         $parametersCode .= '\'' . substr($name, 1) . '\' => ' . $name . ',';
     }
     $parametersCode .= ')';
     $code .= '    \'' . $functionDefinition->getName() . '\',
         ' . $parametersCode . ',
             __CLASS__,
             \'' . $functionDefinition->getVisibility() . '\'
         );';
     // Insert the code
     $placeholder = Placeholders::FUNCTION_BEGIN . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE;
     $bucketData = str_replace($placeholder, $placeholder . $code, $bucketData);
     return true;
 }