2016-08-13 20:20:46 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Psalm\Checker;
|
|
|
|
|
|
|
|
use PhpParser;
|
|
|
|
use Psalm\StatementsSource;
|
2016-08-14 01:44:24 +02:00
|
|
|
use Psalm\Context;
|
2016-08-13 20:20:46 +02:00
|
|
|
|
|
|
|
class ClassChecker extends ClassLikeChecker
|
|
|
|
{
|
2016-08-14 00:54:49 +02:00
|
|
|
protected static $existing_classes = [];
|
|
|
|
protected static $existing_classes_ci = [];
|
|
|
|
protected static $class_extends = [];
|
|
|
|
protected static $class_implements = [];
|
|
|
|
|
2016-08-13 20:20:46 +02:00
|
|
|
public function __construct(PhpParser\Node\Stmt\Class_ $class, StatementsSource $source, $absolute_class)
|
|
|
|
{
|
|
|
|
parent::__construct($class, $source, $absolute_class);
|
2016-08-14 00:54:49 +02:00
|
|
|
|
|
|
|
self::$existing_classes[$absolute_class] = true;
|
|
|
|
self::$existing_classes_ci[strtolower($absolute_class)] = true;
|
|
|
|
|
|
|
|
self::$class_implements[$absolute_class] = [];
|
|
|
|
|
2016-08-14 01:44:24 +02:00
|
|
|
if ($this->class->extends) {
|
|
|
|
$this->parent_class = self::getAbsoluteClassFromName($this->class->extends, $this->namespace, $this->aliased_classes);
|
|
|
|
}
|
|
|
|
|
2016-08-14 00:54:49 +02:00
|
|
|
foreach ($class->implements as $interface_name) {
|
|
|
|
$absolute_interface_name = self::getAbsoluteClassFromName($interface_name, $this->namespace, $this->aliased_classes);
|
|
|
|
|
2016-08-14 01:44:24 +02:00
|
|
|
|
|
|
|
|
2016-08-14 00:54:49 +02:00
|
|
|
self::$class_implements[$absolute_class][$absolute_interface_name] = true;
|
|
|
|
}
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
2016-08-14 01:44:24 +02:00
|
|
|
public function check($check_methods = true, Context $class_context = null)
|
|
|
|
{
|
|
|
|
if ($this->parent_class) {
|
|
|
|
if (self::checkAbsoluteClassOrInterface(
|
|
|
|
$this->parent_class,
|
|
|
|
$this->file_name,
|
|
|
|
$this->class->getLine(),
|
|
|
|
$this->getSuppressedIssues()
|
|
|
|
) === false
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset(self::$public_class_properties[$this->parent_class])
|
|
|
|
|| !isset(self::$public_class_constants[$this->parent_class])
|
|
|
|
|| !isset(self::$class_implements[$this->parent_class])
|
|
|
|
) {
|
|
|
|
self::registerClass($this->parent_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->registerInheritedMethods($this->parent_class);
|
|
|
|
|
|
|
|
self::$class_implements[$this->absolute_class] += self::$class_implements[$this->parent_class];
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (self::$class_implements[$this->absolute_class] as $interface_name => $_) {
|
|
|
|
if (self::checkAbsoluteClassOrInterface(
|
|
|
|
$interface_name,
|
|
|
|
$this->file_name,
|
|
|
|
$this->class->getLine(),
|
|
|
|
$this->getSuppressedIssues()
|
|
|
|
) === false
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self::registerClass($interface_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
parent::check($check_methods, $class_context);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-13 20:20:46 +02:00
|
|
|
public static function classExists($absolute_class)
|
|
|
|
{
|
2016-08-14 00:54:49 +02:00
|
|
|
if (isset(self::$existing_classes_ci[strtolower($absolute_class)])) {
|
|
|
|
return self::$existing_classes_ci[strtolower($absolute_class)];
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (in_array($absolute_class, self::$SPECIAL_TYPES)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-14 00:54:49 +02:00
|
|
|
if (class_exists($absolute_class)) {
|
|
|
|
$reflected_class = new \ReflectionClass($absolute_class);
|
|
|
|
|
|
|
|
self::$existing_classes_ci[strtolower($absolute_class)] = true;
|
|
|
|
self::$existing_classes[$reflected_class->getName()] = true;
|
|
|
|
|
2016-08-13 20:20:46 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-08-14 00:54:49 +02:00
|
|
|
self::$existing_classes_ci[strtolower($absolute_class)] = false;
|
|
|
|
self::$existing_classes_ci[$absolute_class] = false;
|
|
|
|
|
2016-08-13 20:20:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-14 00:54:49 +02:00
|
|
|
public static function hasCorrectCasing($absolute_class)
|
|
|
|
{
|
|
|
|
if (!self::classExists($absolute_class)) {
|
|
|
|
throw new \InvalidArgumentException('Cannot check casing on nonexistent class ' . $absolute_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
return isset(self::$existing_classes[$absolute_class]);
|
|
|
|
}
|
|
|
|
|
2016-08-13 20:20:46 +02:00
|
|
|
/**
|
|
|
|
* @param string $absolute_class
|
|
|
|
* @param string $possible_parent
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function classExtends($absolute_class, $possible_parent)
|
|
|
|
{
|
2016-08-14 00:54:49 +02:00
|
|
|
if (isset(self::$class_extends[$absolute_class][$possible_parent])) {
|
|
|
|
return self::$class_extends[$absolute_class][$possible_parent];
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!self::classExists($absolute_class) || !self::classExists($possible_parent)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-14 00:54:49 +02:00
|
|
|
if (!isset(self::$class_extends[$absolute_class])) {
|
|
|
|
self::$class_extends[$absolute_class] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
self::$class_extends[$absolute_class][$possible_parent] = is_subclass_of($absolute_class, $possible_parent);
|
|
|
|
|
|
|
|
return self::$class_extends[$absolute_class][$possible_parent];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function getInterfacesForClass($absolute_class)
|
|
|
|
{
|
2016-08-14 01:44:24 +02:00
|
|
|
if (!isset(self::$class_implements[$absolute_class])) {
|
|
|
|
self::$class_implements[$absolute_class] = class_implements($absolute_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
return self::$class_implements[$absolute_class];
|
2016-08-14 00:54:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function classImplements($absolute_class, $interface)
|
|
|
|
{
|
|
|
|
if (isset(self::$class_implements[$absolute_class][$interface])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset(self::$class_implements[$absolute_class])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ClassChecker::classExists($absolute_class)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_array($interface, self::$SPECIAL_TYPES)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-14 01:44:24 +02:00
|
|
|
$class_implementations = self::getInterfacesForClass($absolute_class);
|
2016-08-13 20:20:46 +02:00
|
|
|
|
2016-08-14 01:44:24 +02:00
|
|
|
return isset($class_implementations[$interface]);
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
}
|