Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124271 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id A9F171A009C for ; Mon, 8 Jul 2024 02:26:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1720405644; bh=t9xXEeq6Bqq5bW+PTNKgcEQjzP2mp5Z1xs8W3MaiXgk=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=hE66UHmzSgXUF+AubmppsE+vaknUur/x9vJ//z8ObP62CiHATsN8+AaX85a0z5TIv PKvLYASY7xZlsRSCef41l6WWSFBquVLaVdOspOTlRnYVg4OKj6M5a6SJLzvQoQHygT n0b3ynoaJsICMwXA1AmT3xWedp/m3ZwzF+ycMdDhiFd11DiporWooKAwh1f4rg3lBc bhzJTUcLt3aoG/ghIuoqD9zK7gJJ5nCRgeT7khDkrCSJQhK8DycYMF3oXIfVcNsuzQ wsbEbdAlBnNVUx5L1iJYa4zlwFCrOJtlhUKkrqNnCWd2kOwiv8wzHKDZj1XBnvqRMT mJ2cYJVjSg1/A== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id E9D181801DE for ; Mon, 8 Jul 2024 02:27:23 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mail-pj1-f49.google.com (mail-pj1-f49.google.com [209.85.216.49]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 8 Jul 2024 02:27:23 +0000 (UTC) Received: by mail-pj1-f49.google.com with SMTP id 98e67ed59e1d1-2c95ca60719so1860756a91.3 for ; Sun, 07 Jul 2024 19:25:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720405558; x=1721010358; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=eWV7442bO9oa/NpYZJqCaa24al+hUAdjpp3lxnZxzBA=; b=bRiLM7aod4jdil2N9uktHLDNhUPNjcZ5qtrmf9vFrFkUJARk/89bMEu8/m0aWiaZgF JCyVJwaEZ5OTOJYQrSvZXI+Otl5PWQiMLqsu+VEMBoRsm0EabUrurBY9KGb2bNDc6YF6 tuMRlCdJtTp8w9/AYhNnJcrYWpo7/D7pzpSzcLvbe7++3y08TnNccj3pA3az5ZYW6C6y seyOvEOHI/kXQ5qG482sizyoypYrHM2w98agxlox7sse36XNNWHGZPNqDkYhrIOc+fDj QxAkRlKgAKkVS+Y0Wot63bSF7FyDnEnKual2LV+/j8mA6ZT2iQGY8MHED1ciM/7fCZTL 7zkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720405558; x=1721010358; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=eWV7442bO9oa/NpYZJqCaa24al+hUAdjpp3lxnZxzBA=; b=IaG8isJmncXmhzQc21fGtumdaswNuEVYmo+k9tbvj1Oot7e9ioGNBCle5QQGyb04iE IQ6v91S2ky0eAdGD520jVfyD1ZmB/vUXWIvqW1fyBXIndrChuKVzmX62nEy2vNpn0K+A T2HJgt1fCEdP0KqQln14H+t3uz1OoznDenebvAf/IdX35/yEsN6Cu6c6bWqEtNUaRrdA DyTC2jHJiXz7wtqlPqD9Hcpf6OXAYJWhBuP9I3bzFiQWTvDI+GbQ020jHndGlieq/h72 7LeJvKH4H4v0hEjoxF5DhjsYW7fN68ghVUmtkIyRLRcpjiUaD2eVBRWsnGSjiEZPOHB4 bnDA== X-Gm-Message-State: AOJu0YyGf+wjTOW5qP1deGD0f/x/PsyE7Zcymhl55j/pPvxo8MJAV5Nr +/Z8DMLfHS5jYIuwSfhk536M2dhmhHfTi6P59CS7ctKetiptBNssmjWbapA+cNh+dVUHvz565+4 E6OVtQtg57tLQSQ5HrhFs5SYh4rv/Vw== X-Google-Smtp-Source: AGHT+IHG7RYqzwbZCChK4c3tnH4gkNdoLinDxYKQZPjjW3znmfAQqiMqhHr3msFJFyD3QUW1gyg2R0VD1ddM+5EJdcY= X-Received: by 2002:a17:90b:245:b0:2c9:860c:7802 with SMTP id 98e67ed59e1d1-2c99c6c1e39mr5557398a91.28.1720405558029; Sun, 07 Jul 2024 19:25:58 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: <09559430-4477-4516-8D78-6F4071E1AA6C@newclarity.net> <0182F3D6-F464-477F-9029-A2D0A8B50C71@koalephant.com> <1AFD7AAE-8BEA-460D-88A8-15BB3D30A775@koalephant.com> In-Reply-To: Date: Sun, 7 Jul 2024 19:25:45 -0700 Message-ID: Subject: Re: [PHP-DEV] [PHP-Dev] Versioned Packagers (Iteration IV) To: Michael Morris Cc: PHP internals Content-Type: multipart/alternative; boundary="00000000000027f783061cb322b7" From: jordan.ledoux@gmail.com (Jordan LeDoux) --00000000000027f783061cb322b7 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Wed, Jul 3, 2024 at 5:18=E2=80=AFPM Michael Morris = wrote: > Hello all. Hitting reset again as the primary problem at hand has become > clear. Let's recap it. > > Autoloading is great for loading packages, but it can't load different > versions of the same package at the same time. Why would you want to do > that? > > When you don't have full control of the code. > > For example, consider Drupal. It is running Twig at some version of 3 at > the moment. Suppose Twig 4 is introduced with significant backward > compatibility breaks (Not saying the authors would do such a thing) but > also wonderful features. > > If you're writing a Drupal extension you might want to use this new Twig. > This is possible if you are willing to monkey-type the package - that is, > have a code package traverse over the entire package and change all > instances of `namespace Twig` in the files to `namespace NewTwig`. You ca= n > then use the package at the namespace of \NewTwig. > > This is painful, but the pain factor increases if multiple extension > developers choose to do the same thing. Each extension using its own Twi= g > library is going to incur a performance hit. > > One upshot of this is I've noted that major package distributors, like > Symfony, take BC into account with major releases - and may not develop n= ew > features or change things in those releases out of fear of people not > wanting to upgrade. > > Now don't get me wrong, changing things just because is a bad thing. If a > BC can be avoided it should be. But having a mechanism to move forward is > important. > > In some ways versioning packages is like static typing variables. It > doesn't seem important at all until you are faced with a problem only it > can solve, or faced with a problem created by dynamic typing of variables= . > > What can be done in the engine? > > Well first off, recognize that autoloading isn't going to work with a > versioned package scheme. Autoloaders, regardless of their resolution > schema be it PSR-0, PSR-4, or BobbysFirstAutoloader-Scheme can only have > one symbol per package, set by the namespace. > > Can PHP support multiple packages without rewriting the whole engine? I > think so, but it isn't trivial, and the side effects need to be cordoned > off so that those who need this complexity can have it while the beginnin= g > and intermediate coders can ignore it just like they ignore strict > comparison operators and strict typing unless a library they are trying t= o > use foists it on them. > > This is why I advocate a new keyword for this - import. Import's behavio= r > is most similar to require_once, but it doesn't have to be the same. Sin= ce > it is a new entrypoint into the engine the way the engine considers the > code can be different - whether slightly different or radically different > is a debate for another time. I'm going to stick with only those changes > that make sense in the context of package links. > > Let's start with the simplest problem, importing this file. > > namespace A; > function foo() { echo 'Hi'; } > > To review, if we require_once this file we'll find the function at > \A\foo(). If our current file uses the same namespace we can just use fo= o() > > At its root import would do the same. `import "file.php"` would do the > same as a require_once assuming there's no difference between the file > structure rules for import - again there is opportunity here, but it's no= t > a requirement. > > If that's all it does, it's pointless. However, import can alias. > > import 'file.php' as B; > > Now we have \B\foo(); This makes it relatively easy to have two differen= t > versions of the package running since in our own code we can always > reference the foo in the B namespace. But while that allows limited packa= ge > versioning, it doesn't solve the multiple extensions wanting to use the n= ew > stuff problem outlined above. > > So we have to call out the version in code, like so. > > import 'file.php v1.0.0'; > > A simple space separates the version from the file. If the filename has = a > space, well \ characters aren't just for namespaces. > > Now for the first real behavior difference between import and > require_once, even if we aren't doing anything fancy. Import cares about > the namespace it's invoked from. Require_once does not. To illustrate > this behavior he's some pseudocode - we are including the file.php given > earlier > > namespace D; > require_once 'file.php'; > > \A\foo(); // Hi. > > import 'file.php'; > > \D\A\foo(); // Hi. > > See that? The namespace of the calling file is prepended to the namespace > contained in the import. > > Why? What's the value here? I'll explain. > > Now, let's suppose we do have two versions of file.php. So in addition to > the above, elsewhere in the code this happens > > namespace C; > import 'file.php v2.0.0' > > A\foo(); // Welcome, since version 2 echoes welcome. Remember your > namespace resolution rules - this import is actually at: > \C\A\foo(); Welcome, as this is the absolute path to the code we just > imported. > \A\foo(); // Hi, as the package at root was brought in by require_once(= ) > \D\A\foo(); Hi, as that's what was imported into the D namespace. > > Now for the kicker > > namespace E; > import 'file.php'; > > A\foo(); // Hi. > > The engine can be left as is and this would work, but if the engine is > altered to support symbolic links on the symbol table then the performanc= e > hit might be avoided. That is, when a redundant import occurs that would > pull the same package the engine just quickly links up the new namespace. > Hence \E\A\foo() quietly points to \D\A\foo() as it was declared first. > > > What hasn't been discussed in this iteration are the following critical > points: > 1) How the package path gets resolved in the first place. Does it work > like require and check locally then check the PHP include paths? > 2) When does the code get downloaded from where it is downloaded? > 3) Is a registry used like composer and npm, or are repos directly invoke= d > as in go (I don't remember how Python does it, but someone providing that > example might be useful) > 4) The huge ball of wax that is the package definition file. Just look at > the properties of composer.json and package.json to get an idea of that > scope. How much of if any of this should PHP deal with. > 5) Is import to be locked into loading other PHP files, or could it deal > with .so (Unix) or .dll (Windows) files? Phar files? > > It's not like I'm not interested in any of these questions, but too many > questions at once is too much so I'd like to leave them aside for now. > > And there are yet more questions as well raised in previous iterations, > but I've again left those out because they touched off controversy. While > I'm not afraid of such, I'm inclined to avoid it if possible. > > A quick thank you to everyone who has participated in the thread, even th= e > torpedo tossers because it's forcing me to think this through entirely. A= nd > I'm trying to take as much into consideration as possible. And yes, this > remains a brainstorm for now, but each successive brainstorm is more tigh= t > than the one before it. > > > I think it's strange that this discussion has driven deep down the tangent of versioning, as if the selling point of any kind of module/package system in PHP core would be to do what composer does. Let compose do what composer does, it does it well. Instead of building out the specific features like this that honestly shouldn't be built into the language directly, I would think it makes more sense for the discussion to be centered around the compiler and engine features that would LET or ENABLE software like composer to easily meet these requirements. Things like separating global scope between importer and importee, managed visibility of symbols and exports from modules/packages, allowing for separate autoloaders for things which are called or included via an import, etc. Those are the things that the language itself can do. All this other stuff feels like a distraction. Jordan --00000000000027f783061cb322b7 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
On Wed, Jul 3, 2024 at 5:18=E2=80=AFP= M Michael Morris <tendoaki@gmail.c= om> wrote:
Hello all. Hitting reset again as the primary problem at= hand has become clear.=C2=A0 Let's recap it.

Autolo= ading is great for loading packages, but it can't load different versio= ns of the same package at the same time.=C2=A0 Why would you want to do tha= t?

When you don't have full control of the cod= e.

For example, consider Drupal.=C2=A0 It is runni= ng Twig at some version of 3 at the moment. Suppose Twig 4 is introduced wi= th significant backward compatibility breaks (Not saying the authors would = do such a thing) but also wonderful features.

If y= ou're writing a Drupal extension you might want to use this new Twig.= =C2=A0 This is possible if you are willing to monkey-type the package - tha= t is, have a code package traverse over the entire package and change all i= nstances of `namespace Twig` in the files to `namespace NewTwig`. You can t= hen use the package at the namespace of \NewTwig.

= This is painful, but the pain factor increases if multiple extension develo= pers choose to do the same thing.=C2=A0 Each extension using its own Twig l= ibrary is going to incur a performance hit.

One up= shot of this is I've noted that major package distributors, like Symfon= y, take BC into account with major releases - and may not develop new featu= res or change things in those releases out of fear of people not wanting to= upgrade.

Now don't get me wrong, changing thi= ngs just because is a bad thing. If a BC can be avoided it should be. But h= aving a mechanism to move forward is important.

In= some ways versioning packages is like static typing variables. It doesn= 9;t seem important at all until you are faced with a problem only it can so= lve, or faced with a problem created by dynamic typing of variables.
<= div>
What can be done in the engine?

Well first off, recognize that autoloading isn't going to work with a = versioned package scheme. Autoloaders, regardless of their resolution schem= a be it PSR-0, PSR-4, or BobbysFirstAutoloader-Scheme=C2=A0can only have on= e symbol per package, set by the namespace.=C2=A0

= Can PHP support multiple packages without rewriting the whole engine?=C2=A0= I think so, but it isn't trivial, and the side effects need to be cord= oned off so that those who need this complexity can have it while the begin= ning and intermediate coders can ignore it just like they ignore strict com= parison operators and strict typing unless a library they are trying to use= foists it on them.

This is why I advocate a new k= eyword for this - import.=C2=A0 Import's behavior is most similar to re= quire_once, but it doesn't have to be the same.=C2=A0 Since it is a new= entrypoint into the engine the way the engine considers the code can be di= fferent - whether slightly different or radically different is a debate for= another time. I'm going to stick with only those changes that make sen= se in the context of package links.

Let's star= t with the simplest problem, importing this file.

= =C2=A0 namespace A;
=C2=A0 function foo() { echo 'Hi'; }<= /div>

To review, if we require_once this file we'll = find the function at \A\foo().=C2=A0 If our current file uses the same name= space we can just use foo()

At its root import wou= ld do the same. `import "file.php"` would do the same as a requir= e_once assuming there's no difference between the file structure rules = for import - again there is opportunity here, but it's not a requiremen= t.

If that's all it does, it's pointless.= =C2=A0 However, import can alias.

=C2=A0 import &#= 39;file.php' as B;

Now we have \B\foo();=C2=A0= This makes it relatively easy to have two different versions of the packag= e running since in our own code we can always reference the foo in the B na= mespace. But while that allows limited package versioning, it doesn't s= olve the multiple extensions wanting to use the new stuff problem outlined = above.

So we have to call out the version in code,= like so.

=C2=A0 import 'file.php v1.0.0';=

A simple space separates the version from the fil= e.=C2=A0 If the filename has a space, well \ characters aren't just for= namespaces.

Now for the first real behavior diffe= rence between import and require_once, even if we aren't doing anything= fancy.=C2=A0 Import cares about the namespace it's invoked from.=C2=A0= Require_once does not.=C2=A0 To illustrate this behavior he's some pse= udocode=C2=A0- we are including the file.php given earlier

=C2=A0 namespace D;
=C2=A0 require_once 'file.php= 9;;

=C2=A0 \A\foo(); // Hi.

=C2=A0 import 'file.php';

=C2=A0 \D\A\f= oo(); // Hi.

See that? The namespace of the callin= g file is prepended to the namespace contained in the import.
Why?=C2=A0 What's the value here?=C2=A0 I'll explain.

Now, let's suppose we do have two versions of f= ile.php. So in addition to the above, elsewhere in the code this happens

=C2=A0 namespace C;
=C2=A0 import 'fil= e.php v2.0.0'

=C2=A0 A\foo(); // Welcome, sinc= e version 2 echoes welcome. Remember your namespace resolution rules - this= import is actually at:
=C2=A0 \C\A\foo(); Welcome, as this is the absol= ute path to the code we just imported.
=C2=A0 \A\foo(); // Hi, as the pa= ckage at root was brought in by require_once()
=C2=A0 \D\A\foo();= Hi, as that's what was imported into the D namespace.

Now for the kicker

=C2=A0 namespace E;
=C2=A0 import 'file.php';

=C2=A0 A= \foo(); // Hi.

The engine can be left as is and th= is would work, but if the engine is altered to support symbolic links on th= e symbol table then the performance hit might be avoided.=C2=A0 That is, wh= en a redundant import occurs that would pull the same package the engine ju= st quickly links up the new namespace.=C2=A0 Hence \E\A\foo() quietly point= s to \D\A\foo() as it was declared first.


What hasn't been discussed in this iteration are the following c= ritical points:
1) How the package path gets resolved in the first place= . Does it work like require and check locally then check the PHP include pa= ths?
2) When does the code get downloaded from where it is downloaded? <= br>3) Is a registry used like composer and npm, or are repos directly invok= ed as in go (I don't remember how Python does it, but someone providing= that example might be useful)
4) The huge ball of wax that is th= e package definition file. Just look at the properties of composer.json and= package.json to get an idea of that scope. How much of if any of this shou= ld PHP deal with.
5) Is import to be locked into loading other PHP files= , or could it deal with .so (Unix) or .dll (Windows) files? Phar files?
=
It's not like I'm not interested in any of these que= stions, but too many questions at once is too much so I'd like to leave= them aside for now.

And there are yet more questi= ons as well raised in previous iterations, but I've again left those ou= t because they touched off controversy. While I'm not afraid of such, I= 'm inclined to avoid it if possible.

A quick thank you to everyo= ne who has participated in the thread, even the torpedo tossers because it&= #39;s forcing me to think this through entirely. And I'm trying to take= as much into consideration as possible.=C2=A0 And yes, this remains a brai= nstorm for now, but each successive brainstorm is more tight than the one b= efore it.

=C2=A0

I think= it's strange that this discussion has driven deep down the tangent of = versioning, as if the selling point of any kind of module/package system in= PHP core would be to do what composer does. Let compose do what composer d= oes, it does it well.

Instead of building out the = specific features like this that honestly shouldn't be built into the l= anguage directly, I would think it makes more sense for the discussion to b= e centered around the compiler and engine features that would LET or ENABLE= software like composer to easily meet these requirements.

Things like separating global scope between importer and importee,= managed visibility of symbols and exports from modules/packages, allowing = for separate autoloaders for things which are called or included via an imp= ort, etc. Those are the things that the language itself can do.=C2=A0
=

All this other stuff feels like a distraction.

Jordan
--00000000000027f783061cb322b7--