During the processing of any .php
OR .uniter.php
file, the first-found composer.lock
file will be loaded. It will be used to create a map of namespaces and files, allowing us to emulate the autoloader during the static analysis.
- Look for
use
statements. - Look for
new
statements.
For a brief example, consider the following:
<?php
use Foo\Application;
use Exception;
use App\Controller\MainController;
$app = new Application(new MainController);
We would find out that this file uses:
\Foo\Application
\Exception
\App\Controller\MainController
Since \Exception
is internal, we'd skip it.
For the other two, we'd look at the collected composer information, and resolve the files:
.../vendor/Foo/src/Application.php
./app/Controller/MainController.php
We'd further process these files, and repeat.
This should result in a top level dependency graph. That one should allow us to make basic programs work. In order to make these namespaces available, we'd create a map from namespace to module.
var exampleMap = {
"\\Foo\\Application": require("..."),
"\\App\\Controller\\MainController": require("...")
}
Next, the second level of dependencies, would be tracked by the require and include statements. Those are to be resolved relative to the files. But we'd replace the actual call.
Before:
<?php
include "./foo.php";
After:
<?php
__webpack_uniter_require__("./foo.php");
Usually, we'd resolve all the files through webpack. This could possibly be the definition of the function above:
var filemap = {
"/absolute/path/to/foo.php": require(...)
}
function __webpack_uniter_require__(moduleId, once) {
if(once) {
// Reset module - aka. remove result from cache and force re-run.
if(typeof require.cache[moduleId] != "undefined") {
delete require.cache[moduleId];
}
}
return require(moduleId);
}
WebPack allows us to handily inject plugins into it's parser, so we can find each instance of a specific function, and re-write it.
To shaddow the local paths used during compilations, we could also replace the paths by IDs.
"./foo.php" -> /some/path/foo.php -> id(1)
This would turn:
include "./foo.php";
to:
__wepback_uniter_require__("./foo.php");
and ultimatively to:
__webpack_uniter_require__(1);
- WebPack's module loader caches modules. In order to use
*_once()
style functions, we'd have to purge the file off the cache. Not impossible at all, but something to keep in mind. - Creating the module index will require a WebPack plugin, in order to obtain numbers of modules. WebPack modules are horribly documented, so this will be a challenge.
- A tool to scan and edit the Uniter AST will have to be made.
- A WebPack loader is configured for
.php
files. - It receives a request for such a file.
- First, the file is parsed.
- The generated AST will be searched for dependencies.
require_once/include_once
statements will be re-written as__webpack_uniter_require__(moduleId, once)
. - Then, the dependencies will become resolved. The responsible JS maps will be made and populated.
- First we insert the dependency code and add it as WebPack dependencies.
- Then we return the generated JS.