1
0
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:
Matthew Brown 2016-06-17 16:58:15 -04:00
parent 84fecaa2a5
commit 4c0b50dab0
6 changed files with 77 additions and 3 deletions

View File

@ -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();

View File

@ -16,7 +16,6 @@ class ExceptionHandler
throw new CodeException($e->getMessage());
}
echo get_class($e) . PHP_EOL;
echo $e->getMessage() . PHP_EOL;
return true;

View File

@ -0,0 +1,7 @@
<?php
namespace CodeInspector\Issue;
class InvalidArrayAccess extends CodeError
{
}

View File

@ -2,6 +2,6 @@
namespace CodeInspector\Issue;
class InvalidArrayAssignment extends CodeIssue
class InvalidArrayAssignment extends CodeArray
{
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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) {