/** * 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; }
/** * 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; }