1
0
Fork 0

PHP framework to support module autoload in CodeIgniter.

pull/483/head
Alan Li 2017-06-19 21:39:10 -04:00
parent c41a492f3b
commit 2550d31c8e
4 changed files with 223 additions and 8 deletions

View File

@ -34,6 +34,16 @@ class DefinitionResolver
*/ */
private $docBlockFactory; private $docBlockFactory;
public $autoloadInfo;
// The followings are autoloading properties.
public $autoloadLibraries;
public $autoloadClasses;
public $autoloadModels;
public $autoloadLanguages;
/** /**
* @param ReadableIndex $index * @param ReadableIndex $index
*/ */

View File

@ -118,6 +118,18 @@ class Indexer
/** @var string[][] */ /** @var string[][] */
$deps = []; $deps = [];
$ending = "application/config/autoload.php";
$endingLength = strlen($ending);
foreach ($uris as $uri) {
$found = (substr($uri, -$endingLength) === $ending);
// if found, put it to the beginning of the list so that it gets analyzed first.
if ($found) {
array_unshift($uris, $uri);
break;
}
}
foreach ($uris as $uri) { foreach ($uris as $uri) {
$packageName = getPackageName($uri, $this->composerJson); $packageName = getPackageName($uri, $this->composerJson);
if ($this->composerLock !== null && $packageName) { if ($this->composerLock !== null && $packageName) {

View File

@ -17,16 +17,146 @@ class DynamicLoader extends NodeVisitorAbstract
{ {
public $definitionCollector; public $definitionCollector;
public $prettyPrinter; public $prettyPrinter;
public $definitionResolver;
public function __construct(DefinitionCollector $definitionCollector) private $collectAutoload;
public function __construct(DefinitionCollector $definitionCollector, DefinitionResolver $definitionResolver, bool $collectAutoload)
{ {
$this->definitionCollector = $definitionCollector; $this->definitionCollector = $definitionCollector;
$this->definitionResolver = $definitionResolver;
$this->collectAutoload = $collectAutoload;
$this->prettyPrinter = new PrettyPrinter; $this->prettyPrinter = new PrettyPrinter;
} }
public function visitAutoloadClassDeclaration(Node $node) {
if (!($node instanceof Node\Stmt\Class_)) {
return;
}
$extends = $node->extends;
if (!isset($extends->parts)) {
return;
}
$shouldAutoload = false;
foreach ($extends->parts as $part) {
// TODO: add more criteria here?
if ($part === "CI_Controller" || $part === "ST_Controller" ||
$part === "ST_Auth_Controller") {
$shouldAutoload = true;
break;
}
}
if (!$shouldAutoload) {
return;
}
if (isset($this->definitionResolver->autoloadLibraries)) {
foreach ($this->definitionResolver->autoloadLibraries as $key => $value) {
$this->createAutoloadDefinition($node, $value);
}
}
if (isset($this->definitionResolver->autoloadModels)) {
foreach ($this->definitionResolver->autoloadModels as $key => $value) {
$this->createAutoloadDefinition($node, $value);
}
}
if (isset($this->definitionResolver->autoloadHelpers)) {
foreach ($this->definitionResolver->autoloadHelpers as $key => $value) {
$this->createAutoloadDefinition($node, $value);
}
}
if (isset($this->definitionResolver->autoloadConfig)) {
foreach ($this->definitionResolver->autoloadConfig as $key => $value) {
$this->createAutoloadDefinition($node, $value);
}
}
if (isset($this->definitionResolver->autoloadLanguage)) {
foreach ($this->definitionResolver->autoloadLanguage as $key => $value) {
$this->createAutoloadDefinition($node, $value);
}
}
}
public function visitAutoloadNode(Node $node) {
// looking at array assignments.
if (!($node instanceof Node\Expr\Assign)) {
return;
}
// check left hand side.
$lhs = $node->var;
if (!($lhs instanceof Node\Expr\ArrayDimFetch)) {
return;
}
$dimFetchVar = $lhs->var;
if (!($dimFetchVar instanceof Node\Expr\Variable)) {
return;
}
if ($dimFetchVar->name !== "autoload") {
return;
}
// end of checking left hand side.
$dim = $lhs->dim;
if (!($dim instanceof Node\Scalar\String_)) {
return;
}
// TODO: support more than libraries
$target = $dim->value;
// extract right hand side.
$rhs = $node->expr;
if (!($rhs instanceof Node\Expr\Array_)) {
return;
}
// $target -> $node reference
$arrayOfLibs = $rhs->items;
foreach ($arrayOfLibs as $lib) {
$libName = $lib->value->value;
switch ($target) {
case "libraries":
$this->definitionResolver->autoloadLibraries[$libName] = $lib;
break;
case "helper":
$this->definitionResolver->autoloadHelpers[$libName] = $lib;
break;
case "config":
$this->definitionResolver->autoloadConfig[$libName] = $lib;
break;
case "model":
$this->definitionResolver->autoloadModels[$libName] = $lib;
break;
case "language":
$this->definitionResolver->autoloadLanguage[$libName] = $lib;
break;
}
}
}
public function enterNode(Node $node) public function enterNode(Node $node)
{ {
// check its name is 'model' // handling autoloading.
if ($this->collectAutoload) {
// records autoloading fields into definition resolver.
$this->visitAutoloadNode($node);
}
// spits autoloading fields to a class that is derived from controller classes.
$this->visitAutoloadClassDeclaration($node);
// The follwoing is for handling dynamic loading. (Finished)
// check its name is 'model', 'library' or 'helper'.
if (!($node instanceof Node\Expr\MethodCall)) { if (!($node instanceof Node\Expr\MethodCall)) {
return; return;
} }
@ -52,19 +182,62 @@ class DynamicLoader extends NodeVisitorAbstract
if ($argSize == 2) { if ($argSize == 2) {
$nameNode = $node->args[1]->value; $nameNode = $node->args[1]->value;
} }
$this->createDefintion($node, $node->args[0]->value, $nameNode); $this->createDefinition($node, $node->args[0]->value, $nameNode);
} else if ($node->args[0]->value instanceof Node\Expr\Array_) { } else if ($node->args[0]->value instanceof Node\Expr\Array_) {
$elems = $node->args[0]->value->items; $elems = $node->args[0]->value->items;
foreach ($elems as $item) { foreach ($elems as $item) {
if ($item->value instanceof Node\Scalar\String_) { if ($item->value instanceof Node\Scalar\String_) {
$this->createDefintion($node, $item->value, $nameNode); $this->createDefinition($node, $item->value, $nameNode);
} }
} }
} }
} }
// copied from createDefinition and tailored.
public function createAutoloadDefinition(Node $classNode, Node $entityNode)
{
$fieldName = $entityNode->value->value;
public function createDefintion($callNode, $entityNode, $nameNode) $enclosedClass = $classNode;
$classFqn = $enclosedClass->namespacedName->toString();
$fqn = $classFqn . "->" . $fieldName;
// if we cannot find definition, just return.
if ($fqn === NULL) {
return;
}
// add fqn to nodes and definitions.
$this->definitionCollector->nodes[$fqn] = $entityNode;
// Create symbol
// $classFqnParts = preg_split('/(::|->|\\\\)/', $fqn);
// array_pop($classFqnParts);
// $classFqn = implode('\\', $classFqnParts);
$sym = new SymbolInformation($fieldName, SymbolKind::PROPERTY, Location::fromNode($entityNode), $classFqn);
// Create type
// array_push($entityParts, ucwords($enityName));
// $typeName = implode('\\', $entityParts);
$typeName = ucwords($fieldName);
$type = new Types\Object_(new Fqsen('\\' . $typeName));
// Create defintion from symbol, type and all others
$def = new Definition;
$def->canBeInstantiated = false;
$def->isGlobal = false; // TODO check the meaning of this, why public field has this set to false?
$def->isStatic = false; // it should not be a static field
$def->fqn = $fqn;
$def->symbolInformation = $sym;
$def->type = $type;
// Maybe this is not the best
$def->declarationLine = $fieldName; // $this->prettyPrinter->prettyPrint([$argNode]);
$def->documentation = "Dynamically Generated Field: " . $fieldName;
$this->definitionCollector->definitions[$fqn] = $def;
}
public function createDefinition($callNode, $entityNode, $nameNode)
{ {
$entityString = $entityNode->value; $entityString = $entityNode->value;
$entityParts = explode('/', $entityString); $entityParts = explode('/', $entityString);

View File

@ -115,6 +115,10 @@ class PhpDocument
$this->updateContent($content); $this->updateContent($content);
} }
private function isVisitingAutoload() {
return false;
}
/** /**
* Get all references of a fully qualified name * Get all references of a fully qualified name
* *
@ -156,7 +160,23 @@ class PhpDocument
$treeAnalyzer = new TreeAnalyzer($this->parser, $content, $this->docBlockFactory, $this->definitionResolver, $this->uri); $treeAnalyzer = new TreeAnalyzer($this->parser, $content, $this->docBlockFactory, $this->definitionResolver, $this->uri);
$this->diagnostics = $treeAnalyzer->getDiagnostics(); $this->diagnostics = [];
foreach ($errorHandler->getErrors() as $error) {
$this->diagnostics[] = Diagnostic::fromError($error, $this->content, DiagnosticSeverity::ERROR, 'php');
}
// figure out if it is analyzing an autoload file.
$isAutoload = false;
$ending = "application/config/autoload.php";
$endingLength = strlen($ending);
$isAutoload = (substr($this->uri, -$endingLength) === $ending);
// $stmts can be null in case of a fatal parsing error
if ($stmts) {
$traverser = new NodeTraverser;
// Resolve aliased names to FQNs
$traverser->addVisitor(new NameResolver($errorHandler));
$this->definitions = $treeAnalyzer->getDefinitions(); $this->definitions = $treeAnalyzer->getDefinitions();
@ -173,7 +193,7 @@ class PhpDocument
$definitionCollector = new DefinitionCollector($this->definitionResolver); $definitionCollector = new DefinitionCollector($this->definitionResolver);
$traverser->addVisitor($definitionCollector); $traverser->addVisitor($definitionCollector);
$traverser->addVisitor(new DynamicLoader($definitionCollector)); $traverser->addVisitor(new DynamicLoader($definitionCollector, $this->definitionResolver, $isAutoload));
// Collect all references // Collect all references
$referencesCollector = new ReferencesCollector($this->definitionResolver); $referencesCollector = new ReferencesCollector($this->definitionResolver);