/** * Returns a FunctionDefinition from a token array. * * This method will use a set of other methods to parse a token array and retrieve any * possible information from it. This information will be entered into a FunctionDefinition object. * * @param array $tokens The token array * @param boolean $getRecursive Do we have to get the ancestral conditions as well? * * @return \TechDivision\PBC\Entities\Definitions\FunctionDefinition */ protected function getDefinitionFromTokens(array $tokens, $getRecursive) { // First of all we need a new FunctionDefinition to fill $functionDefinition = new FunctionDefinition(); // For our next step we would like to get the doc comment (if any) $functionDefinition->docBlock = $this->getDocBlock($tokens, T_FUNCTION); // Get the function signature $functionDefinition->isFinal = $this->hasSignatureToken($tokens, T_FINAL, T_FUNCTION); $functionDefinition->isAbstract = $this->hasSignatureToken($tokens, T_ABSTRACT, T_FUNCTION); $functionDefinition->visibility = $this->getFunctionVisibility($tokens); $functionDefinition->isStatic = $this->hasSignatureToken($tokens, T_STATIC, T_FUNCTION); $functionDefinition->name = $this->getFunctionName($tokens); // Lets also get out parameters $functionDefinition->parameterDefinitions = $this->getParameterDefinitionList($tokens); // Do we have a private context here? If so we have to tell the annotation parser $privateContext = false; if ($functionDefinition->getVisibility() === 'private') { $privateContext = true; } // So we got our docBlock, now we can parse the precondition annotations from it $annotationParser = new AnnotationParser($this->file, $this->config, $this->tokens, $this->currentDefinition); $functionDefinition->preconditions = $annotationParser->getConditions($functionDefinition->getDocBlock(), PBC_KEYWORD_PRE, $privateContext); // Does this method require the use of our "old" mechanism? $functionDefinition->usesOld = $this->usesKeyword($functionDefinition->getDocBlock(), PBC_KEYWORD_OLD); // We have to get the body of the function, so we can recreate it $functionDefinition->body = $this->getFunctionBody($tokens); // So we got our docBlock, now we can parse the postcondition annotations from it $functionDefinition->postconditions = $annotationParser->getConditions($functionDefinition->getDocBlock(), PBC_KEYWORD_POST, $privateContext); // If we have to parse the definition in a recursive manner, we have to get the parent invariants if ($getRecursive === true) { // Add all the assertions we might get from ancestral dependencies $this->addAncestralAssertions($functionDefinition); } // All done? Then lock the definition to make it a DTO $functionDefinition->lock(); return $functionDefinition; }
/** * Will generate the code used before the original function body * * @param bool $injectNeeded Determine if we have to use a try...catch block * @param FunctionDefinition $functionDefinition The function definition object * * @return string */ protected function generateBeforeCode($injectNeeded, FunctionDefinition $functionDefinition) { $suffix = PBC_ORIGINAL_FUNCTION_SUFFIX . str_replace('.', '', microtime(true)); $code = PBC_CONTRACT_CONTEXT . ' = \\TechDivision\\PBC\\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->getIsStatic() && $functionDefinition->getName() !== '__construct') { $code .= PBC_INVARIANT_PLACEHOLDER . PBC_PLACEHOLDER_CLOSE; } $code .= PBC_PRECONDITION_PLACEHOLDER . $functionDefinition->getName() . PBC_PLACEHOLDER_CLOSE . PBC_OLD_SETUP_PLACEHOLDER . $functionDefinition->getName() . PBC_PLACEHOLDER_CLOSE; // If we inject something we might need a try ... catch around the original call. if ($injectNeeded === true) { $code .= 'try {'; } // Build up the call to the original function. $code .= PBC_KEYWORD_RESULT . ' = ' . $functionDefinition->getHeader('call', $suffix) . ';'; // Finish the try ... catch and place the inject marker if ($injectNeeded === true) { $code .= '} catch (\\Exception $e) {}' . PBC_METHOD_INJECT_PLACEHOLDER . $functionDefinition->getName() . PBC_PLACEHOLDER_CLOSE; } // No just place all the other placeholder for other filters to come $code .= PBC_POSTCONDITION_PLACEHOLDER . $functionDefinition->getName() . PBC_PLACEHOLDER_CLOSE; // Invariant is not needed in private or static functions if ($functionDefinition->getVisibility() !== 'private' && !$functionDefinition->getIsStatic()) { $code .= PBC_INVARIANT_PLACEHOLDER . PBC_PLACEHOLDER_CLOSE; } $code .= 'if (' . PBC_CONTRACT_CONTEXT . ') {\\TechDivision\\PBC\\ContractContext::close();} return ' . PBC_KEYWORD_RESULT . ';}'; $code .= $functionDefinition->getHeader('definition', $suffix, true) . '{'; return $code; }