Completed
Push — master ( 90ed95...4bbc63 )
by Дмитрий
06:02
created

Context   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 26.97%

Importance

Changes 7
Bugs 0 Features 1
Metric Value
wmc 23
c 7
b 0
f 1
lcom 2
cbo 8
dl 0
loc 256
ccs 24
cts 89
cp 0.2697
rs 10

18 Methods

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