PHP framework to support module autoload in CodeIgniter.
parent
c41a492f3b
commit
2550d31c8e
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -17,16 +17,146 @@ class DynamicLoader extends NodeVisitorAbstract
|
||||||
{
|
{
|
||||||
public $definitionCollector;
|
public $definitionCollector;
|
||||||
public $prettyPrinter;
|
public $prettyPrinter;
|
||||||
|
public $definitionResolver;
|
||||||
|
|
||||||
|
private $collectAutoload;
|
||||||
|
|
||||||
public function __construct(DefinitionCollector $definitionCollector)
|
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,26 +182,69 @@ 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);
|
||||||
$enityName = array_pop($entityParts);
|
$enityName = array_pop($entityParts);
|
||||||
$fieldName = $enityName;
|
$fieldName = $enityName;
|
||||||
|
|
||||||
// deal with case like: $this->_CI->load->model('users_mdl', 'hahaha');
|
// deal with case like: $this->_CI->load->model('users_mdl', 'hahaha');
|
||||||
if ($callNode->name = "model" && $nameNode !== NULL) {
|
if ($callNode->name = "model" && $nameNode !== NULL) {
|
||||||
if (!($nameNode instanceof Node\Scalar\String_)) {
|
if (!($nameNode instanceof Node\Scalar\String_)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue