Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124019 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 D49281ADDD0 for ; Sat, 29 Jun 2024 09:40:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1719654091; bh=XE74ljJIok0G1hCGNhY80YvXpi8jz11CGaCiD9sp4e8=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=WrI8HjXnGqqhNgjYqyjbKubZuN9xO4/VjofLnVdxWc6o6jW+TBKn75jpsOLJhlerD FkGgfoyN5TVd9zgYapRj/iVJv0kPiN66b9zyUnWnkX6Pqa8VHmoejgAY5owCz6hzvF oJd9VKFrQu+1MlVET0YDs/Jhdt/ncln8Mb0EpI/fVMUoNwPmpraBFphQrtEtIx9eBw Yr3nY1WKphwJCH+rcAvoVUVBSkp/aC0wYbQ649OzyrC4MEUi7Ll76u4BRqN/zvQwHO MRNMMJ21BrzeCkEvbnRSz5TjrE05oOcjMM0N/b6zzON45Pdy9xCKPynDsttXcbZ6bs 8yPoAmsOYUy3Q== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CEBF21809E4 for ; Sat, 29 Jun 2024 09:41:27 +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.8 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DMARC_MISSING,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,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-yw1-f177.google.com (mail-yw1-f177.google.com [209.85.128.177]) (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 09:41:19 +0000 (UTC) Received: by mail-yw1-f177.google.com with SMTP id 00721157ae682-63bca6db118so12540207b3.3 for ; Sat, 29 Jun 2024 02:40:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20230601.gappssmtp.com; s=20230601; t=1719653999; x=1720258799; darn=lists.php.net; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=/zL6Eu61/IktPvnKDeYHbJBo5rUX11NG1m+3QyzSwb0=; b=z2Ek/JVM9yPS62sxyawFwMJdmFky14WeCSeWm3kTFJFtbmYT0c1M6SGiVgsb49RiTe J+zCBXUB1W3E0BdTa+oBSTUNcDj4md1tnYG9ETJo13Kp0W7uoBub5Urqc1W0YCqvv5YR RrFqGDSLlU9+oYwS73MDDHlTQU3LnEmmyVXb/cFzEYoYEkzFHxbeueEbN7saeyrfM3y3 0sNYdOVSC/v5Ari9pa8xwO6Cg0pvvXqlw/JF61AYMMSjSLvs7IG3TWgBdUNT1y6gE5t7 jLjWXEw9Yi/i4TDToNMnjVmg9crJIg1vi8tY2H9RpwqyVyOXr/Eq2o+9BG6GcNRp9ceD 8UWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719653999; x=1720258799; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=/zL6Eu61/IktPvnKDeYHbJBo5rUX11NG1m+3QyzSwb0=; b=FKcMDeL1Avpmbjpzp2sUsbrtwu+/8FeHPjbRG+YTh0Pzk7mXYdeU9PX/Ydkc72iM+F Chr6rhevjd9TuaMKwLu5EbyPNRXHor88IpKqyBSIrdroCTQEmpdXADg1KQY5ViLLtgsp +m5jHfShXfMmAzkOWigL1J+D3WFBxxH/nBhso1Lo9UkrXS2inlHWaksDieHObrsAuY0X ms2M3dey7fxdSmQU9nNUjshbyT2K70QaGR1eHhEPbfxt8/nbhLKAXeKCiL41AvyVf8Ee nE2zo9H4Fs2K0V3iGpRUbX/CY5XU7FC5Mj04qx+hMI6+vK6OFkOWOgQeBd9m/hqSJpl1 Vsgg== X-Gm-Message-State: AOJu0Yz/FaOZVweEpo3OIEvw8f5/j6QJPFfWDndZGBtTeWOG6N7NdEna KJBStvlwUjIH7KwfETmJtMJvGnXiXd9Fe0mSh5DkMEX9smmdhbOrMl/U8MVE2MsfkUt8aknU8Y/ YyvM= X-Google-Smtp-Source: AGHT+IHBJP9Ov4uuuOZjG/JCO0qmt1OpC1kXPI+YQpyGhJB6vb0Td6hm+u8YvEI+jyXYmdxD+QcFxg== X-Received: by 2002:a05:690c:b0f:b0:64a:7040:2d8e with SMTP id 00721157ae682-64c72f397bamr6917497b3.33.1719653999407; Sat, 29 Jun 2024 02:39:59 -0700 (PDT) Received: from smtpclient.apple (c-98-252-216-111.hsd1.ga.comcast.net. [98.252.216.111]) by smtp.gmail.com with ESMTPSA id 00721157ae682-64a9c310859sm6235077b3.127.2024.06.29.02.39.57 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 29 Jun 2024 02:39:57 -0700 (PDT) Message-ID: <39B496F8-062E-4848-9B3B-529BE8D3415A@newclarity.net> Content-Type: multipart/alternative; boundary="Apple-Mail=_A6D5F754-A3F1-48E8-8E08-C7189AF6983C" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3696.120.41.1.8\)) Subject: Re: [PHP-DEV] [Initial Feedback] PHP User Modules - An Adaptation of ES6 from JavaScript Date: Sat, 29 Jun 2024 05:39:57 -0400 In-Reply-To: Cc: php internals To: Michael Morris References: <1917CF7C-26D8-4DBE-B05C-5AA650AC6C9F@rwec.co.uk> <551cd5b0-1c00-4818-a9ca-97f6b7e8c3dc@app.fastmail.com> X-Mailer: Apple Mail (2.3696.120.41.1.8) From: mike@newclarity.net (Mike Schinkel) --Apple-Mail=_A6D5F754-A3F1-48E8-8E08-C7189AF6983C Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Jun 29, 2024, at 2:32 AM, Michael Morris = wrote: >=20 > Not replying to anyone in particular and instead doing a mild reset = taking 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 = root 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 = that 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.json file which in turn was inspired by node's = package-lock.json. >=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. = Handling auth tokens for restricted registries is outside of scope at = the moment. That is very Go-like, as you stated.=20 However, be aware that in a Go project repo you are likely to have only = one `go.mod` =E2=80=94 or multiple if you have numerous CLI apps being = generated =E2=80=94 whereas every directory with Go code is a package = (which I think is equivalent to what you are calling "module." So I think your use of them here is conflating the two concepts. One is = a project-wide concept and the other is a "package" concept. Maybe you would be better to adopt `module` to mean project and = `package` to mean packaged code as Go has them? =46rom here on I will refer to directory rather than module or package = to avoid confusion. By directory I will mean what Go calls a "package" = and what I think your original proposal called a "module." A big difference between Go and PHP is that Go have a compiler that = compiles into an executable before it runs. That is clearly not = compatible with PHP, and why I was proposing that each directory could = have a pre-compiled `.php.module` that could be pre-compiled, or = compiled on the fly at first import. Also, it is problematic to have `php.mod` and `php.sum` because web = servers would serve them if not carefully configured hence why I went = with a leading dot, e.g. `.php.module` >=20 > So let's build the module. We'll make a file called hello.phm. The = reason 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 access 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 > 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']); Aside from being familiar per Javascript, what is the argument to = requiring the import of specific symbols vs just a package import, e.g.: twig->render('index', ['name' =3D> 'World']); To me is seems to just add to boilerplate required. Note that having = `mymodule` everywhere you reference `twig` makes code a lot more = self-documenting, especially on line 999 of a PHP file. =F0=9F=99=82 >=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 >=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 > ) Having a `php.sum` file is interesting but again, it should start with a = period if so. That said, I wonder if incorporating versioning does not make the scope = of modules too big to complete? > Note the automatically entered comment that marks the imported = dependencies of twig. Meanwhile the php.sum file will also be updated = with the checksums of these packages. >=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" I would like this, but I think hosting vendors would block it since = extensions can have C bugs and create vulnerabilities for servers. I have long thought PHP should kick off a new type of extension using = WASM, which can be sandboxed.=20 But I digress. >=20 > The @ marks that the extension is either a .so or .dll library, as = I'll hazard a guess that the resolution mechanic will be radically = different from the php 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 of a lot easier since it will no longer be = necessary to monkey the php.ini file to include them. At a minimum the = parser needs to know 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. >=20 > Final thing to touch on is how the module namespaces behave. The = export 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 = namespace - 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 = grafts onto the symbol table. Subsequent file loads need only use the = use statement. Exported variables however must be explicitly pulled = because the variable symbol table isn't affected by namespaces (if I = recall correctly, call me an idiot if I'm wrong). >=20 > The from clause is useful for permanently aliasing - if something is = imported under an alias it will remain under that alias. Continuing the = prior example >=20 > import tests\foo as boo from "./src/mymodule"; >=20 > boo() >=20 > That's enough to chew on I think. I don't think it is wise to intertwine this concept of modules with = namespaces like that, but I am replied out for the night. :-) -Mike --Apple-Mail=_A6D5F754-A3F1-48E8-8E08-C7189AF6983C Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8
On = Jun 29, 2024, at 2:32 AM, Michael Morris <tendoaki@gmail.com> = 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 = package. I'll create an index.php file at the root of my website and = populate it with this.

<?php
import = "./src/mymodule";

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

php.mod
php.sum

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.json file which in turn was inspired by node's = package-lock.json.

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

namespace = mymodule
php = 10.0

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:

namespace = mymodule
php = 10.0
registry = (
)
 
Multiple registry entries will be checked for the code in = order.  Handling auth tokens for restricted registries is outside = of scope at the moment.

That is very Go-like, as = you stated. 

However, be aware that in a = Go project repo you are likely to have only one `go.mod` =E2=80=94 or = multiple if you have numerous CLI apps being generated =E2=80=94 whereas = every directory with Go code is a package (which I think is equivalent = to what you are calling "module."

So I think your use of them = here is conflating the two concepts. One is a project-wide concept and = the other is a "package" concept.

Maybe you would be better to = adopt `module` to mean project and `package` to mean packaged code as Go = has them?

=46rom here on I will = refer to directory rather than module or package to avoid confusion. By = directory I will mean what Go calls a "package" and what I think your = original proposal called a "module."

A= big difference between Go and PHP is that Go have a compiler that = compiles into an executable before it runs. That is clearly not = compatible with PHP, and why I was proposing that each directory could = have a pre-compiled `.php.module` that could be pre-compiled, or = compiled on the fly at first import.

Also, it is problematic to have `php.mod` and = `php.sum` because web servers would serve them if not carefully = configured hence why I went with a leading dot, e.g. = `.php.module`


So let's build the module. We'll make a file called = hello.phm.  The reason 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 access to these files at all.

import = "twig/twig";
use = \Twig\Loader\ArrayLoader;
use = \Twig\Environment;

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

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

export = $twig;

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

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

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


Aside from being familiar per Javascript, what is the = argument to requiring the import of specific symbols vs just a package = import, e.g.:

<?php
=
import = "./src/mymodule"

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

To me = is seems to just add to boilerplate required.  Note that having = `mymodule` everywhere = you reference `twig` makes = code a lot more self-documenting, especially on line 999 of a PHP file. = =F0=9F=99=82


 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

namespace = mymodule
php = 10.0

imports = (
  twig/twig = v3.10.3
  symfony/deprecation-contracts v2.5 = //indirect
  symfony/polyfill-mbstring v1.3 = //indirect
  symfony/polyfill-php80 v1.22 = //indirect
)

Having a `php.sum` file is = interesting but again, it should start with a period if = so.

That said, I wonder if = incorporating versioning does not make the scope of modules too big to = complete?

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

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

import = "@php_mysqli"

I would like = this, but I think hosting vendors would block it since extensions can = have C bugs and create vulnerabilities for = servers.

I = have long thought PHP should kick off a new type of extension using = WASM, which can be sandboxed. 

But I digress.


The @ marks that the extension is = either a .so or .dll library, as I'll hazard a guess that the resolution = mechanic will be radically different from the php 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 of a lot easier = since it will no longer be necessary to monkey the php.ini file to = include them. At a minimum the parser needs to know 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.

Final thing to touch on is how the = module namespaces behave. 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

All the files of the = package effectively have the same starting namespace - 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

namespace = tests;

export function foo() = {}

Then in the = importing file

import = "./src/mymodule"
use = \mymodule\tests\foo


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

The = from clause is useful for permanently 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";

boo()

That's enough to chew on I = think.

I don't think it is = wise to intertwine this concept of modules with namespaces like that, = but I am replied out for the night. :-)

-Mike

= --Apple-Mail=_A6D5F754-A3F1-48E8-8E08-C7189AF6983C--