2016-08-13 20:20:46 +02:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Checker;
|
|
|
|
|
|
|
|
use PhpParser;
|
|
|
|
use Psalm\StatementsSource;
|
|
|
|
|
|
|
|
class ClassChecker extends ClassLikeChecker
|
|
|
|
{
|
2016-10-31 20:42:20 +01:00
|
|
|
/**
|
2017-07-25 22:11:02 +02:00
|
|
|
* @param PhpParser\Node\Stmt\Class_ $class
|
2016-11-02 07:29:00 +01:00
|
|
|
* @param StatementsSource $source
|
2016-11-07 23:29:51 +01:00
|
|
|
* @param string|null $fq_class_name
|
2016-09-09 15:13:41 +02:00
|
|
|
*/
|
2017-07-25 22:11:02 +02:00
|
|
|
public function __construct(PhpParser\Node\Stmt\Class_ $class, StatementsSource $source, $fq_class_name)
|
2016-08-13 20:20:46 +02:00
|
|
|
{
|
2017-07-25 22:11:02 +02:00
|
|
|
if (!$fq_class_name) {
|
|
|
|
$fq_class_name = self::getAnonymousClassName($class, $source->getFilePath());
|
2016-10-21 02:54:17 +02:00
|
|
|
}
|
|
|
|
|
2016-11-07 23:29:51 +01:00
|
|
|
parent::__construct($class, $source, $fq_class_name);
|
2016-08-14 00:54:49 +02:00
|
|
|
|
2017-01-27 07:23:12 +01:00
|
|
|
if (!$this->class instanceof PhpParser\Node\Stmt\Class_) {
|
|
|
|
throw new \InvalidArgumentException('Bad');
|
|
|
|
}
|
|
|
|
|
2016-08-14 01:44:24 +02:00
|
|
|
if ($this->class->extends) {
|
2017-01-07 20:35:07 +01:00
|
|
|
$this->parent_fq_class_name = self::getFQCLNFromNameObject(
|
2016-11-02 07:29:00 +01:00
|
|
|
$this->class->extends,
|
2017-07-25 22:11:02 +02:00
|
|
|
$this->source->getAliases()
|
2016-11-02 07:29:00 +01:00
|
|
|
);
|
2016-08-14 01:44:24 +02:00
|
|
|
}
|
2017-07-25 22:11:02 +02:00
|
|
|
}
|
2016-08-14 01:44:24 +02:00
|
|
|
|
2017-07-25 22:11:02 +02:00
|
|
|
/**
|
|
|
|
* @param PhpParser\Node\Stmt\Class_ $class
|
|
|
|
* @param string $file_path
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function getAnonymousClassName(PhpParser\Node\Stmt\Class_ $class, $file_path)
|
|
|
|
{
|
|
|
|
return preg_replace('/[^A-Za-z0-9]/', '_', $file_path . ':' . $class->getLine());
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
2016-09-09 15:13:41 +02:00
|
|
|
/**
|
|
|
|
* Determine whether or not a given class exists
|
|
|
|
*
|
2017-01-02 21:31:18 +01:00
|
|
|
* @param string $fq_class_name
|
|
|
|
* @param FileChecker $file_checker
|
2017-05-27 02:16:18 +02:00
|
|
|
*
|
2016-09-09 15:13:41 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2017-07-29 21:05:06 +02:00
|
|
|
public static function classExists(ProjectChecker $project_checker, $fq_class_name)
|
2016-08-13 20:20:46 +02:00
|
|
|
{
|
2017-01-02 21:31:18 +01:00
|
|
|
if (isset(self::$SPECIAL_TYPES[$fq_class_name])) {
|
|
|
|
return false;
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
2017-01-09 05:58:06 +01:00
|
|
|
if ($fq_class_name === 'Generator') {
|
|
|
|
return true;
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
return $project_checker->hasFullyQualifiedClassName($fq_class_name);
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
|
2016-09-09 15:13:41 +02:00
|
|
|
/**
|
|
|
|
* Determine whether or not a class has the correct casing
|
|
|
|
*
|
2017-01-02 21:31:18 +01:00
|
|
|
* @param string $fq_class_name
|
2017-05-27 02:16:18 +02:00
|
|
|
*
|
2016-09-09 15:13:41 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2017-07-29 21:05:06 +02:00
|
|
|
public static function hasCorrectCasing(ProjectChecker $project_checker, $fq_class_name)
|
2016-08-14 00:54:49 +02:00
|
|
|
{
|
2017-01-09 05:58:06 +01:00
|
|
|
if ($fq_class_name === 'Generator') {
|
|
|
|
return true;
|
2016-08-14 00:54:49 +02:00
|
|
|
}
|
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
return isset($project_checker->existing_classes[$fq_class_name]);
|
2016-08-14 00:54:49 +02:00
|
|
|
}
|
|
|
|
|
2016-08-13 20:20:46 +02:00
|
|
|
/**
|
2016-09-09 15:13:41 +02:00
|
|
|
* Determine whether or not a class extends a parent
|
|
|
|
*
|
2017-01-02 21:31:18 +01:00
|
|
|
* @param string $fq_class_name
|
|
|
|
* @param string $possible_parent
|
2017-05-27 02:16:18 +02:00
|
|
|
*
|
2016-08-13 20:20:46 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2017-07-29 21:05:06 +02:00
|
|
|
public static function classExtends(ProjectChecker $project_checker, $fq_class_name, $possible_parent)
|
2016-08-13 20:20:46 +02:00
|
|
|
{
|
2017-01-09 05:58:06 +01:00
|
|
|
$fq_class_name = strtolower($fq_class_name);
|
|
|
|
|
2017-01-27 16:41:32 +01:00
|
|
|
if ($fq_class_name === 'generator') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
$class_storage = $project_checker->classlike_storage_provider->get($fq_class_name);
|
2016-08-13 20:20:46 +02:00
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
return in_array(strtolower($possible_parent), $class_storage->parent_classes, true);
|
2016-08-14 00:54:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-09-09 15:13:41 +02:00
|
|
|
* Check whether a class implements an interface
|
|
|
|
*
|
2017-01-02 21:31:18 +01:00
|
|
|
* @param string $fq_class_name
|
|
|
|
* @param string $interface
|
2017-05-27 02:16:18 +02:00
|
|
|
*
|
2016-08-14 00:54:49 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2017-07-29 21:05:06 +02:00
|
|
|
public static function classImplements(ProjectChecker $project_checker, $fq_class_name, $interface)
|
2016-08-14 00:54:49 +02:00
|
|
|
{
|
2016-08-22 22:41:45 +02:00
|
|
|
$interface_id = strtolower($interface);
|
|
|
|
|
2017-01-09 07:48:55 +01:00
|
|
|
$fq_class_name = strtolower($fq_class_name);
|
|
|
|
|
|
|
|
if ($interface_id === 'callable' && $fq_class_name === 'closure') {
|
2016-10-21 00:05:28 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-09 02:58:50 +01:00
|
|
|
if ($interface_id === 'traversable' && $fq_class_name === 'generator') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-01-09 07:48:55 +01:00
|
|
|
if (isset(self::$SPECIAL_TYPES[$interface_id]) || isset(self::$SPECIAL_TYPES[$fq_class_name])) {
|
2016-08-14 00:54:49 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
$class_storage = $project_checker->classlike_storage_provider->get($fq_class_name);
|
2016-08-13 20:20:46 +02:00
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
return isset($class_storage->class_implements[$interface_id]);
|
2016-08-13 20:20:46 +02:00
|
|
|
}
|
|
|
|
}
|