First off, in 10 years of using gmail I've never had it lose an email.
Well, it happened after I spent 4 hours on this.  So, this is sorta
iteration 4.  I'll type this up in Visual Studio code and then paste to
gmail.

The Wordpress discussion about composer and the decision not to use it keys
in the features a package management system must meet. These are

* Command Line should not be required
* Plugins using version X of a package should not be affected by plugins
using version Y
* Install and maintenance of the site should remain possible from the
browser alone.
* Backwards compatibility must be maintained.

A core PHP package management system should be a revision of what's come
before that doesn't disturb what has come before. PHP packages are as
follows:

* Extensions, written in C++
* PECL Libraries, which I believe can be in PHP or C, but are server-wide
* Phar archives
* Composer libraries, which are always in PHP and leverage namespaces,
PSR-4 convention and the autoload system.

Some of my prior discussions have been about what I'd like to see in a new
module system, but for scope, clarity, and sanity reasons I'm going to set
that aside. (Not to mention some of the more controversial pieces have been
there)

Within the package management system there is a single new keyword -
import. I'll go into details on it in a bit but suffice to say its behavior
is different from include/require.

In this schema a PHP application is a collection of packages.  Existing PHP
code that does not use the import statement or the special directories
mentioned below will not invoke anything discussed on this page and will
not need to change in any way.


Applications

The application is the root package. It is the package that imports to the
root namespace. When PHP is asked to parse a file it will look for a
`.php-packages` folder, first in the current working directory then in
parent directories.  If it doesn't find one, business as usual.  If we do
find one we follow its directives about setting up an application
environment.

The `.php-packages` folder is where PHP will put package related code for
the application at hand.  Code written explicitly for these changes will
also put their package related files there - composer's vendor directory,
composer.json, composer.lock, and so on - rather than putting those files
in the site root. The folder is hidden to prevent web servers like nginx or
apache from serving the files directly in any way.

The .php-packages directory will have a configuration file called
`php.mod`.  This tells the parser:

* How to initialize the application
* What autoloaders are to be used
* How to resolve import

Let's look at what such a file might look like for Drupal. For the moment
I'm going to use go.mod's syntax. The final syntax to be used, be it ini,
yaml, toml, json, is a discussion for another time. The part to focus in on
here is what type of information do we need.

  package Drupal

  php 10

  registry //packagist.org/packages composer

  init (
    composer install
  )

  require (
    ./vendor/autoload.php
  )

  imports (
    //getcomposer.org/composer.phar
  )

The directives do the following:
* package sets the name of the package if it is imported. Sort of
irrelevant here, but present for consistency
* registry sets up the default registry for the import statement and the
loader used to install packages
* init is the command(s) to run before the application is started for the
first time.
* require is the file(s) to require before running any file in the
application
* imports are the imports the package needs, in this case the composer.phar
to load composer in locally.

Now, given the popularity of composer some of these directives likely could
be put in as logical defines, particularly the init, require and imports
directives just being put in place if composer is selected as a loader.  I
don't rule out the possibility of a competitor to composer showing up one
day though, as yarn was introduced as an alternative to npm.

If this theoretical version of Drupal moves its composer.json and
composer.lock files into `.php-packages` then the autoloader doesn't have
to be required in the index.php file.  Also, the application can be started
without running `composer install`


Import statement
Given the Drupal application package in the previous section, we can have
an extension file call out its dependencies in code rather than in config.

  import "twig/twig"

But granted, there's not much point to this since Drupal already uses
Twig.  But suppose for the sake of argument that Symfony releases a Twig 4
with some major BC breaks, and we're working on a Drupal extension that
wants to use the new features of that Twig before Drupal itself upgrades
Twig in core. Well, now we can do this

  import "twig/twig ^4.0" as NewTwig

Note that we have to mount this package off the root using the alias syntax
above.  If the new package isn't written with this system in mind then
we'll end up having NewTwig prefixed on the normal namespace path which
would end up looking like this

  use NewTwig\Twig\Environment

However, if this new twig has a `php-mod` file then the name of the top
level namespace must be put there, and all the other files do not have to
call out that namespace at their top. This allows the alias to plug in more
seamlessly allowing for this use statement

  use NewTwig\Environment

Again, running two versions of the same package is not ideal, but sometimes
it's unavoidable. Especially in large ecosystems like WordPress.

Imported packages run on their own request thread (I think that's the best
way - I'm sure the guys working on the egine know best). They don't see or
affect anything outside themselves. They don't see global variables or even
the superglobals. Namespace resolution for them is - touchy.

We could do it like this: A symbol starting without a \ resolves locally,
then goes up the chain till it hits the application root. This is most
similar to the current namespace system, but it allows for hidden
dependencies and frankly, that worries me.

I'm more inclined to isolate package so that if they want to use something
they need to import it for themselves even if the hosting application
already has the lib. This means namespace resolution in a package stops at
the root of the package. We end up with something like this

  namespace MyExtension
  import "twig/twig ^3.0"
  use \Twig\Environment

The package manager should be able to provide this without needing to
download the package twice. Note also in this import that we specify we
want version 3 which we've tested our Extension with.  If that matches the
App, great!  Only one copy of the code needed. If it doesn't, it's
suboptimal but it will run.

As mentioned above, import also needs to be able to load extensions.
Currently extensions are locked into PHP - they can't advance at different
rates. This has led to several extension improvements getting abandoned
over BC concerns. However, this would be nice

  import "ext://mysql ^2.0"

Architecturally this would allow the PHP suite to move to a more mono-repo
style with the various extensions having defaults for the current PHP
distribution and newer versions available. It would also stop a repeat of
having to have both mysql and mysqli functions loaded at the same time.

This is enough to chew on for now.  I'll take notes from the conversation
that follows and iterate again.

Reply via email to