/** * Will flatten all conditions available at the time of the call. * That means this method will check which conditions make sense in an inheritance context and will drop the * others. * This method MUST be protected/private so it will run through \TechDivision\PBC\Entities\AbstractLockableEntity's * __call() method which will check the lock status before doing anything. * * @return bool */ protected function flattenConditions() { // As our lists only supports unique entries anyway, the only thing left is to check if the condition's // assertions can be fulfilled (would be possible as direct assertions), and flatten the contained // function definitions as well $ancestralConditionIterator = $this->ancestralInvariants->getIterator(); foreach ($ancestralConditionIterator as $conditionList) { $conditionListIterator = $conditionList->getIterator(); foreach ($conditionListIterator as $assertion) { } } // No flatten all the function definitions we got $functionDefinitionIterator = $this->functionDefinitions->getIterator(); foreach ($functionDefinitionIterator as $functionDefinition) { $functionDefinition->flattenConditions(); } return false; }
/** * Will generate the code needed to enforce made invariant assertions * * @param \TechDivision\PBC\Entities\Lists\TypedListList $assertionLists List of assertion lists * * @return string */ private function generateFunctionCode(TypedListList $assertionLists) { $code = 'protected function ' . PBC_CLASS_INVARIANT_NAME . '($callingMethod) {' . PBC_CONTRACT_CONTEXT . ' = \\TechDivision\\PBC\\ContractContext::open();if (' . PBC_CONTRACT_CONTEXT . ') {'; $invariantIterator = $assertionLists->getIterator(); for ($i = 0; $i < $invariantIterator->count(); $i++) { // Create the inner loop for the different assertions if ($invariantIterator->current()->count() !== 0) { $assertionIterator = $invariantIterator->current()->getIterator(); $codeFragment = array(); for ($j = 0; $j < $assertionIterator->count(); $j++) { $codeFragment[] = $assertionIterator->current()->getString(); $assertionIterator->next(); } $code .= 'if (!((' . implode(') && (', $codeFragment) . '))){' . PBC_FAILURE_VARIABLE . ' = \'(' . str_replace('\'', '"', implode(') && (', $codeFragment)) . ')\';' . PBC_PROCESSING_PLACEHOLDER . 'invariant' . PBC_PLACEHOLDER_CLOSE . '}'; } // increment the outer loop $invariantIterator->next(); } $code .= '}\\TechDivision\\PBC\\ContractContext::close();}'; return $code; }
/** * Will generate the code needed to enforce made precondition assertions * * @param \TechDivision\PBC\Entities\Lists\TypedListList $assertionLists List of assertion lists * * @return string */ private function generateCode(TypedListList $assertionLists) { // We only use contracting if we're not inside another contract already $code = '/* BEGIN OF PRECONDITION ENFORCEMENT */ if (' . PBC_CONTRACT_CONTEXT . ') { $passedOne = false;' . PBC_FAILURE_VARIABLE . ' = array();'; // We need a counter to check how much conditions we got $conditionCounter = 0; $listIterator = $assertionLists->getIterator(); for ($i = 0; $i < $listIterator->count(); $i++) { // Create the inner loop for the different assertions $assertionIterator = $listIterator->current()->getIterator(); // Only act if we got actual entries if ($assertionIterator->count() === 0) { // increment the outer loop $listIterator->next(); continue; } $codeFragment = array(); for ($j = 0; $j < $assertionIterator->count(); $j++) { $codeFragment[] = $assertionIterator->current()->getString(); $assertionIterator->next(); } // Preconditions need or-ed conditions so we make sure only one conditionlist gets checked $conditionCounter++; // Code to catch failed assertions $code .= 'if ($passedOne === false && !((' . implode(') && (', $codeFragment) . '))){' . PBC_FAILURE_VARIABLE . '[] = \'(' . str_replace('\'', '"', implode(') && (', $codeFragment)) . ')\'; } else {$passedOne = true;}'; // increment the outer loop $listIterator->next(); } // Preconditions need or-ed conditions so we make sure only one conditionlist gets checked $code .= 'if ($passedOne === false){' . PBC_FAILURE_VARIABLE . ' = implode(" and ", ' . PBC_FAILURE_VARIABLE . ');' . PBC_PROCESSING_PLACEHOLDER . 'precondition' . PBC_PLACEHOLDER_CLOSE . ' }'; // Closing bracket for contract depth check $code .= '} /* END OF PRECONDITION ENFORCEMENT */'; // If there were no assertions we will just return a comment if ($conditionCounter === 0) { return '/* No preconditions for this function/method */'; } return $code; }
/** * Will generate the code needed to enforce made postcondition assertions * * @param \TechDivision\PBC\Entities\Lists\TypedListList $assertionLists List of assertion lists * * @return string */ private function generateCode(TypedListList $assertionLists) { // We only use contracting if we're not inside another contract already $code = '/* BEGIN OF POSTCONDITION ENFORCEMENT */ if (' . PBC_CONTRACT_CONTEXT . ') {'; // We need a counter to check how much conditions we got $conditionCounter = 0; $listIterator = $assertionLists->getIterator(); for ($i = 0; $i < $listIterator->count(); $i++) { // Create the inner loop for the different assertions $assertionIterator = $listIterator->current()->getIterator(); // Only act if we got actual entries if ($assertionIterator->count() === 0) { // increment the outer loop $listIterator->next(); continue; } $codeFragment = array(); for ($j = 0; $j < $assertionIterator->count(); $j++) { $codeFragment[] = $assertionIterator->current()->getString(); // Forward the iterator and tell them we got a condition $assertionIterator->next(); $conditionCounter++; } // Lets insert the condition check (if there have been any) if (!empty($codeFragment)) { $code .= 'if (!((' . implode(') && (', $codeFragment) . '))){' . PBC_FAILURE_VARIABLE . ' = \'(' . str_replace('\'', '"', implode(') && (', $codeFragment)) . ')\';' . PBC_PROCESSING_PLACEHOLDER . 'postcondition' . PBC_PLACEHOLDER_CLOSE . '}'; } // increment the outer loop $listIterator->next(); } // Closing bracket for contract depth check $code .= '}' . '/* END OF POSTCONDITION ENFORCEMENT */'; // Did we get anything at all? If not only give back a comment. if ($conditionCounter === 0) { $code = '/* No postconditions for this function/method */'; } return $code; }
/** * Retrieves class attributes from token array. * * This method will search for any attributes a class might have. Just pass the token array of the class. * Work is done using token definitions and common sense in regards to PHP syntax. * To retrieve the different properties of an attribute it relies on getAttributeProperties(). * We need the list of invariants to mark attributes wo are under surveillance. * * @param array $tokens Array of tokens for this class * @param TypedListList $invariants List of invariants so we can compare the attributes to * * @return AttributeDefinitionList */ protected function getAttributes(array $tokens, TypedListList $invariants = null) { // Check the tokens $attributes = new AttributeDefinitionList(); for ($i = 0; $i < count($tokens); $i++) { // If we got a variable we will check if there is any function definition above it. // If not, we got an attribute, if so we will check if there is an even number of closing and opening // brackets above it, which would mean we are not in the function. if (is_array($tokens[$i]) && $tokens[$i][0] === T_VARIABLE) { for ($j = $i - 1; $j >= 0; $j--) { if (is_array($tokens[$j]) && $tokens[$j][0] === T_FUNCTION) { // Initialize our counter and also the check if we even started counting $bracketCounter = 0; $usedCounter = false; // We got something, lets count the brackets between it and our variable's position for ($k = $j + 1; $k < $i; $k++) { if ($tokens[$k] === '{' || $tokens[$k][0] === T_CURLY_OPEN) { $usedCounter = true; $bracketCounter++; } elseif ($tokens[$k] === '}') { $usedCounter = true; $bracketCounter--; } } // If we got an even number of brackets (the counter is 0 and got used), we got an attribute if ($bracketCounter === 0 && $usedCounter === true) { $attributes->set($tokens[$i][1], $this->getAttributeProperties($tokens, $i)); } break; } elseif (is_array($tokens[$j]) && $tokens[$j][0] === T_CLASS) { // If we reach the class definition without passing a function we definitely got an attribute $attributes->set($tokens[$i][1], $this->getAttributeProperties($tokens, $i)); break; } } } } // If we got invariants we will check if our attributes are used in invariants if ($invariants !== null) { // Lets iterate over all the attributes and check them against the invariants we got $listIterator = $invariants->getIterator(); $listCount = $listIterator->count(); $attributeIterator = $attributes->getIterator(); $attributeCount = $attributeIterator->count(); for ($i = 0; $i < $attributeCount; $i++) { // Do we have any of these attributes in our invariants? $listIterator = $invariants->getIterator(); for ($j = 0; $j < $listCount; $j++) { // Did we get anything useful? if ($listIterator->current() === null) { continue; } $invariantIterator = $listIterator->current()->getIterator(); $invariantCount = $invariantIterator->count(); for ($k = 0; $k < $invariantCount; $k++) { $attributePosition = strpos($invariantIterator->current()->getString(), '$this->' . ltrim($attributeIterator->current()->name, '$')); if ($attributePosition !== false) { // Tell them we were mentioned and persist it $attributeIterator->current()->inInvariant = true; } $invariantIterator->next(); } $listIterator->next(); } $attributeIterator->next(); } } return $attributes; }