Skip to content
This repository was archived by the owner on Jul 18, 2018. It is now read-only.

Commit 0bcac5d

Browse files
committed
PHP framework to support module autoload in CodeIgniter.
1 parent 126dbd8 commit 0bcac5d

File tree

4 files changed

+212
-7
lines changed

4 files changed

+212
-7
lines changed

src/DefinitionResolver.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ class DefinitionResolver
2626
*/
2727
private $prettyPrinter;
2828

29+
30+
public $autoloadInfo;
31+
32+
// The followings are autoloading properties.
33+
public $autoloadLibraries;
34+
public $autoloadClasses;
35+
public $autoloadModels;
36+
public $autoloadLanguages;
37+
38+
2939
/**
3040
* @param ReadableIndex $index
3141
*/

src/Indexer.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ public function index(): Promise
117117
/** @var string[][] */
118118
$deps = [];
119119

120+
$ending = "application/config/autoload.php";
121+
$endingLength = strlen($ending);
122+
foreach ($uris as $uri) {
123+
$found = (substr($uri, -$endingLength) === $ending);
124+
125+
// if found, put it to the beginning of the list so that it gets analyzed first.
126+
if ($found) {
127+
array_unshift($uris, $uri);
128+
break;
129+
}
130+
}
131+
120132
foreach ($uris as $uri) {
121133
$packageName = getPackageName($uri, $this->composerJson);
122134
if ($this->composerLock !== null && $packageName) {

src/NodeVisitor/DynamicLoader.php

Lines changed: 179 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,146 @@ class DynamicLoader extends NodeVisitorAbstract
1717
{
1818
public $definitionCollector;
1919
public $prettyPrinter;
20+
public $definitionResolver;
21+
22+
private $collectAutoload;
2023

21-
public function __construct(DefinitionCollector $definitionCollector)
24+
public function __construct(DefinitionCollector $definitionCollector, DefinitionResolver $definitionResolver, bool $collectAutoload)
2225
{
2326
$this->definitionCollector = $definitionCollector;
27+
$this->definitionResolver = $definitionResolver;
28+
$this->collectAutoload = $collectAutoload;
2429
$this->prettyPrinter = new PrettyPrinter;
2530
}
2631

32+
public function visitAutoloadClassDeclaration(Node $node) {
33+
if (!($node instanceof Node\Stmt\Class_)) {
34+
return;
35+
}
36+
37+
$extends = $node->extends;
38+
if (!isset($extends->parts)) {
39+
return;
40+
}
41+
$shouldAutoload = false;
42+
foreach ($extends->parts as $part) {
43+
// TODO: add more criteria here?
44+
if ($part === "CI_Controller" || $part === "ST_Controller" ||
45+
$part === "ST_Auth_Controller") {
46+
$shouldAutoload = true;
47+
break;
48+
}
49+
}
50+
51+
if (!$shouldAutoload) {
52+
return;
53+
}
54+
55+
if (isset($this->definitionResolver->autoloadLibraries)) {
56+
foreach ($this->definitionResolver->autoloadLibraries as $key => $value) {
57+
$this->createAutoloadDefinition($node, $value);
58+
}
59+
}
60+
61+
if (isset($this->definitionResolver->autoloadModels)) {
62+
foreach ($this->definitionResolver->autoloadModels as $key => $value) {
63+
$this->createAutoloadDefinition($node, $value);
64+
}
65+
}
66+
67+
if (isset($this->definitionResolver->autoloadHelpers)) {
68+
foreach ($this->definitionResolver->autoloadHelpers as $key => $value) {
69+
$this->createAutoloadDefinition($node, $value);
70+
}
71+
}
72+
73+
if (isset($this->definitionResolver->autoloadConfig)) {
74+
foreach ($this->definitionResolver->autoloadConfig as $key => $value) {
75+
$this->createAutoloadDefinition($node, $value);
76+
}
77+
}
78+
79+
if (isset($this->definitionResolver->autoloadLanguage)) {
80+
foreach ($this->definitionResolver->autoloadLanguage as $key => $value) {
81+
$this->createAutoloadDefinition($node, $value);
82+
}
83+
}
84+
}
85+
86+
public function visitAutoloadNode(Node $node) {
87+
// looking at array assignments.
88+
if (!($node instanceof Node\Expr\Assign)) {
89+
return;
90+
}
91+
92+
// check left hand side.
93+
$lhs = $node->var;
94+
if (!($lhs instanceof Node\Expr\ArrayDimFetch)) {
95+
return;
96+
}
97+
98+
$dimFetchVar = $lhs->var;
99+
if (!($dimFetchVar instanceof Node\Expr\Variable)) {
100+
return;
101+
}
102+
103+
if ($dimFetchVar->name !== "autoload") {
104+
return;
105+
}
106+
// end of checking left hand side.
107+
108+
$dim = $lhs->dim;
109+
if (!($dim instanceof Node\Scalar\String_)) {
110+
return;
111+
}
112+
// TODO: support more than libraries
113+
$target = $dim->value;
114+
115+
// extract right hand side.
116+
$rhs = $node->expr;
117+
if (!($rhs instanceof Node\Expr\Array_)) {
118+
return;
119+
}
120+
121+
// $target -> $node reference
122+
$arrayOfLibs = $rhs->items;
123+
foreach ($arrayOfLibs as $lib) {
124+
$libName = $lib->value->value;
125+
switch ($target) {
126+
case "libraries":
127+
$this->definitionResolver->autoloadLibraries[$libName] = $lib;
128+
break;
129+
case "helper":
130+
$this->definitionResolver->autoloadHelpers[$libName] = $lib;
131+
break;
132+
case "config":
133+
$this->definitionResolver->autoloadConfig[$libName] = $lib;
134+
break;
135+
case "model":
136+
$this->definitionResolver->autoloadModels[$libName] = $lib;
137+
break;
138+
case "language":
139+
$this->definitionResolver->autoloadLanguage[$libName] = $lib;
140+
break;
141+
}
142+
}
143+
144+
}
145+
27146
public function enterNode(Node $node)
28147
{
29-
// check its name is 'model'
148+
// handling autoloading.
149+
if ($this->collectAutoload) {
150+
// records autoloading fields into definition resolver.
151+
$this->visitAutoloadNode($node);
152+
}
153+
154+
// spits autoloading fields to a class that is derived from controller classes.
155+
$this->visitAutoloadClassDeclaration($node);
156+
157+
// The follwoing is for handling dynamic loading. (Finished)
158+
159+
// check its name is 'model', 'library' or 'helper'.
30160
if (!($node instanceof Node\Expr\MethodCall)) {
31161
return;
32162
}
@@ -52,26 +182,69 @@ public function enterNode(Node $node)
52182
if ($argSize == 2) {
53183
$nameNode = $node->args[1]->value;
54184
}
55-
$this->createDefintion($node, $node->args[0]->value, $nameNode);
185+
$this->createDefinition($node, $node->args[0]->value, $nameNode);
56186
} else if ($node->args[0]->value instanceof Node\Expr\Array_) {
57187
$elems = $node->args[0]->value->items;
58188
foreach ($elems as $item) {
59189
if ($item->value instanceof Node\Scalar\String_) {
60-
$this->createDefintion($node, $item->value, $nameNode);
190+
$this->createDefinition($node, $item->value, $nameNode);
61191
}
62192
}
63193
}
64194
}
65195

196+
// copied from createDefinition and tailored.
197+
public function createAutoloadDefinition(Node $classNode, Node $entityNode)
198+
{
199+
$fieldName = $entityNode->value->value;
200+
201+
$enclosedClass = $classNode;
202+
$classFqn = $enclosedClass->namespacedName->toString();
203+
$fqn = $classFqn . "->" . $fieldName;
204+
205+
// if we cannot find definition, just return.
206+
if ($fqn === NULL) {
207+
return;
208+
}
209+
210+
// add fqn to nodes and definitions.
211+
$this->definitionCollector->nodes[$fqn] = $entityNode;
212+
213+
// Create symbol
214+
// $classFqnParts = preg_split('/(::|->|\\\\)/', $fqn);
215+
// array_pop($classFqnParts);
216+
// $classFqn = implode('\\', $classFqnParts);
217+
$sym = new SymbolInformation($fieldName, SymbolKind::PROPERTY, Location::fromNode($entityNode), $classFqn);
218+
219+
// Create type
220+
// array_push($entityParts, ucwords($enityName));
221+
// $typeName = implode('\\', $entityParts);
222+
$typeName = ucwords($fieldName);
223+
$type = new Types\Object_(new Fqsen('\\' . $typeName));
224+
225+
// Create defintion from symbol, type and all others
226+
$def = new Definition;
227+
$def->canBeInstantiated = false;
228+
$def->isGlobal = false; // TODO check the meaning of this, why public field has this set to false?
229+
$def->isStatic = false; // it should not be a static field
230+
$def->fqn = $fqn;
231+
$def->symbolInformation = $sym;
232+
$def->type = $type;
233+
// Maybe this is not the best
234+
$def->declarationLine = $fieldName; // $this->prettyPrinter->prettyPrint([$argNode]);
235+
$def->documentation = "Dynamically Generated Field: " . $fieldName;
236+
237+
$this->definitionCollector->definitions[$fqn] = $def;
238+
}
66239

67-
public function createDefintion($callNode, $entityNode, $nameNode)
240+
public function createDefinition($callNode, $entityNode, $nameNode)
68241
{
69242
$entityString = $entityNode->value;
70243
$entityParts = explode('/', $entityString);
71244
$enityName = array_pop($entityParts);
72245
$fieldName = $enityName;
73246

74-
// deal with case like: $this->_CI->load->model('users_mdl', 'hahaha');
247+
// deal with case like: $this->_CI->load->model('users_mdl', 'hahaha');
75248
if ($callNode->name = "model" && $nameNode !== NULL) {
76249
if (!($nameNode instanceof Node\Scalar\String_)) {
77250
return;

src/PhpDocument.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ public function __construct(
120120
$this->updateContent($content);
121121
}
122122

123+
private function isVisitingAutoload() {
124+
return false;
125+
}
126+
123127
/**
124128
* Get all references of a fully qualified name
125129
*
@@ -169,6 +173,12 @@ public function updateContent(string $content)
169173
$this->diagnostics[] = Diagnostic::fromError($error, $this->content, DiagnosticSeverity::ERROR, 'php');
170174
}
171175

176+
// figure out if it is analyzing an autoload file.
177+
$isAutoload = false;
178+
$ending = "application/config/autoload.php";
179+
$endingLength = strlen($ending);
180+
$isAutoload = (substr($this->uri, -$endingLength) === $ending);
181+
172182
// $stmts can be null in case of a fatal parsing error
173183
if ($stmts) {
174184
$traverser = new NodeTraverser;
@@ -199,7 +209,7 @@ public function updateContent(string $content)
199209
$definitionCollector = new DefinitionCollector($this->definitionResolver);
200210
$traverser->addVisitor($definitionCollector);
201211

202-
$traverser->addVisitor(new DynamicLoader($definitionCollector));
212+
$traverser->addVisitor(new DynamicLoader($definitionCollector, $this->definitionResolver, $isAutoload));
203213

204214
// Collect all references
205215
$referencesCollector = new ReferencesCollector($this->definitionResolver);

0 commit comments

Comments
 (0)