Not replying to anyone in particular and instead doing a mild reset taking into account the discussion that has gone before.
So, I want to import a package. I'll create an index.php file at the root of my website and populate it with this. <?php import "./src/mymodule"; Now I'll create that directory and run a command `php mod init` in that directory. Stealing this from Go, it's fairly straightforward though. Now if we look in the directory we will see two files. php.mod php.sum The second file I'll not be touching on but exists to track checksums of downloaded packages - Composer does the same with its composer-lock.json file which in turn was inspired by node's package-lock.json. The php.mod file stands in for composer.json, but it isn't a json file. It would start something like this: namespace mymodule php 10.0 registry packagist.org/packages We start with three directives - the root namespace is presumed to be the directory name. If that isn't true this is a text file, change it. PHP min version should be straightforward. Registry details where we are going to go get code from. Suppose we want to use our own registry but fallback to packagist. That would be this: namespace mymodule php 10.0 registry ( github.com/myaccount packagist.org/packages ) Multiple registry entries will be checked for the code in order. Handling auth tokens for restricted registries is outside of scope at the moment. So let's build the module. We'll make a file called hello.phm. The reason for phm and not php is so that web SAPIs will not try to parse this code. Further they can be configured to not even allow direct https access to these files at all. import "twig/twig"; use \Twig\Loader\ArrayLoader; use \Twig\Environment; $loader = new ArrayLoader([ 'index' => 'Hello {{ name }}' ]); $twig = new Environment($loader); export $twig; As mentioned in previous discussions, modules have their own variable scope. Back in our index we need to receive the variable <?php import $twig from "./src/mymodule" $twig->render('index', ['name' => 'World']); If we load index.php in the web browser we should see "Hello World". If we look back in the mymodules folder we'll see the php.mod file has been updated namespace mymodule php 10.0 registry packagist.org/packages imports ( twig/twig v3.10.3 symfony/deprecation-contracts v2.5 //indirect symfony/polyfill-mbstring v1.3 //indirect symfony/polyfill-php80 v1.22 //indirect ) Note the automatically entered comment that marks the imported dependencies of twig. Meanwhile the php.sum file will also be updated with the checksums of these packages. So why this instead of composer? Well, a native implementation should be faster, but also it might be able to deal with php extensions. import "@php_mysqli" The @ marks that the extension is either a .so or .dll library, as I'll hazard a guess that the resolution mechanic will be radically different from the php language modules themselves - if it is possible at all. If it can be done it will make working with packages that require extensions a hell of a lot easier since it will no longer be necessary to monkey the php.ini file to include them. At a minimum the parser needs to know that the import will not be in the registry and instead it should look to the extensions directory, hence the lead @. Speaking of, having the extension directory location be a directive of php.mod makes sense here. Each module can have its own extension directory, but if this is kept within the project instead of globally then web SAPIs definitely need to stay out of those directories. Final thing to touch on is how the module namespaces behave. The export statement is used to call out what is leaving the module - everything else is private to that module. class A {} // private export class B {} // public All the files of the package effectively have the same starting namespace - whatever was declared in php.mod. So it isn't necessary to repeat the namespace on each file of the package. If a namespace is given, it will be a sub-namespace namespace tests; export function foo() {} Then in the importing file import "./src/mymodule" use \mymodule\tests\foo Notice here that if there is no from clause everything in the module grafts onto the symbol table. Subsequent file loads need only use the use statement. Exported variables however must be explicitly pulled because the variable symbol table isn't affected by namespaces (if I recall correctly, call me an idiot if I'm wrong). The from clause is useful for permanently aliasing - if something is imported under an alias it will remain under that alias. Continuing the prior example import tests\foo as boo from "./src/mymodule"; boo() That's enough to chew on I think.