Completed
Push — master ( 7cf1b1...484720 )
by Дмитрий
03:47
created

Context::clearSymbols()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 9.4286
cc 1
eloc 3
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
118
        $this->initGlobals();
119
    }
120
121
    public function clearSymbols()
122
    {
123
        unset($this->symbols);
124
        $this->symbols = array();
125
    }
126
127
    public function dump()
128
    {
129
        var_dump($this->symbols);
130
    }
131
132
    /**
133
     * @param $name
134
     * @return Variable|null
135
     */
136 8
    public function getSymbol($name)
137
    {
138 8
        return isset($this->symbols[$name]) ? $this->symbols[$name] : null;
139
    }
140
141
    /**
142
     * @param string $type
143
     * @param string $message
144
     * @return bool
145
     */
146
    public function warning($type, $message)
147
    {
148
        $filepath = $this->filepath;
149
        $this->output->writeln('<comment>Notice:  ' . $message . " in {$filepath}  [{$type}]</comment>");
150
        $this->output->writeln('');
151
        return true;
152
    }
153
154
    /**
155
     * @param string $type
156
     * @param string $message
157
     * @param \PhpParser\NodeAbstract $expr
158
     * @return bool
159
     */
160
    public function notice($type, $message, \PhpParser\NodeAbstract $expr)
161
    {
162
        $filepath = $this->filepath;
163
        $code = file($filepath);
164
165
        $this->output->writeln('<comment>Notice:  ' . $message . " in {$filepath} on {$expr->getLine()} [{$type}]</comment>");
166
        $this->output->writeln('');
167
168
        if ($this->application->getConfiguration()->valueIsTrue('blame')) {
169
            exec("git blame --show-email -L {$expr->getLine()},{$expr->getLine()} " . $filepath, $result);
170
            if ($result && isset($result[0])) {
171
                $result[0] = trim($result[0]);
172
173
                $this->output->writeln("<comment>\t {$result[0]}</comment>");
174
            }
175
        } else {
176
            $code = trim($code[$expr->getLine()-1]);
177
            $this->output->writeln("<comment>\t {$code} </comment>");
178
        }
179
180
        $this->output->writeln('');
181
182
        unset($code);
183
        return true;
184
    }
185
186
    /**
187
     * @param \PhpParser\Error $exception
188
     * @param string $filepath
189
     * @return bool
190
     */
191
    public function sytaxError(\PhpParser\Error $exception, $filepath)
192
    {
193
        $code = file($filepath);
194
195
        $this->output->writeln('<error>Syntax error:  ' . $exception->getMessage() . " in {$filepath} </error>");
196
        $this->output->writeln('');
197
198
        $code = trim($code[($exception->getStartLine()-2)]);
199
        $this->output->writeln("<comment>\t {$code} </comment>");
200
201
        return true;
202
    }
203
204
    /**
205
     * @return Variable[]
206
     */
207
    public function getSymbols()
208
    {
209
        return $this->symbols;
210
    }
211
212
    /**
213
     * @param AbstractDefinition $scope
214
     */
215 366
    public function setScope(AbstractDefinition $scope = null)
216
    {
217 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...
218 366
    }
219
220
    public function debug($message)
221
    {
222
        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...
223
            $this->output->writeln($message);
224
        }
225
    }
226
227
    /**
228
     * @return string
229
     */
230
    public function getFilepath()
231
    {
232
        return $this->filepath;
233
    }
234
235
    /**
236
     * @param string $filepath
237
     */
238
    public function setFilepath($filepath)
239
    {
240
        $this->filepath = $filepath;
241
    }
242
243
    /**
244
     * @return int|string
245
     */
246
    public function getCurrentBranch()
247
    {
248
        return $this->currentBranch;
249
    }
250
251
    /**
252
     * @param int|string $currentBranch
253
     */
254
    public function setCurrentBranch($currentBranch)
255
    {
256
        $this->currentBranch = $currentBranch;
257
    }
258
}
259