On Fri, Jul 5, 2024 at 1:29 AM Mike Schinkel <m...@newclarity.net> wrote:

> On Jul 4, 2024, at 9:16 PM, Michael Morris <tendo...@gmail.com> wrote:
> On Wed, Jul 3, 2024 at 11:11 PM Mike Schinkel <m...@newclarity.net> wrote:
>
>> So I've had more time to mull this over, and some research, and I think I
>> have an approach.
>
>
> First, instead of 'import', use 'require_module'.  The parsing rules for
> require_module differ from require how the file is parsed, a subject for
> another time.  Also, it's parallel to what is to follow.
>
>
> +1
>
> Speaking of new functions, let's start with these
>
>   spl_set_include_ini_map('importmap.ini');
>   spl_set_include_json_map('importmap.json")
>
>
> Those are a mouthful!
>
> The json file is pretty much identical to the JavaScript importmaps. The
> ini file looks like this
>
>   root = "/absolute/path/to/application/root"
>
>   [imports]
>   square = "./path/to/square.js"
>   circle = "./path/to/circle.js"
>   other/ = "./path/to/other/"
>
>   [scopes]
>   \A[square] = './path/to/square/in/namespace/A/a.js'
>
>
> I assume rather than `.js` you mean `.php` files in your example?
>
> Also, I am not following how these imports and scopes will relate to the
> actual PHP code that would be affected/using packages.
>
> Whichever format is used is a matter of personal preference. The file can
> be, and likely should be, written by composer or some future package
> manager.
>
>
> Part of me likes the flexibility of two formats. The other more pragmatic
> part of me says stick with one format for fewer related bugs and to reduce
> the effort to support it for internal code and by 3rd parties.
>
> Unless it can be fully cached by opcache I would think it would need to be
> the format that can be parsed the fastest, which could be binary like a
> Protobuf file.
>
> The root attribute in the map sets the root for all relative paths given
> in the map.
>
>
> Are you saying that a publisher of a package would need to write the
> absolute path to their `importmap.*` file. How will that work?  Even for a
> bespoke app Composer may not have enough access to the server to know this,
> and 3rd party packages will definitely not know it for their users. At
> least I don't think so.
>
> Also, at this point trying to keep track of all your ideas is impossible,
> at least for me. Have you reconsidered putting it in a repo or at least a
> Gist yet so it is easier to see the scope of your current ideas about
> packages?
>
>
I'm getting there. I'm trying to boil things down at this point to
something that can be put into such.

I went to sleep thinking about this post, on import maps in general and how
Composer works, specifically when you use a class map instead of the PSR-0
or PSR-4 schemes.  In that mode, Composer does pretty much what I've
described.  This got me to thinking, could setting an import map be an
alternative to setting an autoload function?  Would having the PHP runtime
load the file be faster than mucking with some userland code to do the
same? And would the engine be able to do anything that can't be done in
userland?  I think so.

So first, I agree that supporting two formats, while convenient, increases
the maintenance burden, so let's just go with ini.  As far as the
installing function - a better name is this.

spl_autoload_map( string $filepath );

This function will register an autoload map wrapped with an internal
autoloader to work with it.  If called multiple times the maps will resolve
in the order they are called.  If called along with the existing
spl_autoload_register function then the maps and autoload functions will be
used in the order of their declaration. Packages are expected to carry
their own autoload maps.  Autoload maps can only be loaded once - attempts
to load the same map multiple times will raise a E_USER_WARNING

The contents of the file along with comments as to what they do.

  ; The root directive affects the relative paths in the map.  If set the
location specified becomes
  ; the root. If not set all relative paths are relative to THIS FILE.
  ;root = '/some/path'

  ; If a package is declared, that is the root namespace for all the
includes that follow.
  ; As this example is for a root autoloader it is commented out, as the
root autoload function of
  ; an app doesn't need a package name.
  ;package =

  ; A map relates symbols to files. This one is for the existing
include/require system. Actual
  ; file loading is performed by require_once.
  [includes]
  A\B\Cat = './path/to/Cat.php'

  ; A symbol can invoke the loading of multiple files. This will be useful
if the package manager that
  ; prepares this file determines that a polyfill will be needed. Files
will be loaded in order given.
  A\TestClass[] = './path/to/polyfill.php'
  A\TestClass[] = './path/to/TestClass.php'

  ; A wildcard can be used if there is a desire to pack multiple symbols
together
  ; Note the path with the most specific match wins here, so \A\foo() will
invoke the autoload below,
  ; but \A\TestClass or \A\B\Cat will trigger their respective definitions
above.
  A\* = './path/to/A/Namespaces/functions.php'

  ; A path fragment can be used, in which case PSR-4 will be used to map
the rest of the symbol to the filename.
  ; Pay attention to the direction of the slash at the tail - if the symbol
key has this the value MUST also have this.
  B/ = './path/to/B/'

  ; A package is declared with a @ and maps the package namespace to its
autoload file.
  ; If the package name here doesn't match what the package calls itself
then the symbol
  ; given here takes precedence, acting as an alias.
  @C = './path/to/C/autoload.ini'

  ; An import into a package can be done like so
  ; Twig will load into \C\Twig and that use will need to be used by any
code outside the C package.
  @C\Twig/ = './path/to/Twig/'

  ; The same library can be loaded into a different package, but a symbolic
link is used internally in the engine to optimize
  @D\Twig/ = './path/to/Twig/'

  ; Nothing stops a different package from loading a different version now.
  @E\Twig/ = './path/to/Twig/Version4/'

  ; Modules are loaded with require_module but their maps work the same.
  [modules]



And, honestly, I think that's it. This autoloader has some tricks up its
sleeve a userland autoloader does not
1) Can deal with functions and constants.
2) Can deal with different versions of the same package being loaded.
3) Should be able to run faster than its equivalent userland code.

As can be inferred from the above, a full autoload map can get complex in a
hurry.  However, it is also meant to be created by Composer or another
package manager at some point.  The advantage is a project like WordPress
can ship with this autoload map and not require the end user to install
composer unless they want to create their own.  Any plugins can register
their own autoloaders to handle their needs.

Reply via email to