/**
  * Will inject enforcement processing for a certain function.
  * Will take default processing code into account and check for custom processing configurations
  *
  * @param string             $bucketData         Payload of the currently filtered bucket
  * @param string             $structureName      The name of the structure for which we create the enforcement code
  * @param string             $structurePath      Path to the file containing the structure
  * @param string             $preconditionCode   Default precondition processing code
  * @param string             $postconditionCode  Default post-condition processing code
  * @param FunctionDefinition $functionDefinition Function definition to create the code for
  *
  * @return null
  */
 protected function injectFunctionEnforcement(&$bucketData, $structureName, $structurePath, $preconditionCode, $postconditionCode, FunctionDefinition $functionDefinition)
 {
     $functionName = $functionDefinition->getName();
     // try to find a local enforcement processing configuration, if we find something we have to
     // create new enforcement code based on that information
     $localType = $this->filterLocalProcessing($functionDefinition->getDocBlock());
     if ($localType !== false) {
         // we found something, make a backup of default enforcement and generate the new code
         $preconditionCode = $this->generateCode($structureName, 'precondition', $localType, $structurePath);
         $postconditionCode = $this->generateCode($structureName, 'postcondition', $localType, $structurePath);
     }
     // Insert the code for the static processing placeholders
     $bucketData = str_replace(array(Placeholders::ENFORCEMENT . $functionName . 'precondition' . Placeholders::PLACEHOLDER_CLOSE, Placeholders::ENFORCEMENT . $functionName . 'postcondition' . Placeholders::PLACEHOLDER_CLOSE), array($preconditionCode, $postconditionCode), $bucketData);
 }
 /**
  * This method will add all assertions any ancestral structures (parent classes, implemented interfaces) might have
  * to the passed class definition.
  *
  * @param \AppserverIo\Doppelgaenger\Entities\Definitions\FunctionDefinition $functionDefinition The function definition
  *                                                                                      we are working on
  *
  * @return void
  */
 protected function addAncestralAssertions(FunctionDefinition $functionDefinition)
 {
     $dependencies = $this->currentDefinition->getDependencies();
     foreach ($dependencies as $dependency) {
         // freshly set the dependency definition to avoid side effects
         $dependencyDefinition = null;
         $fileEntry = $this->structureMap->getEntry($dependency);
         if (!$fileEntry instanceof Structure) {
             // Continue, don't fail as we might have dependencies which are not under Doppelgaenger surveillance
             continue;
         }
         // Get the needed parser
         $structureParserFactory = new StructureParserFactory();
         $parser = $structureParserFactory->getInstance($fileEntry->getType(), $fileEntry->getPath(), $this->config, $this->structureMap, $this->structureDefinitionHierarchy);
         // Get the definition
         $dependencyDefinition = $parser->getDefinition($dependency, true);
         // Get the function definitions of the dependency structure
         $dependencyFunctionDefinitions = $dependencyDefinition->getFunctionDefinitions();
         // If we have a method with the name of the current one we have to get the conditions as ancestrals
         if ($dependencyFunctionDefinitions->entryExists($functionDefinition->getName())) {
             // Get the definition
             $dependencyFunctionDefinition = $dependencyFunctionDefinitions->get($functionDefinition->getName());
             // If the ancestral function uses the old keyword we have to do too
             if ($dependencyFunctionDefinition->usesOld() !== false) {
                 $functionDefinition->setUsesOld(true);
             }
             // Get the conditions
             $functionDefinition->setAncestralPreconditions($dependencyFunctionDefinition->getAllPreconditions(true));
             $functionDefinition->setAncestralPostconditions($dependencyFunctionDefinition->getAllPostconditions(true));
         }
     }
 }
 /**
  * Will generate the code to call the original method logic
  *
  * @param \AppserverIo\Doppelgaenger\Entities\Definitions\FunctionDefinition $functionDefinition The function
  *
  * @return string
  */
 protected function generateCode(FunctionDefinition $functionDefinition)
 {
     // Build up the call to the original function
     return ReservedKeywords::RESULT . ' = ' . $functionDefinition->getHeader('call', ReservedKeywords::ORIGINAL_FUNCTION_SUFFIX, false, true) . ';';
 }
 /**
  * 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;
 }
 /**
  * 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 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;
 }
 /**
  * Used to "straighten out" an expression as some expressions allow for shell regex which makes them hard to
  * generate code from.
  * So with this method a matching pointcut can be altered into having a directly readable expression
  *
  * @param FunctionDefinition|AttributeDefinition $definition Definition to straighten the expression against
  *
  * @return null
  */
 public function straightenExpression($definition)
 {
     // structure name has to be absolute
     $structureName = '\\' . ltrim($definition->getStructureName(), '\\');
     // fix the expression
     $this->expression = str_replace(array($this->callType . $this->function, $this->structure), array($this->callType . $definition->getName(), $structureName), $this->getExpression());
     // set the obvious properties
     $this->function = $definition->getName();
     $this->structure = $structureName;
 }