> On Jun 29, 2024, at 2:32 AM, Michael Morris <[email protected]> wrote:
>
> 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 <http://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 <http://github.com/myaccount>
> packagist.org/packages <http://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.
That is very Go-like, as you stated.
However, be aware that in a Go project repo you are likely to have only one
`go.mod` — or multiple if you have numerous CLI apps being generated — whereas
every directory with Go code is a package (which I think is equivalent to what
you are calling "module."
So I think your use of them here is conflating the two concepts. One is a
project-wide concept and the other is a "package" concept.
Maybe you would be better to adopt `module` to mean project and `package` to
mean packaged code as Go has them?
From here on I will refer to directory rather than module or package to avoid
confusion. By directory I will mean what Go calls a "package" and what I think
your original proposal called a "module."
A big difference between Go and PHP is that Go have a compiler that compiles
into an executable before it runs. That is clearly not compatible with PHP, and
why I was proposing that each directory could have a pre-compiled `.php.module`
that could be pre-compiled, or compiled on the fly at first import.
Also, it is problematic to have `php.mod` and `php.sum` because web servers
would serve them if not carefully configured hence why I went with a leading
dot, e.g. `.php.module`
>
> 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']);
Aside from being familiar per Javascript, what is the argument to requiring the
import of specific symbols vs just a package import, e.g.:
<?php
import "./src/mymodule"
mymodule->twig->render('index', ['name' => 'World']);
To me is seems to just add to boilerplate required. Note that having
`mymodule` everywhere you reference `twig` makes code a lot more
self-documenting, especially on line 999 of a PHP file. 🙂
>
> 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 <http://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
> )
Having a `php.sum` file is interesting but again, it should start with a period
if so.
That said, I wonder if incorporating versioning does not make the scope of
modules too big to complete?
> 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"
I would like this, but I think hosting vendors would block it since extensions
can have C bugs and create vulnerabilities for servers.
I have long thought PHP should kick off a new type of extension using WASM,
which can be sandboxed.
But I digress.
>
> 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.
I don't think it is wise to intertwine this concept of modules with namespaces
like that, but I am replied out for the night. :-)
-Mike