1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* \AppserverIo\Doppelgaenger\StreamFilters\PreconditionFilter |
5
|
|
|
* |
6
|
|
|
* NOTICE OF LICENSE |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the Open Software License (OSL 3.0) |
9
|
|
|
* that is available through the world-wide-web at this URL: |
10
|
|
|
* http://opensource.org/licenses/osl-3.0.php |
11
|
|
|
* |
12
|
|
|
* PHP version 5 |
13
|
|
|
* |
14
|
|
|
* @author Bernhard Wick <[email protected]> |
15
|
|
|
* @copyright 2015 TechDivision GmbH - <[email protected]> |
16
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
17
|
|
|
* @link https://github.com/appserver-io/doppelgaenger |
18
|
|
|
* @link http://www.appserver.io/ |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace AppserverIo\Doppelgaenger\StreamFilters; |
22
|
|
|
|
23
|
|
|
use AppserverIo\Doppelgaenger\Entities\Definitions\FunctionDefinition; |
24
|
|
|
use AppserverIo\Doppelgaenger\Entities\Lists\FunctionDefinitionList; |
25
|
|
|
use AppserverIo\Doppelgaenger\Entities\Lists\TypedListList; |
26
|
|
|
use AppserverIo\Doppelgaenger\Dictionaries\Placeholders; |
27
|
|
|
use AppserverIo\Doppelgaenger\Dictionaries\ReservedKeywords; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* This filter will buffer the input stream and add all precondition related information at prepared locations |
31
|
|
|
* (see $dependencies) |
32
|
|
|
* |
33
|
|
|
* @author Bernhard Wick <[email protected]> |
34
|
|
|
* @copyright 2015 TechDivision GmbH - <[email protected]> |
35
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
36
|
|
|
* @link https://github.com/appserver-io/doppelgaenger |
37
|
|
|
* @link http://www.appserver.io/ |
38
|
|
|
*/ |
39
|
|
|
class PreconditionFilter extends AbstractFilter |
40
|
|
|
{ |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @const integer FILTER_ORDER Order number if filters are used as a stack, higher means below others |
44
|
|
|
*/ |
45
|
|
|
const FILTER_ORDER = 1; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var array $dependencies Other filters on which we depend |
49
|
|
|
*/ |
50
|
|
|
protected $dependencies = array('SkeletonFilter'); |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Filter a chunk of data by adding precondition checks |
54
|
|
|
* |
55
|
|
|
* @param string $chunk The data chunk to be filtered |
56
|
|
|
* @param FunctionDefinitionList $functionDefinitions Definition of the structure the chunk belongs to |
57
|
|
|
* |
58
|
|
|
* @return string |
59
|
|
|
*/ |
60
|
|
View Code Duplication |
public function filterChunk($chunk, FunctionDefinitionList $functionDefinitions) |
|
|
|
|
61
|
|
|
{ |
62
|
|
|
// Get the tokens |
63
|
|
|
$tokens = token_get_all($chunk); |
64
|
|
|
|
65
|
|
|
// Go through the tokens and check what we found |
66
|
|
|
$tokensCount = count($tokens); |
67
|
|
|
for ($i = 0; $i < $tokensCount; $i++) { |
68
|
|
|
// Did we find a function? If so check if we know that thing and insert the code of its preconditions. |
69
|
|
|
if (is_array($tokens[$i]) && $tokens[$i][0] === T_FUNCTION && is_array($tokens[$i + 2])) { |
70
|
|
|
// Get the name of the function |
71
|
|
|
$functionName = $tokens[$i + 2][1]; |
72
|
|
|
|
73
|
|
|
// Check if we got the function in our list, if not continue |
74
|
|
|
$functionDefinition = $functionDefinitions->get($functionName); |
75
|
|
|
|
76
|
|
|
if (!$functionDefinition instanceof FunctionDefinition) { |
77
|
|
|
continue; |
78
|
|
|
|
|
|
|
|
79
|
|
|
} else { |
80
|
|
|
// Get the code for the assertions |
81
|
|
|
$code = $this->generateCode($functionDefinition->getAllPreconditions(), $functionName); |
82
|
|
|
|
83
|
|
|
// Insert the code |
84
|
|
|
$chunk = str_replace( |
85
|
|
|
Placeholders::PRECONDITION . $functionDefinition->getName() . |
86
|
|
|
Placeholders::PLACEHOLDER_CLOSE, |
87
|
|
|
$code, |
88
|
|
|
$chunk |
89
|
|
|
); |
90
|
|
|
|
91
|
|
|
// "Destroy" code and function definition |
92
|
|
|
$code = null; |
|
|
|
|
93
|
|
|
$functionDefinition = null; |
|
|
|
|
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
// Tell them how much we already processed, and stuff it back into the output |
99
|
|
|
return $chunk; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Will generate the code needed to enforce made precondition assertions |
104
|
|
|
* |
105
|
|
|
* @param \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $assertionLists List of assertion lists |
106
|
|
|
* @param string $functionName The name of the function for which we create the enforcement code |
107
|
|
|
* |
108
|
|
|
* @return string |
109
|
|
|
*/ |
110
|
|
|
protected function generateCode(TypedListList $assertionLists, $functionName) |
111
|
|
|
{ |
112
|
|
|
// We only use contracting if we're not inside another contract already |
113
|
|
|
$code = '/* BEGIN OF PRECONDITION ENFORCEMENT */ |
114
|
|
|
if (' . ReservedKeywords::CONTRACT_CONTEXT . ') { |
115
|
|
|
' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' = false; |
116
|
|
|
' . ReservedKeywords::FAILURE_VARIABLE . ' = array(); |
117
|
|
|
' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ' = array(); |
118
|
|
|
'; |
119
|
|
|
|
120
|
|
|
// We need a counter to check how much conditions we got |
121
|
|
|
$conditionCounter = 0; |
122
|
|
|
$listIterator = $assertionLists->getIterator(); |
123
|
|
View Code Duplication |
for ($i = 0; $i < $listIterator->count(); $i++) { |
|
|
|
|
124
|
|
|
// Create the inner loop for the different assertions |
125
|
|
|
$assertionIterator = $listIterator->current()->getIterator(); |
126
|
|
|
|
127
|
|
|
// Only act if we got actual entries |
128
|
|
|
if ($assertionIterator->count() === 0) { |
129
|
|
|
// increment the outer loop |
130
|
|
|
$listIterator->next(); |
131
|
|
|
continue; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
// create a wrap around assuring that inherited conditions get or-combined |
135
|
|
|
$code .= ' |
136
|
|
|
if (' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' === false) { |
137
|
|
|
'; |
138
|
|
|
|
139
|
|
|
// iterate through the conditions for this certain instance |
140
|
|
|
for ($j = 0; $j < $assertionIterator->count(); $j++) { |
141
|
|
|
$conditionCounter++; |
142
|
|
|
|
143
|
|
|
// Code to catch failed assertions |
144
|
|
|
$code .= $assertionIterator->current()->toCode(); |
145
|
|
|
$assertionIterator->next(); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
// close the or-combined wrap |
149
|
|
|
$code .= ' if (empty(' . ReservedKeywords::FAILURE_VARIABLE . ') && empty(' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ')) { |
150
|
|
|
' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' = true; |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
'; |
154
|
|
|
|
155
|
|
|
// increment the outer loop |
156
|
|
|
$listIterator->next(); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
// Preconditions need or-ed conditions so we make sure only one condition list gets checked |
160
|
|
|
$code .= 'if (' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' === false){ |
161
|
|
|
' . Placeholders::ENFORCEMENT . $functionName . 'precondition' . Placeholders::PLACEHOLDER_CLOSE . ' |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
/* END OF PRECONDITION ENFORCEMENT */'; |
165
|
|
|
|
166
|
|
|
// If there were no assertions we will just return a comment |
167
|
|
|
if ($conditionCounter === 0) { |
168
|
|
|
return '/* No preconditions for this function/method */'; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return $code; |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.