Skip to content

Commit cf7ebd4

Browse files
committed
added class inheritance to index
Fixed inheritance ignorance when indexing Fixed properties' type detection via comments
1 parent 1bacbdb commit cf7ebd4

19 files changed

Lines changed: 401 additions & 131 deletions

bin/indexer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
require(dirname(__DIR__) . "/vendor/autoload.php");
55

66
set_time_limit(0);
7-
ini_set('memory_limit','1000M');
7+
ini_set('memory_limit','2048M');
88
ini_set('display_errors', 'stderr');
99

1010
/** @var $command \Command\CommandInterface */

specs/parser/UseParser.spec.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@
5555
expect($fqcn)->to->be->an->instanceof(FQCN::class);
5656
});
5757
describe('Scalar types', function(){
58-
$scalars = ['string', 'int', 'float', 'array'];
58+
$scalars = [
59+
'string', 'int', 'float',
60+
'array', 'bool', 'object',
61+
'void', 'mixed'
62+
];
5963
foreach($scalars AS $scalar){
6064
it('creates valid parts from ' . $scalar, function() use ($scalar){
6165
$fqcn = $this->useParser->parseFQCN($scalar);

src/Complete/Completer/ObjectCompleter.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Entity\FQCN;
77
use Entity\Node\MethodData;
88
use Entity\Node\ClassProperty;
9+
use Entity\Node\InterfaceData;
910
use Entity\Completion\Context;
1011
use Entity\Completion\Entry;
1112
use Entity\Completion\Scope;
@@ -24,13 +25,22 @@ public function getEntries(Project $project, Context $context){
2425
$index = $project->getIndex();
2526
$this->logger->addDebug('Creating completion for ' . $fqcn->toString());
2627
$class = $index->findClassByFQCN($fqcn);
28+
if(empty($class)){
29+
$class = $index->findInterfaceByFQCN($fqcn);
30+
}
31+
if(empty($class)){
32+
return [];
33+
}
2734
$entries = [];
2835
if($class->methods !== null){
2936
foreach($class->methods->all() AS $method){
3037
$entry = $this->createEntryForMethod($method);
3138
$entries[] = $entry;
3239
}
3340
}
41+
if($class instanceof InterfaceData){
42+
return $entries;
43+
}
3444
if($class->properties !== null){
3545
foreach($class->properties->all() AS $property){
3646
$entries[] = $this->createEntryForProperty($property);

src/Entity/Collection/MethodsCollection.php

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
namespace Entity\Collection;
44

55
use Entity\Node\MethodData;
6+
use Entity\Node\ClassData;
7+
use Entity\Node\InterfaceData;
68

79
class MethodsCollection{
810
private $methods = [];
11+
private $class;
12+
13+
public function __construct($class){
14+
$this->class = $class;
15+
}
916
public function add(MethodData $method){
1017
$this->methods[$method->name] = $method;
1118
}
@@ -14,12 +21,66 @@ public function remove(MethodData $method){
1421
unset($this->methods[$method->name]);
1522
}
1623
}
17-
public function get($name){
24+
public function get($name, $mode = 'private', $magic = true){
25+
list($public, $protected, $private, $parentMode) = $this->expandMode($mode);
1826
if(array_key_exists($name, $this->methods)){
1927
return $this->methods[$name];
2028
}
29+
if($this->class instanceof InterfaceData){
30+
return;
31+
}
32+
$parent = $this->class->getParent();
33+
if($parent instanceof ClassData){
34+
return $parent->methods->get($name, $parentMode, $magic);
35+
}
36+
}
37+
public function all($mode='private', $magic=false){
38+
$methods = [];
39+
list($public, $protected, $private, $parentMode) = $this->expandMode($mode);
40+
foreach($this->methods AS $method){
41+
if(!$public && $method->isPublic()){
42+
continue;
43+
}
44+
if(!$protected && $method->isProtected()){
45+
continue;
46+
}
47+
if(!$private && $method->isPrivate()){
48+
continue;
49+
}
50+
if($magic !== $method->isMagic()){
51+
continue;
52+
}
53+
$methods[$method->name] = $method;
54+
}
55+
if($this->class instanceof ClassData){
56+
$parent = $this->class->getParent();
57+
if($parent instanceof ClassData){
58+
$methods = array_merge(
59+
$parent->methods->all($parentMode, $magic),
60+
$methods
61+
);
62+
}
63+
}
64+
sort($methods);
65+
return $methods;
2166
}
22-
public function all(){
23-
return $this->methods;
67+
protected function expandMode($mode){
68+
if($mode === 'private'){
69+
$public = $protected = $private = true;
70+
$parentMode = 'protected';
71+
}
72+
elseif($mode === 'protected'){
73+
$public = $protected = true;
74+
$private = false;
75+
$parentMode = 'protected';
76+
}
77+
else {
78+
$protected = $private = false;
79+
$public = true;
80+
$parentMode = 'public';
81+
}
82+
return [
83+
$public, $protected, $private, $parentMode
84+
];
2485
}
2586
}

src/Entity/Collection/PropertiesCollection.php

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,68 @@
55
use Entity\Node\ClassProperty;
66

77
class PropertiesCollection {
8-
private $map = [];
8+
9+
public function __construct($class){
10+
$this->class = $class;
11+
}
912
public function add(ClassProperty $prop){
1013
$this->map[$prop->name] = $prop;
1114
}
12-
public function all(){
13-
return $this->map;
15+
public function all($mode='private'){
16+
$props = [];
17+
list($public, $protected, $private, $parentMode) = $this->expandMode($mode);
18+
foreach($this->map AS $prop){
19+
if(!$public && $prop->isPublic()){
20+
continue;
21+
}
22+
if(!$protected && $prop->isProtected()){
23+
continue;
24+
}
25+
if(!$private && $prop->isPrivate()){
26+
continue;
27+
}
28+
$props[$prop->name] = $prop;
29+
}
30+
$parent = $this->class->getParent();
31+
if($parent instanceof ClassData){
32+
$props = array_merge(
33+
$parent->properties->all($parentMode, $magic),
34+
$props
35+
);
36+
}
37+
sort($props);
38+
return $props;
1439
}
15-
public function get($propName){
40+
public function get($propName, $mode='private'){
41+
list($public, $protected, $private, $parentMode) = $this->expandMode($mode);
1642
if(array_key_exists($propName, $this->map)){
1743
return $this->map[$propName];
1844
}
19-
return null;
45+
$parent = $this->class->getParent();
46+
if($parent instanceof ClassData){
47+
return $parent->properties->get($name, $parentMode);
48+
}
2049
}
50+
protected function expandMode($mode){
51+
if($mode === 'private'){
52+
$public = $protected = $private = true;
53+
$parentMode = 'protected';
54+
}
55+
elseif($mode === 'protected'){
56+
$public = $protected = true;
57+
$private = false;
58+
$parentMode = 'protected';
59+
}
60+
else {
61+
$protected = $private = false;
62+
$public = true;
63+
$parentMode = 'public';
64+
}
65+
return [
66+
$public, $protected, $private, $parentMode
67+
];
68+
}
69+
70+
private $map = [];
71+
private $class;
2172
}

src/Entity/FQCN.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public function __construct($className, $namespace = "", $isArray=false){
2222
case "string":
2323
case "float":
2424
case "array":
25+
case "mixed":
26+
case "void":
27+
case "object":
28+
case "bool":
2529
$this->_isScalar = true;
2630
break;
2731
}

src/Entity/Index.php

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
class Index {
99
private $fqcns = [];
1010
private $classes = [];
11+
private $interfaces = [];
1112
private $classMap = [];
1213
private $flippedClassMap = [];
1314
private $extends = [];
@@ -17,9 +18,10 @@ class Index {
1718
public function getFQCNs(){
1819
return $this->fqcns;
1920
}
20-
public function addFQCN(FQCN $fqcn){
21-
$this->fqcns[$fqcn->toString()] = $fqcn;
22-
}
21+
22+
/**
23+
* @return FQCN
24+
*/
2325
public function findFQCNByFile($file){
2426
if(!array_key_exists($file, $this->flippedClassMap)){
2527
return null;
@@ -34,28 +36,54 @@ public function findFQCNByFile($file){
3436
return $this->fqcns[$fqcnStr];
3537
}
3638

37-
public function getInterfaces(){
38-
return $this->interfaces;
39-
}
40-
public function addInterface(InterfaceData $interface){
41-
$this->interfaces[$interface->fqcn->toString()] = $interface;
42-
}
43-
44-
public function getClasses(){
45-
return $this->classes;
46-
}
39+
/**
40+
* @return ClassData
41+
*/
4742
public function findClassByFQCN(FQCN $fqcn){
4843
$str = $fqcn->toString();
4944
if(array_key_exists($str, $this->classes)){
5045
return $this->classes[$str];
5146
}
5247
return null;
5348
}
54-
public function addClass(ClassData $class, $key = null){
55-
if($key)
56-
$this->classes[$key] = $class;
57-
else
58-
$this->classes[$class->fqcn->toString()] = $class;
49+
50+
/**
51+
* @return InterfaceData
52+
*/
53+
public function findInterfaceByFQCN(FQCN $fqcn){
54+
$str = $fqcn->toString();
55+
if(array_key_exists($str, $this->interfaces)){
56+
return $this->interfaces[$str];
57+
}
58+
}
59+
public function findClassChildren(FQCN $class){
60+
if(!array_key_exists($class->toString(), $this->extends)
61+
|| !is_array($this->extends[$class->toString()])
62+
){
63+
$this->extends[$class->toString()] = [];
64+
}
65+
return $this->extends[$class->toString()];
66+
}
67+
public function getClasses(){
68+
return $this->classes;
69+
}
70+
public function addClass(ClassData $class){
71+
$this->classes[$class->fqcn->toString()] = $class;
72+
if(!empty($class->getParent())){
73+
$this->addExtend($class, $class->getParent());
74+
}
75+
foreach($this->findClassChildren($class->fqcn) AS $child){
76+
$child->setParent($class);
77+
}
78+
}
79+
public function addInterface(InterfaceData $interface){
80+
$this->interfaces[$interface->fqcn->toString()] = $interface;
81+
}
82+
public function getInterfaces(){
83+
return $this->interfaces;
84+
}
85+
public function addFQCN(FQCN $fqcn){
86+
$this->fqcns[$fqcn->toString()] = $fqcn;
5987
}
6088

6189
public function getClassMap(){
@@ -64,27 +92,27 @@ public function getClassMap(){
6492
public function getFlippedClassMap(){
6593
return $this->classMap;
6694
}
67-
public function setClassMap(array $classMap){
68-
$this->classMap = $classMap;
69-
$this->flippedClassMap = array_flip($classMap);
95+
public function getImplements(){
96+
return $this->implements;
7097
}
7198
public function getExtends(){
7299
return $this->extends;
73100
}
74-
public function addExtend($class, $parent){
75-
if(!array_key_exists($parent, $this->extends)
76-
|| !is_array($this->extends[$parent])){
77-
$this->extends[$parent] = [];
78-
}
79-
if(!in_array($class, $this->extends[$parent])){
80-
$this->extends[$parent][] = $class;
101+
102+
public function setClassMap(array $classMap){
103+
$this->classMap = $classMap;
104+
$this->flippedClassMap = array_flip($classMap);
105+
}
106+
protected function addExtend(ClassData $class, FQCN $parent){
107+
$this->findClassChildren($parent);
108+
$this->extends[$parent->toString()][$class->fqcn->toString()] = $class;
109+
$parentClass = $this->findClassByFQCN($parent);
110+
if($parentClass instanceof ClassData){
111+
$class->setParent($parentClass);
81112
}
82113
}
83114

84-
public function getImplements(){
85-
return $this->implements;
86-
}
87-
public function addImplement($class, $interface){
115+
protected function addImplement($class, $interface){
88116
if(!array_key_exists($interface, $this->implements)
89117
|| !is_array($this->implements[$interface])){
90118
$this->implements[$interface] = [];

0 commit comments

Comments
 (0)