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.

Reply via email to