Completed
Push — master ( ab0655...5df8b2 )
by Дмитрий
04:36 queued 11s
created

Context::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4286
cc 1
eloc 4
nc 1
nop 2
crap 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
        return true;
155
    }
156
157
    /**
158
     * @param string $type
159
     * @param string $message
160
     * @param \PhpParser\NodeAbstract $expr
161
     * @return bool
162
     */
163
    public function notice($type, $message, \PhpParser\NodeAbstract $expr)
164
    {
165
        $filepath = $this->filepath;
166
        $code = file($filepath);
167
168
        $this->output->writeln('<comment>Notice:  ' . $message . " in {$filepath} on {$expr->getLine()} [{$type}]</comment>");
169
        $this->output->writeln('');
170
171
        if ($this->application->getConfiguration()->valueIsTrue('blame')) {
172
            exec("git blame --show-email -L {$expr->getLine()},{$expr->getLine()} " . $filepath, $result);
173
            if ($result && isset($result[0])) {
174
                $result[0] = trim($result[0]);
175
176
                $this->output->writeln("<comment>\t {$result[0]}</comment>");
177
            }
178
        } else {
179
            $code = trim($code[$expr->getLine()-1]);
180
            $this->output->writeln("<comment>\t {$code} </comment>");
181
        }
182
183
        $this->output->writeln('');
184
185
        unset($code);
186
        return true;
187
    }
188
189
    /**
190
     * @param \PhpParser\Error $exception
191
     * @param string $filepath
192
     * @return bool
193
     */
194
    public function sytaxError(\PhpParser\Error $exception, $filepath)
195
    {
196
        $code = file($filepath);
197
198
        $this->output->writeln('<error>Syntax error:  ' . $exception->getMessage() . " in {$filepath} </error>");
199
        $this->output->writeln('');
200
201
        $code = trim($code[($exception->getStartLine()-2)]);
202
        $this->output->writeln("<comment>\t {$code} </comment>");
203
204
        return true;
205
    }
206
207
    /**
208
     * @return Variable[]
209
     */
210
    public function getSymbols()
211
    {
212
        return $this->symbols;
213
    }
214
215
    /**
216
     * @param AbstractDefinition $scope
217
     */
218 366
    public function setScope(AbstractDefinition $scope = null)
219
    {
220 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...
221 366
    }
222
223
    public function debug($message)
224
    {
225
        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...
226
            $this->output->writeln($message);
227
        }
228
    }
229
230
    /**
231
     * @return string
232
     */
233
    public function getFilepath()
234
    {
235
        return $this->filepath;
236
    }
237
238
    /**
239
     * @param string $filepath
240
     */
241
    public function setFilepath($filepath)
242
    {
243
        $this->filepath = $filepath;
244
    }
245
246
    /**
247
     * @return int|string
248
     */
249
    public function getCurrentBranch()
250
    {
251
        return $this->currentBranch;
252
    }
253
254
    /**
255
     * @param int|string $currentBranch
256
     */
257
    public function setCurrentBranch($currentBranch)
258
    {
259
        $this->currentBranch = $currentBranch;
260
    }
261
}
262