On Sat, Jul 6, 2024 at 2:12 AM Mike Schinkel <m...@newclarity.net> wrote:

> On Jul 5, 2024, at 1:47 PM, Michael Morris <tendo...@gmail.com> wrote:
> 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.
>
>
> I very much like this direction of thought.
>
> For context, when I worked with WordPress for about a decade I only ever
> used only Composer for websites but never for my own plugins, and I almost
> never used namespaces nor PSR-4 autoloaders for anything except when a
> plugin used them.  I almost exclusively used naming convensions for
> "namespacing" and classmaps for autoloading.
>

Some context from where I'm coming from.  I have been working exclusively
in React, NodeJS and Go up till about a year ago, and in Drupal before that
- it being 10 years since the last time I looked at WordPress. I need work
though, and I was offered a job where about two-thirds of my workload is a
massive WordPress site that has grown well outside the scope WordPress does
best.  So I've been getting used to it again and added some ulcers along
the way.


> Obviously I agree with having only one format, but not sure I concur with
> the use of .ini. However, me debating against `.ini` would be me
> bikeshedding and thus I will demure here.
>

I'm not married to ini either, but it will work for illustration. Adopting
another format will be one of the first decisions to make come
implementation.


> <epiphany>
> ...
> Many (most?) people on PHP Internals view WordPress coding standards as
> bad and some even view addressing WordPress developers needs as bad for
> PHP....
>

I really don't want to get into that crossfire.  WordPress is the 800lb.
gorilla of the PHP app world having more server market share that is
dominant - how dominant depends on who you ask. The most conservative
estimate I've seen is about half of PHP sites are running WordPress, and
the most pro-WP quote I saw claimed it has around 80% share.



>   ; 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/'
>
>
> It is not clear to me what a trailing slash means, and especially why it
> is needed on the left-hand side?
>

This is taken from the JavaScript importmap specification, but it exists to
cordon off direct file includes from directory includes. Original spec
here:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap


> And why slash here when namespaces use backslash?
>

Because it's the end of a path and unless you're on Windows directories are
delimited with /. They aren't the same thing


>
> Also, as someone raised on DOS and then Windows only "converting" in 2009,
> I still get confused in *nix when to use a trailing slash and when to not,
> so this trailing slash worries me, if only for that reason alone.
>

I understand.  Here it's to call out that the key references a directory,
not a file. The value must explicitly be a directory, not a file, and a
trailing slash is the way that is done.  Files are not *required* to have
extensions after all.



>
> Using the `@` here feels cryptic, and hard to discover and remember.
>

Perhaps, but this file is meant to be assembled by composer or some other
automated means - not by hand.  @ as a package operator is used in Node.js
to mark package namespaces - essentially the vendor of the package is
marked with @.


>
> I think this would be infinitely easier to follow if packages were just
> included in a `[packages]` section.
>

Packages will need to have some special handling though.  And the questions
I'm getting make that point increasingly clear.

If, as Larry Gafield has stated, the engine cannot be made to maintain
multiple symbol tables, then the packages must be bound onto the master
symbol table.  This presents a problem if multiple packages with different
versions are to be installed. One way to resolve this is some chicanery.
If PHP is instructed to install a file as a "package" it could load it onto
the symbol table in an auto-generated namespace - say \pkg_hash\Twig - and
then symbolic link to the package from other namespaces that are
referencing it.  Packages can (must) include other packages as their
dependencies.  If the symlinks are done right the end programmers do not
need to concern themselves with this implementation detail.

But the legacy code in the package itself needs to believe it is where it
expects to be - on root.  When a Twig file calls to another Twig file it
calls `use Twig\Class` in some form or another.

Monkey patching such as what happens in
https://packagist.org/packages/brianhenryie/strauss file accomplishes this
in userland by rewriting the files.  The whole of the namespace resolution
system is on the fly file renaming, so doing this in the engine should be
possible.

  ; 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/'


> Your comments also confuse me a bit.
>
> Is this saying that your hypothetical app — which you stated this `.ini`
> file is for — needs to use a package named `C` use "definition" is located
> at './path/to/C/autoload.ini' then it would use this syntax, and that in
> the app its components would be accessed at namespace `\C`?
>

Packages can have an ini file, but they are not REQUIRED to have one.
Otherwise, the existing library of packages will not be loadable because
they do not have such files.  Without the @ operator the library wouldn't
load correctly because all the files in Twig, parsed normally, will load to
\Twig.  The @C\ tells PHP to prefix C to every namespace declaration loaded
in that directory and each use statement. This is a new behavior entirely
and needs to be carefully considered.


>
> And I were to have:
>
> @Foo\Bar\Baz = './path/to/Foo/Bar/Baz/autoload.ini'
>
>
> Then in the app its components would be accessed at namespace
> `\Foo\Bar\Baz`?
>

Yes.


>
> Okay, this makes sense.  OTOH, this is the part that of your proposal that
> is incomplete for the needs of User-managed apps IMO.
>

This is the part that needs the most scrutiny overall.



>
> I think you are implying a necessary "best practice" that whenever any PHP
> library, or package would include code they would need to prefix the
> namespace of package when importing it and then when using it. Given an org
> named ACME that released a library called Widgets then if it were to use
> Twig it should import and use Twig like this* (did I understand your
> intent correctly?):*
>
> @ACME\Widgets\Twig/ = './path/to/Twig/'
>
> And in PHP code?:
>
> use \ACME\Widgets\Twig;
>

Or

  namespace ACME\Widgets;
  use Twig\Extension;

Namespace resolution rules can be tricky.


>
> I think that would work well for newer libraries and packages authored and
> used by developers of Developer-managed apps. OTOH I do not think it would
> be sufficient for any existing libraries or frameworks, nor for
> non-professional developers scratching their own itch on a User-managed
> apps and then deciding to publish it for others to use *(which happens a
> lot with User-managed apps.)*
>
> The problem would be that most (all?) of those would not be
> namespace-prefixing Twig but instead using it directly. I believe you need
> an ADDITIONAL `replace` sectionS that allowed an app/website developer to
> indicate that namespace A should instead be replaced in `use` statements
> and direct references with `B\A` for code that exists in directory(s) `C`
> but not in directories `C\D` where `C` and `D` can be globs.
>
>
...
>
> [replace]
> Twig[UpdraftPlus] = 'Twig_edaf27eb'
> Twig[Elementor] = 'Twig_edaf27eb'
>
>
No, PHP should handle this quietly under the hood without needing the
autocomplete author to do this whether it is a person or a userland program
like Composer.



> And lastly, because WordPress would need to generate this and having a web
> app write to a file is a modern security no-no, then `spl_autoload_map()`
> should accept multiple different valid values:
>
> spl_autoload_map( string|array|\PHP\AutoloadMap $map);
>
> 1. String would be the `.ini` file path
>
> 2. Array would be the format returned by parse_ini_file() for parsing an
> applicable `.ini` file
>
> 3. \PHP\AutoloadMap could be a new class containing the required values in
> object format. *(Hopefully adding such a class as a third option would
> not be controversial to the list members who criticize those developers
> still wanting to use arrays as hash maps?)*
>
> And that is about it for my feedback today.
>
>
I'm not opposed to that. Indeed, doing that would allow for additional
formats without the overhead of directly maintaining them

spl_autoload_map(json_decode('autoload.json'))

________

Now earlier today I had a thought that shook me.  The entry point of the
application in this example will likely call this function before anything
else, just as applications like Drupal call the autoload require before
anything else.

So, why not call the map first? then let the map designate the entry point?

A HUGE Can of opportunity worms opens. This is also a wild tangent, but
again this is a brainstorm thread.

  ; First this autoload map needs to tell PHP what it is.  A Package, or an
application.
  map_type = application

  ; The Setup files are those which always need to be run before the
application is
  ; ready to do anything.  These files will be loaded with NO ACCESS to
globals or
  ; or super globals. The engine's state after running these files will be
op cached and
  ; each subsequent request starts from here.  This allows the considerable
setup work
  ; that applications like Drupal do to be resolved only once and then
start from that
  ; cache. This is also the reason why superglobals aren't visible to these
files - the
  ; state cannot make decisions about any specific request because they'd
bleed into
  ; subsequent requests - a potential security nightmare.
  ;
  ; Note the autoloader of this script will be setup in full before these
files parse.
  [setup]
    [] = 'path/to/first/setup/file.php'
    [] = 'path/to/second/setup/file.php'

Just a thought.

Reply via email to