Completed
Push — master ( a6347e...54c663 )
by Дмитрий
18:15 queued 15:43
created

Context::getCurrentBranch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * @author Patsura Dmitry https://github.com/ovr <[email protected]>
4
 */
5
6
namespace PHPSA;
7
8
use PHPSA\Compiler\GlobalVariable;
9
use PHPSA\Definition\AbstractDefinition;
10
use PHPSA\Definition\ParentDefinition;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
class Context
14
{
15
    /**
16
     * For FunctionDefinition it's null, use scopePointer
17
     *
18
     * @var ParentDefinition|null
19
     */
20
    public $scope;
21
22
    /**
23
     * @var AliasManager
24
     */
25
    public $aliasManager;
26
27
    /**
28
     * @var Application
29
     */
30
    public $application;
31
32
    /**
33
     * @var string|integer
34
     */
35
    public $currentBranch;
36
37
    /**
38
     * @var OutputInterface
39
     */
40
    public $output;
41
42
    /**
43
     * @var Variable[]
44
     */
45
    protected $symbols = array();
46
47
    /**
48
     * @var ScopePointer|null
49
     */
50
    public $scopePointer;
51
52
    /**
53
     * @var string
54
     */
55
    protected $filepath;
56
57
    /**
58
     * Construct our Context with all needed information
59
     *
60
     * @param OutputInterface $output
61
     * @param Application $application
62
     */
63 366
    public function __construct(OutputInterface $output, Application $application)
64
    {
65 366
        $this->output = $output;
66 366
        $this->application = $application;
67
68 366
        $this->initGlobals();
69 366
    }
70
71 366
    public function initGlobals()
72
    {
73
        /**
74
         * http://php.net/manual/language.variables.superglobals.php
75
         */
76 366
        $this->addVariable(new GlobalVariable('GLOBALS', array(), CompiledExpression::ARR));
77 366
        $this->addVariable(new GlobalVariable('_SERVER', array(), CompiledExpression::ARR));
78 366
        $this->addVariable(new GlobalVariable('_GET', array(), CompiledExpression::ARR));
79 366
        $this->addVariable(new GlobalVariable('_POST', array(), CompiledExpression::ARR));
80 366
        $this->addVariable(new GlobalVariable('_FILES', array(), CompiledExpression::ARR));
81 366
        $this->addVariable(new GlobalVariable('_COOKIE', array(), CompiledExpression::ARR));
82 366
        $this->addVariable(new GlobalVariable('_SESSION', array(), CompiledExpression::ARR));
83 366
        $this->addVariable(new GlobalVariable('_REQUEST', array(), CompiledExpression::ARR));
84 366
        $this->addVariable(new GlobalVariable('_ENV', array(), CompiledExpression::ARR));
85 366
    }
86
87
    /**
88
     * @param string $name
89
     * @return Variable
90
     */
91
    public function addSymbol($name)
92
    {
93
        $variable = new Variable($name);
94
        $this->symbols[$name] = $variable;
95
96
        return $variable;
97
    }
98
99
    /**
100
     * @param Variable $variable
101
     * @return bool
102
     */
103 366
    public function addVariable(Variable $variable)
104
    {
105 366
        $this->symbols[$variable->getName()] = $variable;
106
107 366
        return true;
108
    }
109
110
    /**
111
     * Clear prevent context
112
     */
113
    public function clear()
114
    {
115
        $this->symbols = array();
116
        $this->scope = null;
117
        $this->scopePointer = null;
118
        $this->currentBranch = null;
119
        $this->aliasManager = null;
120
121
        $this->initGlobals();
122
    }
123
124
    public function clearSymbols()
125
    {
126
        unset($this->symbols);
127
        $this->symbols = array();
128
    }
129
130
    public function dump()
131
    {
132
        /**
133
         * @expected
134
         */
135
        var_dump($this->symbols);
136
    }
137
138
    /**
139
     * @param $name
140
     * @return Variable|null
141
     */
142 8
    public function getSymbol($name)
143
    {
144 8
        return isset($this->symbols[$name]) ? $this->symbols[$name] : null;
145
    }
146
147
    /**
148
     * @param string $type
149
     * @param string $message
150
     * @return bool
151
     */
152
    public function warning($type, $message)
153
    {
154
        $this->output
155
            ->writeln('<comment>Warning:  ' . $message . ' in ' . $this->filepath . '  [' . $type . ']</comment>');
156
157
        $this->output->writeln('');
158
159
        return true;
160
    }
161
162
    /**
163
     * @param string $type
164
     * @param string $message
165
     * @param \PhpParser\NodeAbstract $expr
166
     * @param int $status
167
     * @return bool
168
     */
169
    public function notice($type, $message, \PhpParser\NodeAbstract $expr, $status = Check::CHECK_SAFE)
0 ignored issues
show
Unused Code introduced by
The parameter $status is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
170
    {
171
        $filepath = $this->filepath;
172
        $code = file($filepath);
173
174
        $this->output->writeln('<comment>Notice:  ' . $message . " in {$filepath} on {$expr->getLine()} [{$type}]</comment>");
175
        $this->output->writeln('');
176
177
        if ($this->application->getConfiguration()->valueIsTrue('blame')) {
178
            exec("git blame --show-email -L {$expr->getLine()},{$expr->getLine()} " . $filepath, $result);
179
            if ($result && isset($result[0])) {
180
                $result[0] = trim($result[0]);
181
182
                $this->output->writeln("<comment>\t {$result[0]}</comment>");
183
            }
184
        } else {
185
            $code = trim($code[$expr->getLine() - 1]);
186
            $this->output->writeln("<comment>\t {$code} </comment>");
187
        }
188
189
        $this->output->writeln('');
190
191
        $this->application->getIssuesCollector()
192
            ->addIssue($type, $message, $filepath, $expr->getLine() - 1);
193
194
        return true;
195
    }
196
197
    /**
198
     * @param \PhpParser\Error $exception
199
     * @param string $filepath
200
     * @return bool
201
     */
202
    public function sytaxError(\PhpParser\Error $exception, $filepath)
203
    {
204
        $code = file($filepath);
205
206
        $this->output->writeln('<error>Syntax error:  ' . $exception->getMessage() . " in {$filepath} </error>");
207
        $this->output->writeln('');
208
209
        $this->application->getIssuesCollector()
210
            ->addIssue('syntax-error', 'syntax-error', $filepath, $exception->getStartLine() - 2);
211
212
        $code = trim($code[($exception->getStartLine()-2)]);
213
        $this->output->writeln("<comment>\t {$code} </comment>");
214
215
        return true;
216
    }
217
218
    /**
219
     * @return Variable[]
220
     */
221
    public function getSymbols()
222
    {
223
        return $this->symbols;
224
    }
225
226
    /**
227
     * @param AbstractDefinition $scope
228
     */
229 366
    public function setScope(AbstractDefinition $scope = null)
230
    {
231 366
        $this->scope = $scope;
0 ignored issues
show
Documentation Bug introduced by
It seems like $scope can also be of type object<PHPSA\Definition\AbstractDefinition>. However, the property $scope is declared as type object<PHPSA\Definition\ParentDefinition>|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
232 366
    }
233
234
    public function debug($message, \PhpParser\Node $expr = null)
235
    {
236
        if ($this->output->isDebug()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method isDebug() does only exist in the following implementations of said interface: Symfony\Component\Console\Output\BufferedOutput, Symfony\Component\Console\Output\ConsoleOutput, Symfony\Component\Console\Output\NullOutput, Symfony\Component\Console\Output\Output, Symfony\Component\Console\Output\StreamOutput, Symfony\Component\Consol...ts\Fixtures\DummyOutput, Symfony\Component\Console\Tests\Output\TestOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
237
            $this->output->writeln('[DEBUG] ' . $message);
238
            $this->output->write($this->filepath);
239
240
            if ($expr) {
241
                $this->output->write(':' . $expr->getLine());
242
            }
243
244
            $this->output->writeln('');
245
        }
246
    }
247
248
    /**
249
     * @return string
250
     */
251
    public function getFilepath()
252
    {
253
        return $this->filepath;
254
    }
255
256
    /**
257
     * @param string $filepath
258
     */
259
    public function setFilepath($filepath)
260
    {
261
        $this->filepath = $filepath;
262
    }
263
264
    /**
265
     * @return int|string
266
     */
267
    public function getCurrentBranch()
268
    {
269
        return $this->currentBranch;
270
    }
271
272
    /**
273
     * @param int|string $currentBranch
274
     */
275
    public function setCurrentBranch($currentBranch)
276
    {
277
        $this->currentBranch = $currentBranch;
278
    }
279
}
280