mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Protect against array assignment to strings
This commit is contained in:
parent
84fecaa2a5
commit
4c0b50dab0
@ -244,7 +244,20 @@ class ClassMethodChecker extends FunctionChecker
|
||||
return;
|
||||
}
|
||||
|
||||
$method = new \ReflectionMethod($method_id);
|
||||
try {
|
||||
$method = new \ReflectionMethod($method_id);
|
||||
}
|
||||
catch (\ReflectionException $e) {
|
||||
// maybe it's an old-timey constructor
|
||||
|
||||
$absolute_class = explode('::', $method_id)[0];
|
||||
$class_name = array_pop(explode('\'', $absolute_class));
|
||||
|
||||
$alt_method_id = $absolute_class . '::' . $class_name;
|
||||
|
||||
$method = new \ReflectionMethod($alt_method_id);
|
||||
}
|
||||
|
||||
self::$_have_reflected[$method_id] = true;
|
||||
|
||||
self::$_static_methods[$method_id] = $method->isStatic();
|
||||
|
@ -16,7 +16,6 @@ class ExceptionHandler
|
||||
throw new CodeException($e->getMessage());
|
||||
}
|
||||
|
||||
echo get_class($e) . PHP_EOL;
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
|
||||
return true;
|
||||
|
7
src/CodeInspector/Issue/InvalidArrayAccess.php
Normal file
7
src/CodeInspector/Issue/InvalidArrayAccess.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace CodeInspector\Issue;
|
||||
|
||||
class InvalidArrayAccess extends CodeError
|
||||
{
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
|
||||
namespace CodeInspector\Issue;
|
||||
|
||||
class InvalidArrayAssignment extends CodeIssue
|
||||
class InvalidArrayAssignment extends CodeArray
|
||||
{
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use CodeInspector\Issue\NullReference;
|
||||
use CodeInspector\Issue\ParentNotFound;
|
||||
use CodeInspector\Issue\PossiblyUndefinedVariable;
|
||||
use CodeInspector\Issue\InvalidArrayAssignment;
|
||||
use CodeInspector\Issue\InvalidArrayAccess;
|
||||
use CodeInspector\Issue\InvalidScope;
|
||||
use CodeInspector\Issue\InvalidStaticInvocation;
|
||||
use CodeInspector\Issue\InvalidStaticVariable;
|
||||
@ -1842,6 +1843,16 @@ class StatementsChecker
|
||||
return $type;
|
||||
}
|
||||
|
||||
foreach ($assignment_type->types as $at) {
|
||||
if ($type->value === 'string' && $at->isString()) {
|
||||
if (ExceptionHandler::accepts(
|
||||
new InvalidArrayAssignment('Cannot assign value on variable ' . $var_id . ' using string offset', $this->_file_name, $line_number)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($type->value !== 'array' && !ClassChecker::classImplements($type->value, 'ArrayAccess')) {
|
||||
if (ExceptionHandler::accepts(
|
||||
new InvalidArrayAssignment('Cannot assign value on variable ' . $var_id . ' that does not implement ArrayAccess', $this->_file_name, $line_number)
|
||||
@ -2818,15 +2829,48 @@ class StatementsChecker
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PhpParser\Node\Expr\ArrayDimFetch $stmt
|
||||
* @param array &$vars_in_scope
|
||||
* @param array &$vars_possibly_in_scope
|
||||
* @return false|null
|
||||
*/
|
||||
protected function _checkArrayAccess(PhpParser\Node\Expr\ArrayDimFetch $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope)
|
||||
{
|
||||
if ($this->_checkExpression($stmt->var, $vars_in_scope, $vars_possibly_in_scope) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$var_type = null;
|
||||
|
||||
if (isset($stmt->var->inferredType)) {
|
||||
$var_type = $stmt->var->inferredType;
|
||||
|
||||
if ($var_type instanceof Type\Generic) {
|
||||
// create a union type to pass back to the statement
|
||||
$array_type = $var_type->type_params[0] instanceof Type\Union ? $var_type->type_params[0] : new Type\Union([$var_type->type_params[0]]);
|
||||
$stmt->inferredType = $array_type;
|
||||
}
|
||||
}
|
||||
|
||||
if ($stmt->dim) {
|
||||
if ($this->_checkExpression($stmt->dim, $vars_in_scope, $vars_possibly_in_scope) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($stmt->dim->inferredType) && $var_type && $var_type->isString()) {
|
||||
foreach ($stmt->dim->inferredType->types as $at) {
|
||||
if ($at->isString()) {
|
||||
$var_id = self::getVarId($stmt->var);
|
||||
|
||||
if (ExceptionHandler::accepts(
|
||||
new InvalidArrayAccess('Cannot access value on string variable ' . $var_id . ' using string offset', $this->_file_name, $stmt->getLine())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,6 +291,17 @@ abstract class Type
|
||||
}
|
||||
}
|
||||
|
||||
public function isString()
|
||||
{
|
||||
if ($this instanceof Atomic) {
|
||||
return $this->value === 'string';
|
||||
}
|
||||
|
||||
if ($this instanceof Union) {
|
||||
return count($this->types) === 1 && isset($this->types['string']);
|
||||
}
|
||||
}
|
||||
|
||||
public function isVoid()
|
||||
{
|
||||
if ($this instanceof Atomic) {
|
||||
|
Loading…
Reference in New Issue
Block a user