Completed
Pull Request — master (#40)
by Bernhard
04:17
created

PreconditionFilter::filterChunk()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 41
Code Lines 19

Duplication

Lines 41
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 41
loc 41
rs 8.439
cc 6
eloc 19
nc 4
nop 2
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
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;
0 ignored issues
show
Unused Code introduced by
$code is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
93
                    $functionDefinition = null;
0 ignored issues
show
Unused Code introduced by
$functionDefinition is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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