2018-01-14 18:09:40 +01:00
|
|
|
<?php
|
2018-11-06 03:57:36 +01:00
|
|
|
namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
|
2018-01-14 18:09:40 +01:00
|
|
|
|
|
|
|
use PhpParser;
|
2018-11-06 03:57:36 +01:00
|
|
|
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
2020-05-18 21:13:27 +02:00
|
|
|
use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
|
2018-11-06 03:57:36 +01:00
|
|
|
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
|
|
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
2020-09-21 05:59:52 +02:00
|
|
|
use Psalm\Internal\ControlFlow\TaintSource;
|
2018-01-14 18:09:40 +01:00
|
|
|
use Psalm\CodeLocation;
|
|
|
|
use Psalm\Context;
|
2020-08-24 00:37:46 +02:00
|
|
|
use Psalm\Issue\ImpureVariable;
|
2018-01-14 18:09:40 +01:00
|
|
|
use Psalm\Issue\InvalidScope;
|
|
|
|
use Psalm\Issue\PossiblyUndefinedGlobalVariable;
|
|
|
|
use Psalm\Issue\PossiblyUndefinedVariable;
|
|
|
|
use Psalm\Issue\UndefinedGlobalVariable;
|
|
|
|
use Psalm\Issue\UndefinedVariable;
|
|
|
|
use Psalm\IssueBuffer;
|
|
|
|
use Psalm\Type;
|
2019-06-26 22:52:29 +02:00
|
|
|
use function is_string;
|
2020-05-19 18:56:23 +02:00
|
|
|
use function in_array;
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2018-12-02 00:37:49 +01:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2018-11-06 03:57:36 +01:00
|
|
|
class VariableFetchAnalyzer
|
2018-01-14 18:09:40 +01:00
|
|
|
{
|
2020-09-20 18:54:46 +02:00
|
|
|
private const SUPER_GLOBALS = [
|
2020-05-19 18:56:23 +02:00
|
|
|
'$GLOBALS',
|
|
|
|
'$_SERVER',
|
|
|
|
'$_GET',
|
|
|
|
'$_POST',
|
|
|
|
'$_FILES',
|
|
|
|
'$_COOKIE',
|
|
|
|
'$_SESSION',
|
|
|
|
'$_REQUEST',
|
|
|
|
'$_ENV',
|
|
|
|
'$http_response_header',
|
|
|
|
];
|
|
|
|
|
2018-01-14 18:09:40 +01:00
|
|
|
/**
|
|
|
|
* @param bool $passed_by_reference
|
|
|
|
* @param Type\Union|null $by_ref_type
|
|
|
|
* @param bool $array_assignment
|
2018-05-23 05:38:27 +02:00
|
|
|
* @param bool $from_global - when used in a global keyword
|
2018-01-14 18:09:40 +01:00
|
|
|
*/
|
|
|
|
public static function analyze(
|
2018-11-11 18:01:14 +01:00
|
|
|
StatementsAnalyzer $statements_analyzer,
|
2018-01-14 18:09:40 +01:00
|
|
|
PhpParser\Node\Expr\Variable $stmt,
|
|
|
|
Context $context,
|
|
|
|
$passed_by_reference = false,
|
|
|
|
Type\Union $by_ref_type = null,
|
2018-05-23 05:38:27 +02:00
|
|
|
$array_assignment = false,
|
|
|
|
$from_global = false
|
2020-05-18 21:13:27 +02:00
|
|
|
) : bool {
|
2018-11-11 18:01:14 +01:00
|
|
|
$project_analyzer = $statements_analyzer->getFileAnalyzer()->project_analyzer;
|
|
|
|
$codebase = $statements_analyzer->getCodebase();
|
2018-10-07 02:11:19 +02:00
|
|
|
|
2018-01-14 18:09:40 +01:00
|
|
|
if ($stmt->name === 'this') {
|
2018-11-11 18:01:14 +01:00
|
|
|
if ($statements_analyzer->isStatic()) {
|
2018-01-14 18:09:40 +01:00
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new InvalidScope(
|
|
|
|
'Invalid reference to $this in a static context',
|
2018-11-11 18:01:14 +01:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
2018-01-14 18:09:40 +01:00
|
|
|
),
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->getSuppressedIssues()
|
2018-01-14 18:09:40 +01:00
|
|
|
)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-10-26 22:17:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset($context->vars_in_scope['$this'])) {
|
2018-01-14 18:09:40 +01:00
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new InvalidScope(
|
|
|
|
'Invalid reference to $this in a non-class context',
|
2018-11-11 18:01:14 +01:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
2018-01-14 18:09:40 +01:00
|
|
|
),
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->getSuppressedIssues()
|
2018-01-14 18:09:40 +01:00
|
|
|
)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-09 17:50:17 +02:00
|
|
|
$context->vars_in_scope['$this'] = Type::getMixed();
|
|
|
|
$context->vars_possibly_in_scope['$this'] = true;
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, clone $context->vars_in_scope['$this']);
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2019-02-24 07:33:25 +01:00
|
|
|
if ($codebase->store_node_types
|
2019-07-01 15:55:39 +02:00
|
|
|
&& !$context->collect_initializations
|
|
|
|
&& !$context->collect_mutations
|
2019-11-25 17:44:54 +01:00
|
|
|
&& ($stmt_type = $statements_analyzer->node_data->getType($stmt))
|
2018-10-26 22:17:15 +02:00
|
|
|
) {
|
|
|
|
$codebase->analyzer->addNodeType(
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->getFilePath(),
|
2018-10-26 22:17:15 +02:00
|
|
|
$stmt,
|
2020-02-23 23:03:27 +01:00
|
|
|
$stmt_type->getId()
|
2018-10-26 22:17:15 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-09-04 22:46:20 +02:00
|
|
|
if (!$context->collect_mutations && !$context->collect_initializations) {
|
|
|
|
if ($context->pure) {
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new ImpureVariable(
|
|
|
|
'Cannot reference $this in a pure context',
|
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
|
|
|
),
|
|
|
|
$statements_analyzer->getSuppressedIssues()
|
|
|
|
)) {
|
|
|
|
// fall through
|
|
|
|
}
|
|
|
|
} elseif ($statements_analyzer->getSource() instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer
|
|
|
|
&& $statements_analyzer->getSource()->track_mutations
|
|
|
|
) {
|
|
|
|
$statements_analyzer->getSource()->inferred_impure = true;
|
2020-08-24 04:07:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$context->check_variables) {
|
|
|
|
if (is_string($stmt->name)) {
|
|
|
|
$var_name = '$' . $stmt->name;
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
if (!$context->hasVariable($var_name, $statements_analyzer)) {
|
2018-01-14 18:09:40 +01:00
|
|
|
$context->vars_in_scope[$var_name] = Type::getMixed();
|
|
|
|
$context->vars_possibly_in_scope[$var_name] = true;
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, Type::getMixed());
|
2018-01-14 18:09:40 +01:00
|
|
|
} else {
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, clone $context->vars_in_scope[$var_name]);
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
} else {
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, Type::getMixed());
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
2020-05-19 18:56:23 +02:00
|
|
|
if (is_string($stmt->name) && self::isSuperGlobal('$' . $stmt->name)) {
|
2019-03-06 00:14:07 +01:00
|
|
|
$var_name = '$' . $stmt->name;
|
|
|
|
|
|
|
|
if (isset($context->vars_in_scope[$var_name])) {
|
2020-06-18 19:45:58 +02:00
|
|
|
$type = clone $context->vars_in_scope[$var_name];
|
|
|
|
|
|
|
|
self::taintVariable($statements_analyzer, $var_name, $type, $stmt);
|
|
|
|
|
|
|
|
$statements_analyzer->node_data->setType($stmt, $type);
|
2019-01-19 17:31:51 +01:00
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2019-01-19 17:31:51 +01:00
|
|
|
}
|
|
|
|
|
2020-05-19 18:56:23 +02:00
|
|
|
$type = self::getGlobalType($var_name);
|
2019-03-06 00:14:07 +01:00
|
|
|
|
2020-06-18 19:45:58 +02:00
|
|
|
self::taintVariable($statements_analyzer, $var_name, $type, $stmt);
|
|
|
|
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, $type);
|
2019-03-06 00:14:07 +01:00
|
|
|
$context->vars_in_scope[$var_name] = clone $type;
|
|
|
|
$context->vars_possibly_in_scope[$var_name] = true;
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_string($stmt->name)) {
|
2020-08-24 00:37:46 +02:00
|
|
|
if ($context->pure) {
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new ImpureVariable(
|
|
|
|
'Cannot reference an unknown variable in a pure context',
|
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
|
|
|
),
|
|
|
|
$statements_analyzer->getSuppressedIssues()
|
|
|
|
)) {
|
|
|
|
// fall through
|
|
|
|
}
|
2020-08-28 18:42:55 +02:00
|
|
|
} elseif ($statements_analyzer->getSource() instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer
|
|
|
|
&& $statements_analyzer->getSource()->track_mutations
|
2020-08-24 00:37:46 +02:00
|
|
|
) {
|
|
|
|
$statements_analyzer->getSource()->inferred_impure = true;
|
|
|
|
}
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
return ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context);
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($passed_by_reference && $by_ref_type) {
|
2020-05-18 21:13:27 +02:00
|
|
|
AssignmentAnalyzer::assignByRefParam(
|
2019-04-26 00:02:19 +02:00
|
|
|
$statements_analyzer,
|
|
|
|
$stmt,
|
|
|
|
$by_ref_type,
|
|
|
|
$by_ref_type,
|
|
|
|
$context
|
|
|
|
);
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$var_name = '$' . $stmt->name;
|
|
|
|
|
2019-07-04 22:38:31 +02:00
|
|
|
if (!$context->hasVariable($var_name, !$array_assignment ? $statements_analyzer : null)) {
|
2018-01-14 18:09:40 +01:00
|
|
|
if (!isset($context->vars_possibly_in_scope[$var_name]) ||
|
2018-11-11 18:01:14 +01:00
|
|
|
!$statements_analyzer->getFirstAppearance($var_name)
|
2018-01-14 18:09:40 +01:00
|
|
|
) {
|
|
|
|
if ($array_assignment) {
|
|
|
|
// if we're in an array assignment, let's assign the variable
|
|
|
|
// because PHP allows it
|
|
|
|
|
|
|
|
$context->vars_in_scope[$var_name] = Type::getArray();
|
|
|
|
$context->vars_possibly_in_scope[$var_name] = true;
|
|
|
|
|
|
|
|
// it might have been defined first in another if/else branch
|
2018-11-11 18:01:14 +01:00
|
|
|
if (!$statements_analyzer->hasVariable($var_name)) {
|
|
|
|
$statements_analyzer->registerVariable(
|
2018-01-14 18:09:40 +01:00
|
|
|
$var_name,
|
2018-11-11 18:01:14 +01:00
|
|
|
new CodeLocation($statements_analyzer, $stmt),
|
2018-01-21 22:24:20 +01:00
|
|
|
$context->branch_point
|
2018-01-14 18:09:40 +01:00
|
|
|
);
|
|
|
|
}
|
2018-05-01 04:13:13 +02:00
|
|
|
} elseif (!$context->inside_isset
|
2018-11-11 18:01:14 +01:00
|
|
|
|| $statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
|
2018-05-01 04:13:13 +02:00
|
|
|
) {
|
2018-05-23 05:38:27 +02:00
|
|
|
if ($context->is_global || $from_global) {
|
2018-01-14 18:09:40 +01:00
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new UndefinedGlobalVariable(
|
|
|
|
'Cannot find referenced variable ' . $var_name . ' in global scope',
|
2020-07-16 15:49:59 +02:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
|
|
|
$var_name
|
2018-01-14 18:09:40 +01:00
|
|
|
),
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->getSuppressedIssues()
|
2018-01-14 18:09:40 +01:00
|
|
|
)) {
|
2020-05-15 04:51:51 +02:00
|
|
|
// fall through
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, Type::getMixed());
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
2018-06-01 16:52:26 +02:00
|
|
|
if (IssueBuffer::accepts(
|
2018-01-14 18:09:40 +01:00
|
|
|
new UndefinedVariable(
|
|
|
|
'Cannot find referenced variable ' . $var_name,
|
2018-11-11 18:01:14 +01:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
2018-06-01 16:52:26 +02:00
|
|
|
),
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->getSuppressedIssues()
|
2018-06-01 16:52:26 +02:00
|
|
|
)) {
|
|
|
|
// fall through
|
|
|
|
}
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, Type::getMixed());
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$first_appearance = $statements_analyzer->getFirstAppearance($var_name);
|
2018-01-14 18:09:40 +01:00
|
|
|
|
2018-02-10 16:30:08 +01:00
|
|
|
if ($first_appearance && !$context->inside_isset && !$context->inside_unset) {
|
2018-01-22 19:54:32 +01:00
|
|
|
if ($context->is_global) {
|
2018-11-06 03:57:36 +01:00
|
|
|
if ($codebase->alter_code) {
|
2018-11-11 18:01:14 +01:00
|
|
|
if (!isset($project_analyzer->getIssuesToFix()['PossiblyUndefinedGlobalVariable'])) {
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-22 19:54:32 +01:00
|
|
|
}
|
2018-01-21 22:24:20 +01:00
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$branch_point = $statements_analyzer->getBranchPoint($var_name);
|
2018-01-21 22:24:20 +01:00
|
|
|
|
2018-01-22 19:54:32 +01:00
|
|
|
if ($branch_point) {
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->addVariableInitialization($var_name, $branch_point);
|
2018-01-22 19:54:32 +01:00
|
|
|
}
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-22 19:54:32 +01:00
|
|
|
}
|
2018-01-21 22:24:20 +01:00
|
|
|
|
2018-01-14 18:09:40 +01:00
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new PossiblyUndefinedGlobalVariable(
|
|
|
|
'Possibly undefined global variable ' . $var_name . ', first seen on line ' .
|
|
|
|
$first_appearance->getLineNumber(),
|
2020-07-16 15:49:59 +02:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
|
|
|
$var_name
|
2018-01-14 18:09:40 +01:00
|
|
|
),
|
2019-12-02 21:24:01 +01:00
|
|
|
$statements_analyzer->getSuppressedIssues(),
|
|
|
|
(bool) $statements_analyzer->getBranchPoint($var_name)
|
2018-01-14 18:09:40 +01:00
|
|
|
)) {
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
} else {
|
2018-11-06 03:57:36 +01:00
|
|
|
if ($codebase->alter_code) {
|
2018-11-11 18:01:14 +01:00
|
|
|
if (!isset($project_analyzer->getIssuesToFix()['PossiblyUndefinedVariable'])) {
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-22 19:54:32 +01:00
|
|
|
}
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$branch_point = $statements_analyzer->getBranchPoint($var_name);
|
2018-01-22 19:54:32 +01:00
|
|
|
|
|
|
|
if ($branch_point) {
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->addVariableInitialization($var_name, $branch_point);
|
2018-01-22 19:54:32 +01:00
|
|
|
}
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-22 19:54:32 +01:00
|
|
|
}
|
|
|
|
|
2018-01-14 18:09:40 +01:00
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new PossiblyUndefinedVariable(
|
|
|
|
'Possibly undefined variable ' . $var_name . ', first seen on line ' .
|
|
|
|
$first_appearance->getLineNumber(),
|
2018-11-11 18:01:14 +01:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
2018-01-14 18:09:40 +01:00
|
|
|
),
|
2019-12-02 21:24:01 +01:00
|
|
|
$statements_analyzer->getSuppressedIssues(),
|
|
|
|
(bool) $statements_analyzer->getBranchPoint($var_name)
|
2018-01-14 18:09:40 +01:00
|
|
|
)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-01-29 18:13:44 +01:00
|
|
|
|
2019-07-02 00:48:33 +02:00
|
|
|
if ($codebase->store_node_types
|
|
|
|
&& !$context->collect_initializations
|
|
|
|
&& !$context->collect_mutations
|
|
|
|
) {
|
|
|
|
$codebase->analyzer->addNodeReference(
|
|
|
|
$statements_analyzer->getFilePath(),
|
|
|
|
$stmt,
|
|
|
|
$first_appearance->raw_file_start . '-' . $first_appearance->raw_file_end . ':mixed'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->registerVariableUses([$first_appearance->getHash() => $first_appearance]);
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
} else {
|
2019-11-25 17:44:54 +01:00
|
|
|
$stmt_type = clone $context->vars_in_scope[$var_name];
|
2018-10-26 22:17:15 +02:00
|
|
|
|
2019-11-25 17:44:54 +01:00
|
|
|
$statements_analyzer->node_data->setType($stmt, $stmt_type);
|
|
|
|
|
|
|
|
if ($stmt_type->possibly_undefined_from_try && !$context->inside_isset) {
|
2019-11-09 18:25:30 +01:00
|
|
|
if ($context->is_global) {
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new PossiblyUndefinedGlobalVariable(
|
|
|
|
'Possibly undefined global variable ' . $var_name . ' defined in try block',
|
2020-07-16 15:49:59 +02:00
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt),
|
|
|
|
$var_name
|
2019-11-09 18:25:30 +01:00
|
|
|
),
|
|
|
|
$statements_analyzer->getSuppressedIssues()
|
|
|
|
)) {
|
|
|
|
// fall through
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
new PossiblyUndefinedVariable(
|
|
|
|
'Possibly undefined variable ' . $var_name . ' defined in try block',
|
|
|
|
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
|
|
|
),
|
|
|
|
$statements_analyzer->getSuppressedIssues()
|
|
|
|
)) {
|
|
|
|
// fall through
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-24 07:33:25 +01:00
|
|
|
if ($codebase->store_node_types
|
2019-07-01 15:55:39 +02:00
|
|
|
&& !$context->collect_initializations
|
|
|
|
&& !$context->collect_mutations
|
2018-10-26 22:17:15 +02:00
|
|
|
) {
|
|
|
|
$codebase->analyzer->addNodeType(
|
2018-11-11 18:01:14 +01:00
|
|
|
$statements_analyzer->getFilePath(),
|
2018-10-26 22:17:15 +02:00
|
|
|
$stmt,
|
2020-02-23 23:03:27 +01:00
|
|
|
$stmt_type->getId()
|
2018-10-26 22:17:15 +02:00
|
|
|
);
|
|
|
|
}
|
2019-07-02 00:48:33 +02:00
|
|
|
|
|
|
|
if ($codebase->store_node_types
|
|
|
|
&& !$context->collect_initializations
|
|
|
|
&& !$context->collect_mutations
|
|
|
|
) {
|
|
|
|
$first_appearance = $statements_analyzer->getFirstAppearance($var_name);
|
|
|
|
|
|
|
|
if ($first_appearance) {
|
|
|
|
$codebase->analyzer->addNodeReference(
|
|
|
|
$statements_analyzer->getFilePath(),
|
|
|
|
$stmt,
|
|
|
|
$first_appearance->raw_file_start
|
|
|
|
. '-' . $first_appearance->raw_file_end
|
2019-11-25 17:44:54 +01:00
|
|
|
. ':' . $stmt_type->getId()
|
2019-07-02 00:48:33 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
return true;
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|
2020-05-19 18:56:23 +02:00
|
|
|
|
2020-06-18 19:45:58 +02:00
|
|
|
private static function taintVariable(
|
|
|
|
StatementsAnalyzer $statements_analyzer,
|
|
|
|
string $var_name,
|
|
|
|
Type\Union $type,
|
|
|
|
PhpParser\Node\Expr\Variable $stmt
|
|
|
|
) : void {
|
2020-09-25 06:37:40 +02:00
|
|
|
if ($statements_analyzer->control_flow_graph instanceof \Psalm\Internal\Codebase\TaintFlowGraph
|
2020-07-02 17:53:44 +02:00
|
|
|
&& !\in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
|
|
|
|
) {
|
2020-06-22 23:16:06 +02:00
|
|
|
if ($var_name === '$_GET'
|
|
|
|
|| $var_name === '$_POST'
|
|
|
|
|| $var_name === '$_COOKIE'
|
|
|
|
|| $var_name === '$_REQUEST'
|
|
|
|
) {
|
2020-06-18 19:45:58 +02:00
|
|
|
$taint_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
|
|
|
|
|
2020-09-21 05:59:52 +02:00
|
|
|
$server_taint_source = new TaintSource(
|
2020-06-18 19:45:58 +02:00
|
|
|
$var_name . ':' . $taint_location->file_name . ':' . $taint_location->raw_file_start,
|
|
|
|
$var_name,
|
2020-06-19 00:48:19 +02:00
|
|
|
null,
|
2020-06-18 19:45:58 +02:00
|
|
|
null,
|
|
|
|
Type\TaintKindGroup::ALL_INPUT
|
|
|
|
);
|
|
|
|
|
2020-09-21 05:59:52 +02:00
|
|
|
$statements_analyzer->control_flow_graph->addSource($server_taint_source);
|
2020-06-18 19:45:58 +02:00
|
|
|
|
|
|
|
$type->parent_nodes = [
|
|
|
|
$server_taint_source
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-23 19:52:31 +02:00
|
|
|
/**
|
|
|
|
* @psalm-pure
|
|
|
|
*/
|
2020-05-19 18:56:23 +02:00
|
|
|
public static function isSuperGlobal(string $var_id) : bool
|
|
|
|
{
|
|
|
|
return in_array(
|
|
|
|
$var_id,
|
|
|
|
self::SUPER_GLOBALS,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function getGlobalType(string $var_id) : Type\Union
|
|
|
|
{
|
|
|
|
$config = \Psalm\Config::getInstance();
|
|
|
|
|
|
|
|
if (isset($config->globals[$var_id])) {
|
|
|
|
return Type::parseString($config->globals[$var_id]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($var_id === '$argv') {
|
|
|
|
return new Type\Union([
|
|
|
|
new Type\Atomic\TArray([Type::getInt(), Type::getString()]),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($var_id === '$argc') {
|
|
|
|
return Type::getInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self::isSuperGlobal($var_id)) {
|
|
|
|
$type = Type::getArray();
|
|
|
|
|
|
|
|
return $type;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Type::getMixed();
|
|
|
|
}
|
2018-01-14 18:09:40 +01:00
|
|
|
}
|