Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124207 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 0A2541A009C for ; Thu, 4 Jul 2024 00:16:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1720052271; bh=4nVDOulgIm9lTCI14D4uNIpDov3tuWoh8K/8cEYM/XQ=; h=References:In-Reply-To:From:Date:Subject:To:From; b=JGWnRw7cPJHWOuIfBEggf15+B/ikL2nVLrfLbCIoWi6hpwnrfVzxLEj8MvXVQz831 yxE65SrpvWigMF+96LJO/7FMCeRXrZgR0CpkKqZXMnHliSELFqbPTldvT0TRF1X8OL XAIBMEXO9vg3KiLfHIVfVhCUNFEad7t621fnw3NRoMZegGwfYR9JqDjJu8YLLADH7q EpJ4FJSMZmSMrJ9a7zVRgE8Fnp8MEPy8OGEEAAJahYDmixSE/X6INNerT8ZgEIcRxo zNaIuspekCLBFa+jPlqE49lTXoxdV7L0CHgAZG1vwqRbA6n9XEsSYsNGlBtXuqNkME 1vmcM9gZVwGCw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id A5C84180061 for ; Thu, 4 Jul 2024 00:17:50 +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_H2,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-qv1-f48.google.com (mail-qv1-f48.google.com [209.85.219.48]) (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 ; Thu, 4 Jul 2024 00:17:50 +0000 (UTC) Received: by mail-qv1-f48.google.com with SMTP id 6a1803df08f44-6b553b1a159so612976d6.0 for ; Wed, 03 Jul 2024 17:16:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720052187; x=1720656987; darn=lists.php.net; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=8x2G/627N8XiWyMLkrCosEnCfdBfP+/lkZVhkZwPiC0=; b=VaVzipnGWNqvjdZpv4Uo9ClF+S2YxbzdEsY97iuCyouwQ2mMUsziuOXbnKp0BuBpG1 HL9w7tdqmm3cL2a7foJMl4F/mZwIP0MB4iw07IhQY5LfGSepnTFp3u5xYw+G0izrTINs 4pwIecmk0sxRft1+PKr1hLxUsGi7WRTTEHWGQuYkhRhBRq8QmEAf15LpTVZDUJlK8hKD M/8QlorU9p6x171oFyE5vvAXu9YjVKbyiAjmmbCKZPVntVYeVAXu8KoWrRN/eERUR1bo zrpCdm5ywaZ2HcVGajYyNfkZXRJgilRpBfUii/9oG6cbvknnzPT4a90Fb89Wpkf8F4Je 5GpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720052187; x=1720656987; h=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=8x2G/627N8XiWyMLkrCosEnCfdBfP+/lkZVhkZwPiC0=; b=C1u0osZ1RcHwq3iCZmFML2y0KSVqPNTM0EsVrcQT3/Hrn6ZRm+TkHA+kRbisSAGWXC tv3oldHcmD9a4eywkRndxLRb1o5MVMI2tRDOiZvvugUTuIYy1QH/dIObaN3hF7a6O9hf o2/hx9ocYuCgFd/H5gZnjuAT++lpvg7sj41k4rTQ+2gB0J3Ek+3Rz4hJz3R/ov12nfgR 9Gd0k3SOOAg9SAoo+Dy8KYhy2yfSuWUGF6ZNk/51zaooGZMFxO6FVthW40mupgK/nUmE myILPlkjZsh+7WSViRl9NA8qt2d0y4fkVBu35jD8fgcI3VVK0nBXfovdoMl5t+W29Krv C8bw== X-Gm-Message-State: AOJu0Yx0bcrbedDYhkMVbP0bXADjec4ftysGA7KY5txjkRFOIbnP36N+ oA688t+f8aCpdkITvCDalxrOPZVg922UhtENstsuqzcQn52cg4/d78mG+jhgCAvtdA1AZZwgEkl QbT6muikSvFpWw+LWB0Qnut/HCZuZhw== X-Google-Smtp-Source: AGHT+IFGkKMcOMGC97heDXtDcpCl6/WS1pDwGYfuWT3h2zQBcjstuy0a3SuyZf+RTPKFvpT4TxhR5SCwf/qGfyvpHmQ= X-Received: by 2002:a05:6214:1c0d:b0:6b5:936d:e5e9 with SMTP id 6a1803df08f44-6b5ecfb98d9mr1219366d6.26.1720052187320; Wed, 03 Jul 2024 17:16:27 -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: Wed, 3 Jul 2024 20:16:15 -0400 Message-ID: Subject: [PHP-DEV] [PHP-Dev] Versioned Packagers (Iteration IV) To: PHP internals Content-Type: multipart/alternative; boundary="0000000000009ed84e061c60db52" From: tendoaki@gmail.com (Michael Morris) --0000000000009ed84e061c60db52 Content-Type: text/plain; charset="UTF-8" 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 can 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 Twig 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 new 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 beginning and intermediate coders can ignore it just like they ignore strict comparison operators and strict typing unless a library they are trying to use foists it on them. This is why I advocate a new keyword for this - import. Import's behavior is most similar to require_once, but it doesn't have to be the same. Since 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 foo() 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 not 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 different 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 package versioning, it doesn't solve the multiple extensions wanting to use the new 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 performance 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 invoked 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 the torpedo tossers because it's forcing me to think this through entirely. And 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 tight than the one before it. --0000000000009ed84e061c60db52 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hello all. Hitting reset again as the primary problem at h= and has become clear.=C2=A0 Let's recap it.

Autoload= ing is great for loading packages, but it can't load different versions= of the same package at the same time.=C2=A0 Why would you want to do that?=

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

For example, consider Drupal.=C2=A0 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.=C2= =A0 This is possible if you are willing to monkey-type the package - that i= s, have a code package traverse over the entire package and change all inst= ances of `namespace Twig` in the files to `namespace NewTwig`. You can then= use the package at the namespace of \NewTwig.

Thi= s is painful, but the pain factor increases if multiple extension developer= s choose to do the same thing.=C2=A0 Each extension using its own Twig libr= ary is going to incur a performance hit.

One upsho= t of this is I've noted that major package distributors, like Symfony, = take BC into account with major releases - and may not develop new features= or change things in those releases out of fear of people not wanting to up= grade.

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

In so= me 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?

We= ll first off, recognize that autoloading isn't going to work with a ver= sioned package scheme. Autoloaders, regardless of their resolution schema b= e it PSR-0, PSR-4, or BobbysFirstAutoloader-Scheme=C2=A0can only have one s= ymbol 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 cordone= d 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 compar= ison operators and strict typing unless a library they are trying to use fo= ists it on them.

This is why I advocate a new keyw= ord for this - import.=C2=A0 Import's behavior is most similar to requi= re_once, but it doesn't have to be the same.=C2=A0 Since it is a new en= trypoint into the engine the way the engine considers the code can be diffe= rent - whether slightly different or radically different is a debate for an= other time. I'm going to stick with only those changes that make sense = in the context of package links.

Let's start w= ith the simplest problem, importing this file.

=C2= =A0 namespace A;
=C2=A0 function foo() { echo 'Hi'; }

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

At its root import would = do the same. `import "file.php"` would do the same as a require_o= nce assuming there's no difference between the file structure rules for= import - again there is opportunity here, but it's not a requirement.<= /div>

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

=C2=A0 import '= file.php' as B;

Now we have \B\foo();=C2=A0 Th= is makes it relatively easy to have two different versions of the package r= unning since in our own code we can always reference the foo in the B names= pace. But while that allows limited package versioning, it doesn't solv= e the multiple extensions wanting to use the new stuff problem outlined abo= ve.

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

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

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

Now for the first real behavior differe= nce between import and require_once, even if we aren't doing anything f= ancy.=C2=A0 Import cares about the namespace it's invoked from.=C2=A0 R= equire_once does not.=C2=A0 To illustrate this behavior he's some pseud= ocode=C2=A0- we are including the file.php given earlier

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

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

=C2=A0 import 'file.php';

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

See that? The namespace of the calling = file is prepended to the namespace contained in the import.

<= /div>
Why?=C2=A0 What's the value here?=C2=A0 I'll explain.

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

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

=C2=A0 A\foo(); // Welcome, since = version 2 echoes welcome. Remember your namespace resolution rules - this i= mport is actually at:
=C2=A0 \C\A\foo(); Welcome, as this is the absolut= e path to the code we just imported.
=C2=A0 \A\foo(); // Hi, as the pack= age at root was brought in by require_once()
=C2=A0 \D\A\foo(); H= i, 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\f= oo(); // 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 performance hit might be avoided.=C2=A0 That is, when= a redundant import occurs that would pull the same package the engine just= quickly links up the new namespace.=C2=A0 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 cri= tical 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 path= s?
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 invoked= as in go (I don't remember how Python does it, but someone providing t= hat 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 p= ackage.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 quest= ions, but too many questions at once is too much so I'd like to leave t= hem aside for now.

And there are yet more question= s 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&#= 39;m inclined to avoid it if possible.

A quick thank you to everyone= who has participated in the thread, even the torpedo tossers because it= 9;s forcing me to think this through entirely. And I'm trying to take a= s much into consideration as possible.=C2=A0 And yes, this remains a brains= torm for now, but each successive brainstorm is more tight than the one bef= ore it.

=C2=A0
--0000000000009ed84e061c60db52--