Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124011 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 D8E261A009C for ; Sat, 29 Jun 2024 07:52:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1719647621; bh=Rzf6FUn8Ma23TsiRi0Avcr56KdbTiibnV/HUjrMes6I=; h=In-Reply-To:References:Date:From:To:Subject:From; b=BGJx4vZAStwlTFon/tvtLlx4tHa0l/McirEBPPQAexRIk7RH1PLEPiAFSf7vdk27r wczGYSrQMhNwLwC4l3a7DqSJ8XQQbLJGY/K+KLAuhJyNCKMMzzqW9PjlxXRXC0fRM0 kOUnQ9aVOeWG91P2DN1jtW4VrZrnYI1qiYDO0vLbiIcCDQVyruTUyWcafmB02R5y1Y D0VQ9eMjfCq/zlPs3fRgeejd+K2reNXs/euOeZ5he9BHP0KcwVNGNBwH45/61Og1q9 tRr376zTeTbq68W+eQN24fFSNrX0YNhHOePP6nAyavS1F1kQXf6vT37H5KLmH4sPtp ngA955mO7cNHg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 57DCB18005C for ; Sat, 29 Jun 2024 07:53:40 +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.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,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 fout1-smtp.messagingengine.com (fout1-smtp.messagingengine.com [103.168.172.144]) (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 ; Sat, 29 Jun 2024 07:53:39 +0000 (UTC) Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailfout.nyi.internal (Postfix) with ESMTP id 236F6138024E for ; Sat, 29 Jun 2024 03:52:20 -0400 (EDT) Received: from imap49 ([10.202.2.99]) by compute1.internal (MEProxy); Sat, 29 Jun 2024 03:52:20 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm3; t=1719647540; x=1719733940; bh=YM7uXF6JXp TfqRn1iSYkt7drYatxjIRwU8yKNsDfb2Y=; b=iOH29F190ljNWxGILx3LV08B26 Q3XMPVkIxLCzJR9V/yf/JT+irvC13AzAxHHsb7mGPVJOlM6KBt6qiyDyHJmRH0fk BOYu/B4mAE6zIEDdWh7X0/d1NF3nHYdaNKzIttSYIq6mVAMN4troG3j1f61XQxfa 0PZPtVUIvbp5cIUV2hFd/c9vI7q9iYYmnHAjKzuLLPXdb8Ah0WIDZJt3TwSEz2+v S41BP0qYYfULHMz6pPCGzt3kq419RxbHK658ZoBUVzmhi1MllPLsrx4/4v9uwMHx IE3VDlCevygdJyJiSLZTHfpoSYtdJFlBxWF8LgFJhorsj6sqPcKZnbQqCycQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1719647540; x=1719733940; bh=YM7uXF6JXpTfqRn1iSYkt7drYatx jIRwU8yKNsDfb2Y=; b=aAb8xN5d92Xwr5sM3M1miqfW0me1NsSPRYbnelp49Kju uU0xOZrPVKXQykm4bn1DpOY072xPheedQoOhXipPIP3qOvmRPorJ8P7wBRq2u6xr uIvFMieiKLkZ1UR63WpvFmMQfmPCdILslW7dm83xSw9rjtgftDLjZPvpPi1WqcO3 fMLnpfG9OwppI5/YaEFbZ9bRLTxtiAlhDY+cniNROC0o7alzk24xRG71A49FdGfW mYwW2gYiGA3Vu0KLDae9SCz2YOa0jVQTNQ6saM/c2uQdrwmTM1jbO/BHSvbzfbJl 1rDzlN25MNuWGNx5B2FFxXpxYnaTvvsuT28ewzlodQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrtdekgdduvdehucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucenucfjughrpefofgggkfgjfhffhffvufgtsegrtd erreerreejnecuhfhrohhmpedftfhosgcunfgrnhguvghrshdfuceorhhosgessghothht lhgvugdrtghouggvsheqnecuggftrfgrthhtvghrnhepgfehieffleehfefgtdfhueffhe ejtedvhfdttdevtefhtefgieetudevjefgffelnecuffhomhgrihhnpehprggtkhgrghhi shhtrdhorhhgpdhgihhthhhusgdrtghomhdpmhhougdrshhonecuvehluhhsthgvrhfuih iivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosgessghothhtlhgvugdrtgho uggvsh X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id B303315A0092; Sat, 29 Jun 2024 03:52:19 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.11.0-alpha0-538-g1508afaa2-fm-20240616.001-g1508afaa Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 Message-ID: <4c8ec1e7-d126-45f4-9260-ae1d90bcfe8c@app.fastmail.com> In-Reply-To: References: <1917CF7C-26D8-4DBE-B05C-5AA650AC6C9F@rwec.co.uk> <551cd5b0-1c00-4818-a9ca-97f6b7e8c3dc@app.fastmail.com> Date: Sat, 29 Jun 2024 09:51:58 +0200 To: internals@lists.php.net Subject: Re: [PHP-DEV] [Initial Feedback] PHP User Modules - An Adaptation of ES6 from JavaScript Content-Type: multipart/alternative; boundary=32b5ec5794f848478c7504b55e735e55 From: rob@bottled.codes ("Rob Landers") --32b5ec5794f848478c7504b55e735e55 Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sat, Jun 29, 2024, at 08:32, Michael Morris wrote: > Not replying to anyone in particular and instead doing a mild reset ta= king into account the discussion that has gone before.=20 >=20 > So, I want to import a package. I'll create an index.php file at the r= oot of my website and populate it with this. >=20 >> > import "./src/mymodule"; >=20 > Now I'll create that directory and run a command `php mod init` in tha= t directory. Stealing this from Go, it's fairly straightforward though. = Now if we look in the directory we will see two files. >=20 >> php.mod >> php.sum >=20 > The second file I'll not be touching on but exists to track checksums = of downloaded packages - Composer does the same with its composer-lock.j= son file which in turn was inspired by node's package-lock.json. I don't think that is correct... package-lock.json didn't come about unt= il what, 2016-7ish? with pressure from yarn which did a yarn.lock file. = Pretty sure composer was doing that since the beginning. I remember this= being a BIG reason we switched from npm to yarn when it came out, becau= se dev A would have different versions of libraries than dev B. Bug hunt= ing was FUN when it was in a library. >=20 > The php.mod file stands in for composer.json, but it isn't a json file= . It would start something like this: >=20 >> namespace mymodule >> php 10.0 >> registry packagist.org/packages >>=20 > We start with three directives - the root namespace is presumed to be = the directory name. If that isn't true this is a text file, change it. = PHP min version should be straightforward. Registry details where we are= going to go get code from. Suppose we want to use our own registry but= fallback to packagist. That would be this: >=20 >> namespace mymodule >> php 10.0 >> registry ( >> github.com/myaccount >> packagist.org/packages >> ) > =20 > Multiple registry entries will be checked for the code in order. Hand= ling auth tokens for restricted registries is outside of scope at the mo= ment. While this looks good on paper, you're going to have to standardize how = packages are accessed (API calls, etc) so they can be used in this file,= or literally anyone who wants to add a competing registry will have to = create an RFC to allow accessing their own registry, which is a ton of p= olitics for something that is strictly technical -- not to mention a bun= ch of if-this-registry-do-that type statements scattered throughout the = code, which makes it harder to maintain. >=20 > So let's build the module. We'll make a file called hello.phm. The re= ason for phm and not php is so that web SAPIs will not try to parse this= code. Further they can be configured to not even allow direct https ac= cess to these files at all. >=20 >> import "twig/twig"; >> use \Twig\Loader\ArrayLoader; >> use \Twig\Environment; >>=20 >> $loader =3D new ArrayLoader([ >> 'index' =3D> 'Hello {{ name }}' >> ]); >>=20 >> $twig =3D new Environment($loader); >>=20 >> export $twig; >=20 SAPIs are the programs that parse ALL php code and return it to the serv= er (ie, nginx, apache, caddy, etc) to be displayed. The SAPI absolutely = needs to parse these files in order to execute them. Servers are designe= d to display files, so any server configured today will just output the = contents of these files because it won't be configured to send the reque= st to the SAPI instead. It's better to suggest moving these files out of= the web-root so it's a non-issue. In other news, I'm not a fan of how many times I have to write "twig" ju= st to get Twig in the current file. The module already registers a names= pace, why can't the use-statement implicitly import the module? > As mentioned in previous discussions, modules have their own variable = scope. Back in our index we need to receive the variable >=20 >> > import $twig from "./src/mymodule" >>=20 >> $twig->render('index', ['name' =3D> 'World']); >=20 > If we load index.php in the web browser we should see "Hello World". = If we look back in the mymodules folder we'll see the php.mod file has = been updated In real life, my code is going to be in a module/framework and I'm going= to need to render it there. This example of exporting a dependency also= kinda breaks encapsulation principles, and even though it is an example= , things like this end up in documentation of a feature and cause all ki= nds of bad practices (like Symfony and anemic objects). >=20 >> namespace mymodule >> php 10.0 >> registry packagist.org/packages >>=20 >> imports ( >> twig/twig v3.10.3 >> symfony/deprecation-contracts v2.5 //indirect >> symfony/polyfill-mbstring v1.3 //indirect >> symfony/polyfill-php80 v1.22 //indirect >> ) >=20 > Note the automatically entered comment that marks the imported depende= ncies of twig. Meanwhile the php.sum file will also be updated with the = checksums of these packages. One of the first things I do in a composer.json file is remove polyfills= through the replace key. It's unnecessary, annoys me in my IDE with hav= ing multiple classes of the same name, and hides the fact that I should = probably install an extension for better performance. How do we do that = with this new setup? In fact, it is worth pointing out that how would this system work with p= olyfills in-general? Polyfills have their uses -- especially for library= /framework code where you don't control the runtime environment. Like ho= w would someone polyfill mb_string since people will be adding `import @= mbstring` and not `import symfony/polyfill-mbstring`? >=20 > So why this instead of composer? Well, a native implementation should= be faster, but also it might be able to deal with php extensions. >=20 >> import "@php_mysqli" >>=20 > The @ marks that the extension is either a .so or .dll library, as I'l= l hazard a guess that the resolution mechanic will be radically differen= t from the php language modules themselves - if it is possible at all. I= f it can be done it will make working with packages that require extensi= ons a hell of a lot easier since it will no longer be necessary to monke= y the php.ini file to include them. At a minimum the parser needs to kno= w that the import will not be in the registry and instead it should look= to the extensions directory, hence the lead @. Speaking of, having the = extension directory location be a directive of php.mod makes sense here.= Each module can have its own extension directory, but if this is kept = within the project instead of globally then web SAPIs definitely need to= stay out of those directories. So ... if we want to round, we have to use `import @math` and then we ca= n call the global round() function? Or if we want to use DateTimeImmutab= le we have to add `import @date`? That seems like a step in the wrong di= rection since most people don't even know that most (if not all) global = library functions come from extensions -- and virtually nobody knows the= name of each extension and what functions they have. Also, installing e= xtensions is not 100% straightforward as some environments need to use p= ecl, some need to use OS package managers. >=20 > Final thing to touch on is how the module namespaces behave. The expor= t statement is used to call out what is leaving the module - everything = else is private to that module.=20 >=20 >> class A {} // private >> export class B {} // public >>=20 > All the files of the package effectively have the same starting namesp= ace - whatever was declared in php.mod. So it isn't necessary to repeat= the namespace on each file of the package. If a namespace is given, it = will be a sub-namespace >=20 >> namespace tests; >>=20 >> export function foo() {} >>=20 > Then in the importing file >=20 >> import "./src/mymodule" >> use \mymodule\tests\foo >>=20 >>=20 > Notice here that if there is no from clause everything in the module g= rafts onto the symbol table. Subsequent file loads need only use the us= e statement. Exported variables however must be explicitly pulled becaus= e the variable symbol table isn't affected by namespaces (if I recall co= rrectly, call me an idiot if I'm wrong). >=20 > The from clause is useful for permanently aliasing - if something is i= mported under an alias it will remain under that alias. Continuing the p= rior example >=20 >> import tests\foo as boo from "./src/mymodule"; >>=20 >> boo() >>=20 > That's enough to chew on I think. =E2=80=94 Rob --32b5ec5794f848478c7504b55e735e55 Content-Type: text/html;charset=utf-8 Content-Transfer-Encoding: quoted-printable

=
On Sat, Jun 29, 2024, at 08:32, Michael Morris wrote:
=
Not replying to anyone in particular and instead doing a = mild reset taking into account the discussion that has gone before.=  

So, I want to import a p= ackage. I'll create an index.php file at the root of my website and popu= late it with this.

<= div>
<?php<= /span>
import "./src/mymodule";

Now I'll create that directory an= d run a command `php mod init` in that directory. Stealing this from Go,= it's fairly straightforward though.  Now if we look in the directo= ry we will see two files.

php.mod
php.sum

The secon= d file I'll not be touching on but exists to track checksums of download= ed packages - Composer does the same with its composer-lock.json file wh= ich in turn was inspired by node's package-lock.json.

I don't think that = is correct... package-lock.json didn't come about until what, 2016-7ish?= with pressure from yarn which did a yarn.lock file. Pretty sure compose= r was doing that since the beginning. I remember this being a BIG reason= we switched from npm to yarn when it came out, because dev A would have= different versions of libraries than dev B. Bug hunting was FUN when it= was in a library.

=

The php.mod file stands in for composer.json, but it= isn't a json file.  It would start something like this:
<= div>
namespace mymodule
php 10.0

We start with three dire= ctives - the root namespace is presumed to be the directory name. If tha= t isn't true this is a text file, change it.  PHP min version shoul= d be straightforward. Registry details where we are going to go get code= from.  Suppose we want to use our own registry but fallback to pac= kagist.  That would be this:

namespace mymodule
php 10.0
registry (<= /span>
)
 
Multiple re= gistry entries will be checked for the code in order.  Handling aut= h tokens for restricted registries is outside of scope at the moment.

While this looks good on p= aper, you're going to have to standardize how packages are accessed (API= calls, etc) so they can be used in this file, or literally anyone who w= ants to add a competing registry will have to create an RFC to allow acc= essing their own registry, which is a ton of politics for something that= is strictly technical -- not to mention a bunch of if-this-registry-do-= that type statements scattered throughout the code, which makes it harde= r to maintain.


So let's build the module= . We'll make a file called hello.phm.  The reason for phm and not p= hp is so that web SAPIs will not try to parse this code.  Further t= hey can be configured to not even allow direct https access to these fil= es at all.

import "twig/twig= ";
<= span class=3D"font" style=3D"font-family:monospace;">use \Twig\Loader\Ar= rayLoader;
use \Twig\Environ= ment;

= $loader =3D new Ar= rayLoader([
  'index' =3D= > 'Hello {{ name }}'
]);

$twig =3D new Environment= ($loader);

=
export $twig;=

SAPIs are the programs that parse ALL php code and return it= to the server (ie, nginx, apache, caddy, etc) to be displayed. The SAPI= absolutely needs to parse these files in order to execute them. Servers= are designed to display files, so any server configured today will just= output the contents of these files because it won't be configured to se= nd the request to the SAPI instead. It's better to suggest moving these = files out of the web-root so it's a non-issue.

<= div>In other news, I'm not a fan of how many times I have to write "twig= " just to get Twig in the current file. The module already registers a n= amespace, why can't the use-statement implicitly import the module?

As mentioned in previous discussions, modules have their own = variable scope.  Back in our index we need to receive the variable<= br>

<?php
import $twig from "./src/mymodule"=

$twig->render('index', ['name= ' =3D> 'World']);

&n= bsp;If we load index.php in the web browser we should see "Hello World".=   If we look back in the mymodules folder we'll see the php.mod fil= e has been updated

In re= al life, my code is going to be in a module/framework and I'm going to n= eed to render it there. This example of exporting a dependency also kind= a breaks encapsulation principles, and even though it is an example, thi= ngs like this end up in documentation of a feature and cause all kinds o= f bad practices (like Symfony and anemic objects).

<= blockquote type=3D"cite" id=3D"qt" style=3D"">

=
namespace mymodule
=
php 10.0

imports (=
  twig/twig v3.10.3
  symfony/deprecation-contr= acts v2.5 //indirect
  = symfony/polyfill-mbstring v1.3 //indirect
<= blockquote style=3D"margin-top:0px;margin-right:0px;margin-bottom:0px;ma= rgin-left:40px;border-top-width:initial;border-right-width:initial;borde= r-bottom-width:initial;border-left-width:initial;border-top-style:none;b= order-right-style:none;border-bottom-style:none;border-left-style:none;b= order-top-color:initial;border-right-color:initial;border-bottom-color:i= nitial;border-left-color:initial;border-image-source:initial;border-imag= e-slice:initial;border-image-width:initial;border-image-outset:initial;b= order-image-repeat:initial;padding-top:0px;padding-right:0px;padding-bot= tom:0px;padding-left:0px;">
  symfony/polyfill-php80 v1.22 //indirect
<= /div>
)

Note the automatically entered comment that marks the imported de= pendencies of twig. Meanwhile the php.sum file will also be updated with= the checksums of these packages.

<= /div>
One of the first things I do in a composer.json file is remove= polyfills through the replace key. It's unnecessary, annoys me in my ID= E with having multiple classes of the same name, and hides the fact that= I should probably install an extension for better performance. How do w= e do that with this new setup?

In fact, it = is worth pointing out that how would this system work with polyfills in-= general? Polyfills have their uses -- especially for library/framework c= ode where you don't control the runtime environment. Like how would some= one polyfill mb_string since people will be adding `import @mbstring` an= d not `import symfony/polyfill-mbstring`?


So why this instead of composer?  Well, a native implementati= on should be faster, but also it might be able to deal with php extensio= ns.

<= span class=3D"font" style=3D"font-family:monospace;">import "@php_mysqli= "

The @ mark= s that the extension is either a .so or .dll library, as I'll hazard a g= uess that the resolution mechanic will be radically different from the p= hp language modules themselves - if it is possible at all. If it can be = done it will make working with packages that require extensions a hell o= f a lot easier since it will no longer be necessary to monkey the php.in= i file to include them. At a minimum the parser needs to know that the i= mport will not be in the registry and instead it should look to the exte= nsions directory, hence the lead @. Speaking of, having the extensi= on directory location be a directive of php.mod makes sense here.  = Each module can have its own extension directory, but if this is kept wi= thin the project instead of globally then web SAPIs definitely need to s= tay out of those directories.
So ... if we want to round, we have to use `import @math` an= d then we can call the global round() function? Or if we want to use Dat= eTimeImmutable we have to add `import @date`? That seems like a step in = the wrong direction since most people don't even know that most (if not = all) global library functions come from extensions -- and virtually nobo= dy knows the name of each extension and what functions they have. Also, = installing extensions is not 100% straightforward as some environments n= eed to use pecl, some need to use OS package managers.

=
Final thing to touch on is how the module namespaces beha= ve. The export statement is used to call out what is leaving the module = - everything else is private to that module. 

class A {}  // private
=
export class B {}  // public

Al= l the files of the package effectively have the same starting namespace = - whatever was declared in php.mod.  So it isn't necessary to repea= t the namespace on each file of the package. If a namespace is given, it= will be a sub-namespace

= namespace tests;

export function foo() {}

Then in the i= mporting file

<= div>
import ".= /src/mymodule"
us= e \mymodule\tests\foo


Notice here that if there is no from claus= e everything in the module grafts onto the symbol table.  Subsequen= t file loads need only use the use statement. Exported variables however= must be explicitly pulled because the variable symbol table isn't affec= ted by namespaces (if I recall correctly, call me an idiot if I'm wrong)= .

The from clause is useful for perma= nently aliasing - if something is imported under an alias it will remain= under that alias. Continuing the prior example

=
import tests\foo as boo from "./src/mymodule";<= /span>

boo()

That's enough to chew on= I think.

=E2=80=94 Rob
--32b5ec5794f848478c7504b55e735e55--