[RFC] Namespace-scoped declares, again

  101323
December 11, 2017 13:43 nikita.ppv@gmail.com (Nikita Popov)
Hi internals!

Some time ago I introduced the following proposal for namespace-scoped
declares:

    https://wiki.php.net/rfc/namespace_scoped_declares

The idea is to allow specifying declare directives for a whole library or
project using:

    namespace_declare('Vendor\Lib', ['strict_types' => 1]);

I've finally gotten around to implementing this proposal (
https://github.com/php/php-src/pull/2972) and would like to move forward
with it.

The reason why I'm picking it up again is some feedback I received for the
explicit call-time send-by-ref proposal. The main objection seems to be
that the feature has limited usefulness if it's optional rather than
required, because you still can't be sure that something is a by-value
pass, just because no & is present. At the same time, we can't make this
required anytime soon due to the large BC impact.

Namespace-scoped declares are perfectly suited to resolve this problem. We
can introduce a require_explicit_send_by_ref declare directive to make the
call-site annotation required, and libraries/projects can easily opt-in to
it using namespace_declare(). There would be no BC impact, while at the
same time projects could benefit from the additional clarity and
performance improvements immediately.

Thanks,
Nikita
  101324
December 11, 2017 15:46 pollita@php.net (Sara Golemon)
On Mon, Dec 11, 2017 at 8:43 AM, Nikita Popov ppv@gmail.com> wrote:
> Some time ago I introduced the following proposal for namespace-scoped > declares: > > https://wiki.php.net/rfc/namespace_scoped_declares > > The idea is to allow specifying declare directives for a whole library or > project using: > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > I've finally gotten around to implementing this proposal ( > https://github.com/php/php-src/pull/2972) and would like to move forward > with it. > Being lazy here because I'm literally on my way out the door and don't
have a moment to parse through the implementation, but can you clarify how this impact compile-time declares?
  101326
December 11, 2017 15:56 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Dec 11, 2017 at 4:46 PM, Sara Golemon <pollita@php.net> wrote:

> On Mon, Dec 11, 2017 at 8:43 AM, Nikita Popov ppv@gmail.com> > wrote: > > Some time ago I introduced the following proposal for namespace-scoped > > declares: > > > > https://wiki.php.net/rfc/namespace_scoped_declares > > > > The idea is to allow specifying declare directives for a whole library or > > project using: > > > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > > > I've finally gotten around to implementing this proposal ( > > https://github.com/php/php-src/pull/2972) and would like to move forward > > with it. > > > Being lazy here because I'm literally on my way out the door and don't > have a moment to parse through the implementation, but can you clarify > how this impact compile-time declares? > > > require('a.php"); > require('b.php"); // This file has a namespace declaration > require('c.php"); > > In this example, does a.php wind up getting compiled with a different > set of declares than b.php and c.php? > > -Sara >
Nope. The namespace_declare() call in this case will throw an Error, because code using that namespace has already been loaded. The implementation is careful to ensure that it's never possible to end up in a situation where the used declare directives are inconsistent. Nikita
  101325
December 11, 2017 15:55 markyr@gmail.com (Mark Randall)
On 11/12/2017 13:43, Nikita Popov wrote:
> Namespace-scoped declares are perfectly suited to resolve this problem. We > can introduce a require_explicit_send_by_ref declare directive to make the > call-site annotation required, and libraries/projects can easily opt-in to > it using namespace_declare().
Seems solid to me, I would definitely make use of this feature, as I found adding the declare to each file felt untidy and even a little clumsy. I certainly like the direction that PHP seems to be moving in terms of making the script structure more like a compiled application, rather than a set of independent scripts. If this doesn't pass, long term I think the main alternative would be to have a single statement that was like a quasi-include of declarations in another file, almost like a ".h" declare_import('/my/namespace/nsprefs.php'); -- Mark Randall
  101331
December 12, 2017 07:46 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

> The idea is to allow specifying declare directives for a whole library or > project using: > > namespace_declare('Vendor\Lib', ['strict_types' => 1]);
I am not sure I like this. Namespace is a prefix to a class name. Anybody can declare a class with any name, and the side-effect that they would inherit some context, which is completely invisible and would magically exist somewhere globally, does not seem like a good idea to me. Moreover, what if something - like older version of the same library or test or something like that - would have different idea about what that global state should be? How nice would it be if a piece of unrelated code could completely change how my code is interpreted? How nice would it be if whether it works or not would depend on in which order classes were loaded in this particular request? Hidden global context has the same smell as php.ini settings, and for the same reason - but this, as far as I can see, will also be hidden and depending on file loading order, class loading order, etc. which in big applications can lead to a lot of fun debugging why some code randomly produces fatal errors... Debugging PHP is fun enough without quantum entanglement-like effects :)
> The reason why I'm picking it up again is some feedback I received for the > explicit call-time send-by-ref proposal. The main objection seems to be > that the feature has limited usefulness if it's optional rather than > required, because you still can't be sure that something is a by-value > pass, just because no & is present. At the same time, we can't make this > required anytime soon due to the large BC impact.
Where "soon" means "for all practical purposes, forever, unless we stop calling that new thing PHP", IMO.
> Namespace-scoped declares are perfectly suited to resolve this problem. We
I don't think so. First of all, I don't think, of course, that this problem needs to be solved by adding more complexity to save a purely cosmetic feature. But second of all, I don't think global hidden context that could change at a distance how the code is interpreted is a good idea regardless of whether it solves the issues with send-by-ref. -- Stas Malyshev smalyshev@gmail.com
  101334
December 12, 2017 13:43 nikita.ppv@gmail.com (Nikita Popov)
On Tue, Dec 12, 2017 at 8:46 AM, Stanislav Malyshev <smalyshev@gmail.com>
wrote:

> Hi! > > > The idea is to allow specifying declare directives for a whole library or > > project using: > > > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > I am not sure I like this. Namespace is a prefix to a class name. > Anybody can declare a class with any name, and the side-effect that they > would inherit some context, which is completely invisible and would > magically exist somewhere globally, does not seem like a good idea to > me. Moreover, what if something - like older version of the same library > or test or something like that - would have different idea about what > that global state should be? How nice would it be if a piece of > unrelated code could completely change how my code is interpreted? How > nice would it be if whether it works or not would depend on in which > order classes were loaded in this particular request? >
The way PHP works, it's not possible to have two versions of a library loaded at the same time. There are projects like https://github.com/humbug/php-scoper to allow loading a library multiple times, which work by prefixing all namespaces. Of course, in this case namespace_declares() would also be applied to different namespaces and there will be no interference. There is no dependence on loading order. The implementation is careful to ensure that the used declare state is consistent. It's not possible to call namespace_declare() twice on the same namespace. It's not possible to first load some files from a namespace, do the namespace_declare() call and then load some more files. If a namespace_declare() call succeeds without throwing an error, then that is the ground truth, without any dependence on order or other calls.
> Hidden global context has the same smell as php.ini settings, and for > the same reason - but this, as far as I can see, will also be hidden and > depending on file loading order, class loading order, etc. which in big > applications can lead to a lot of fun debugging why some code randomly > produces fatal errors... Debugging PHP is fun enough without quantum > entanglement-like effects :) >
As said above, the implementation makes sure that all quantum state is collapsed ;) The only possible mistake that can occur is that the namespace_declare() call doesn't happen at all, but anything depending on loading order or conflicting calls is not possible. The big issue with ini settings is (mostly) not the "hidden" part, it's the "global" part. Ini settings that change language behavior are tabu because they apply to everything, so no library can assume any particular ini configuration. Namespace-scoped declares explicitly avoid this. A library can namespace_declare() it's configuration and be sure that this is the configuration it's going to get, at the same time not interfering with any other library or code that uses a different configuration.
> The reason why I'm picking it up again is some feedback I received for the > > explicit call-time send-by-ref proposal. The main objection seems to be > > that the feature has limited usefulness if it's optional rather than > > required, because you still can't be sure that something is a by-value > > pass, just because no & is present. At the same time, we can't make this > > required anytime soon due to the large BC impact. > > Where "soon" means "for all practical purposes, forever, unless we stop > calling that new thing PHP", IMO. > > > Namespace-scoped declares are perfectly suited to resolve this problem. > We > > I don't think so. First of all, I don't think, of course, that this > problem needs to be solved by adding more complexity to save a purely > cosmetic feature. But second of all, I don't think global hidden context > that could change at a distance how the code is interpreted is a good > idea regardless of whether it solves the issues with send-by-ref. >
I haven't thought too carefully about whether having an option for the explicit-send-by-ref feature *specifically* would be beneficial, but I think it's very important to at least have the option on the table. Too many issues in PHP cannot be solved for reasons of backwards-compatibility. We need to have *some* way to evolve the language without BC breaks, and I think namespace-declares are an elegant way to do this. Regards, Nikita
  101340
December 12, 2017 23:14 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

> There is no dependence on loading order. The implementation is careful > to ensure that the used declare state is consistent. It's not possible > to call namespace_declare() twice on the same namespace. It's not > possible to first load some files from a namespace, do the > namespace_declare() call and then load some more files. If a
This means that namespace_declare can be only in one file, and if any mention of any namespace class has been made before that file has been loaded, then the declare would fail. That is loading order dependency - if A.php contains namespace_declare and B.php contains another class of the same namespace, then order (A, B) works but order (B, A) does not.
> The big issue with ini settings is (mostly) not the "hidden" part, it's > the "global" part. Ini settings that change language behavior are tabu
Exactly. And this proposal adds another global state, which is also invisible, so figuring out what the state is when debugging is... fun. It's better than php.ini as it's "namespaced php.ini" but still suffers from the same problems within the namespace (and namespaces can be pretty big).
> this. A library can namespace_declare() it's configuration and be sure > that this is the configuration it's going to get, at the same time not > interfering with any other library or code that uses a different > configuration.
This assumes each namespace is homogeneous code always belonging to the same library and nothing is every added to that namespace. This is not always the case. -- Stas Malyshev smalyshev@gmail.com
  101342
December 13, 2017 00:16 andreas@dqxtech.net (Andreas Hennings)
I agree with all of Stanislav's emails in this thread so far.

On 12 December 2017 at 14:43, Nikita Popov ppv@gmail.com> wrote:
> On Tue, Dec 12, 2017 at 8:46 AM, Stanislav Malyshev <smalyshev@gmail.com> > wrote: > >> Hi! >> >> > The idea is to allow specifying declare directives for a whole library or >> > project using: >> > >> > namespace_declare('Vendor\Lib', ['strict_types' => 1]); >> >> I am not sure I like this. Namespace is a prefix to a class name. >> Anybody can declare a class with any name, and the side-effect that they >> would inherit some context, which is completely invisible and would >> magically exist somewhere globally, does not seem like a good idea to >> me. Moreover, what if something - like older version of the same library >> or test or something like that - would have different idea about what >> that global state should be? How nice would it be if a piece of >> unrelated code could completely change how my code is interpreted? How >> nice would it be if whether it works or not would depend on in which >> order classes were loaded in this particular request? >> > > The way PHP works, it's not possible to have two versions of a library > loaded at the same time.
Really? PHP does not even know what a "library" is. PHP sees files, directories and namespaces. But it does not know how to conceptualize any of this as a "library". With Composer you get the notion of a "package". In general, Composer would prevent you from using different versions of the same package. But you could manually download two versions of a package, and wire up the class loader in a way that it would load some classes from one version, and some classes from another version. E.g. if a class does not exist in version 2, load the class from version 1 instead. PHP as a language should not assume that someone is using Composer correctly. And even if we avoid two package versions coexisting: There could easily be two distinct packages that use the same namespace. We could discuss if this is a good idea, but it should not be the job of PHP as a language to make this situation difficult.
> > There is no dependence on loading order. The implementation is careful to > ensure that the used declare state is consistent. It's not possible to call > namespace_declare() twice on the same namespace. It's not possible to first > load some files from a namespace, do the namespace_declare() call and then > load some more files. If a namespace_declare() call succeeds without > throwing an error, then that is the ground truth, without any dependence on > order or other calls.
So by "is not possible", in fact you mean "will trigger an error". I imagine with Composer we would have to agree on a canonical file name where a namespace_declare() would be found. Maybe something like in src/Acme/Animal.namespace.php. The class loader would then have to make sure that this file is included before the first class from that namespace is included. Or alternatively, Composer init script would have to include all such files at the beginning of the process. If Composer (or whichever tool one wants to use) would include class file src/Acme/Animal/Cat.php without prior inclusion of the Animal.namespace.php, the class file would be interpreted without the desired setting. If the process then continues without ever including Animal.namespace.php, it will silently misbehave. If, however, Animal.namespace.php is included some time later in the process, then it would notice that something went wrong, and trigger an error.
> I haven't thought too carefully about whether having an option for the > explicit-send-by-ref feature *specifically* would be beneficial, but I > think it's very important to at least have the option on the table. Too > many issues in PHP cannot be solved for reasons of backwards-compatibility. > We need to have *some* way to evolve the language without BC breaks, and I > think namespace-declares are an elegant way to do this.
So if you want a setting for explicit-send-by-ref, why not do this per file, as we already do for strict_types? If at some day in the future we find that the declares become too verbose, we could bundle multiple declares into a new setting. E.g. something like "declare(php=8.1);" to combine all the declares that would be considered default in PHP 8.1. Or introduce some other shortcut like "
  101343
December 13, 2017 04:04 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
2017-12-13 1:16 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>:

> I agree with all of Stanislav's emails in this thread so far. > > On 12 December 2017 at 14:43, Nikita Popov ppv@gmail.com> wrote: > > On Tue, Dec 12, 2017 at 8:46 AM, Stanislav Malyshev <smalyshev@gmail.com > > > > wrote: > > > >> Hi! > >> > >> > The idea is to allow specifying declare directives for a whole > library or > >> > project using: > >> > > >> > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > >> > >> I am not sure I like this. Namespace is a prefix to a class name. > >> Anybody can declare a class with any name, and the side-effect that they > >> would inherit some context, which is completely invisible and would > >> magically exist somewhere globally, does not seem like a good idea to > >> me. Moreover, what if something - like older version of the same library > >> or test or something like that - would have different idea about what > >> that global state should be? How nice would it be if a piece of > >> unrelated code could completely change how my code is interpreted? How > >> nice would it be if whether it works or not would depend on in which > >> order classes were loaded in this particular request? > >> > > > > The way PHP works, it's not possible to have two versions of a library > > loaded at the same time. > > Really? > PHP does not even know what a "library" is. PHP sees files, > directories and namespaces. But it does not know how to conceptualize > any of this as a "library". > > With Composer you get the notion of a "package". > In general, Composer would prevent you from using different versions > of the same package. > But you could manually download two versions of a package, and wire up > the class loader in a way that it would load some classes from one > version, and some classes from another version. > E.g. if a class does not exist in version 2, load the class from > version 1 instead. > > PHP as a language should not assume that someone is using Composer > correctly. > > And even if we avoid two package versions coexisting: There could > easily be two distinct packages that use the same namespace. > We could discuss if this is a good idea, but it should not be the job > of PHP as a language to make this situation difficult. > > > > > There is no dependence on loading order. The implementation is careful to > > ensure that the used declare state is consistent. It's not possible to > call > > namespace_declare() twice on the same namespace. It's not possible to > first > > load some files from a namespace, do the namespace_declare() call and > then > > load some more files. If a namespace_declare() call succeeds without > > throwing an error, then that is the ground truth, without any dependence > on > > order or other calls. > > So by "is not possible", in fact you mean "will trigger an error". > > I imagine with Composer we would have to agree on a canonical file > name where a namespace_declare() would be found. > Maybe something like in src/Acme/Animal.namespace.php. > > The class loader would then have to make sure that this file is > included before the first class from that namespace is included. > Or alternatively, Composer init script would have to include all such > files at the beginning of the process. > > If Composer (or whichever tool one wants to use) would include class > file src/Acme/Animal/Cat.php without prior inclusion of the > Animal.namespace.php, the class file would be interpreted without the > desired setting. > If the process then continues without ever including > Animal.namespace.php, it will silently misbehave. > If, however, Animal.namespace.php is included some time later in the > process, then it would notice that something went wrong, and trigger > an error. > > If we're going to introduce someday a namespace file, then IMO it should
not be putted outside namespace folder. For eg class Acme\Animal\Cat in src/Acme/Animal/Cat.php should have namespace file in src/Acme/Aniimal/namespace.php or even more src/Acme/Animal/ns.php Why? Because users use PSR-4 so then they're src folder looks more like: src/Cat.php <-- class Acme\Animal\Cat src/ns.php <-- that should be then a place for namespace declare or even function and constants. Such namespace file can be a good place for namespace function and constants declaration. Also I think there is no need for another global function named `namespace_declare` if we had namespace file then we can utilise declare for that. Lets imagine such syntax: // src/Acme/Animal/ns.php > I haven't thought too carefully about whether having an option for the > > explicit-send-by-ref feature *specifically* would be beneficial, but I > > think it's very important to at least have the option on the table. Too > > many issues in PHP cannot be solved for reasons of > backwards-compatibility. > > We need to have *some* way to evolve the language without BC breaks, and > I > > think namespace-declares are an elegant way to do this. > > > So if you want a setting for explicit-send-by-ref, why not do this per > file, as we already do for strict_types? > > If at some day in the future we find that the declares become too > verbose, we could bundle multiple declares into a new setting. > E.g. something like "declare(php=8.1);" to combine all the declares > that would be considered default in PHP 8.1. > > Or introduce some other shortcut like " opening tag. > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  101344
December 13, 2017 04:24 andreas@dqxtech.net (Andreas Hennings)
On 13 December 2017 at 05:04, Michał Brzuchalski <michal@brzuchalski.com> wrote:
> > If we're going to introduce someday a namespace file, then IMO it should not > be putted outside namespace folder. > For eg class Acme\Animal\Cat in src/Acme/Animal/Cat.php should have > namespace file in src/Acme/Aniimal/namespace.php > or even more src/Acme/Animal/ns.php > Why? Because users use PSR-4 so then they're src folder looks more like: > src/Cat.php <-- class Acme\Animal\Cat > src/ns.php <-- that should be then a place for namespace declare or even > function and constants.
You are right, my previous proposal src/Acme/Animal.namespace.php would not work universally. But your proposal, src/Acme/Animal/ns.php clashes with the class file for class \Acme\Animal\ns. We would need something other than NAME.php. E.g. src/Acme/Animal/ns.inc.php But then Composer would still need to make sure that this file is always included before any class files from this directory. On language level we cannot assume that Composer is being used, and that it is being used correctly. So again this would be fragile.
> > Such namespace file can be a good place for namespace function and constants > declaration. > Also I think there is no need for another global function named > `namespace_declare` if we had namespace file > then we can utilise declare for that. > Lets imagine such syntax: > > // src/Acme/Animal/ns.php > > namespace Acme\Animal declare(strict_types=1,another_option=0); > const CAT = 1; > function createCat() : Cat {}
This means you are changing the meaning of existing declare() to apply to the entire namespace? Or to the entire directory? Or maybe the difference here is that there is no semicolon directly after the namespace declaration? I am not convinced by this syntax. But even if we would find a good syntax, the behavioral problems pointed out by Stanislav still apply.
  101345
December 13, 2017 04:38 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
2017-12-13 5:24 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>:

> On 13 December 2017 at 05:04, Michał Brzuchalski <michal@brzuchalski..com> > wrote: > > > > If we're going to introduce someday a namespace file, then IMO it should > not > > be putted outside namespace folder. > > For eg class Acme\Animal\Cat in src/Acme/Animal/Cat.php should have > > namespace file in src/Acme/Aniimal/namespace.php > > or even more src/Acme/Animal/ns.php > > Why? Because users use PSR-4 so then they're src folder looks more like: > > src/Cat.php <-- class Acme\Animal\Cat > > src/ns.php <-- that should be then a place for namespace declare or even > > function and constants. > > You are right, my previous proposal src/Acme/Animal.namespace.php > would not work universally. > > But your proposal, src/Acme/Animal/ns.php clashes with the class file > for class \Acme\Animal\ns. > > We would need something other than NAME.php. > E.g. src/Acme/Animal/ns.inc.php > > But then Composer would still need to make sure that this file is > always included before any class files from this directory. > On language level we cannot assume that Composer is being used, and > that it is being used correctly. > > So again this would be fragile. > > > > > Such namespace file can be a good place for namespace function and > constants > > declaration. > > Also I think there is no need for another global function named > > `namespace_declare` if we had namespace file > > then we can utilise declare for that. > > Lets imagine such syntax: > > > > // src/Acme/Animal/ns.php > > > > > namespace Acme\Animal declare(strict_types=1,another_option=0); > > const CAT = 1; > > function createCat() : Cat {} > > This means you are changing the meaning of existing declare() to apply > to the entire namespace? > Or to the entire directory? > > To entire namespace just like:
1, 'dynamic_object_properties' => 0, ... ]); namespace Acme\Animal declare( strict_types=1, dynamic_object_properties=0 ); // <-- this works same as namespace_declare call above namespace Acme\Machines { // here we have strict_types=0 turned off }
> Or maybe the difference here is that there is no semicolon directly > after the namespace declaration? > > I am not convinced by this syntax. > But even if we would find a good syntax, the behavioral problems > pointed out by Stanislav still apply. >
-- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  101347
December 13, 2017 05:04 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
2017-12-13 5:38 GMT+01:00 Michał Brzuchalski <michal@brzuchalski.com>:

> > > 2017-12-13 5:24 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>: > >> On 13 December 2017 at 05:04, Michał Brzuchalski <michal@brzuchalski.com> >> wrote: >> > >> > If we're going to introduce someday a namespace file, then IMO it >> should not >> > be putted outside namespace folder. >> > For eg class Acme\Animal\Cat in src/Acme/Animal/Cat.php should have >> > namespace file in src/Acme/Aniimal/namespace.php >> > or even more src/Acme/Animal/ns.php >> > Why? Because users use PSR-4 so then they're src folder looks more like: >> > src/Cat.php <-- class Acme\Animal\Cat >> > src/ns.php <-- that should be then a place for namespace declare or even >> > function and constants. >> >> You are right, my previous proposal src/Acme/Animal.namespace.php >> would not work universally. >> >> But your proposal, src/Acme/Animal/ns.php clashes with the class file >> for class \Acme\Animal\ns. >> >> We would need something other than NAME.php. >> E.g. src/Acme/Animal/ns.inc.php >> > Actually not true. `namespace` keyword is reserved and none class can have
such name. This means this example couses a syntax error: >> But then Composer would still need to make sure that this file is >> always included before any class files from this directory. >> On language level we cannot assume that Composer is being used, and >> that it is being used correctly. >> >> So again this would be fragile. >> >> > >> > Such namespace file can be a good place for namespace function and >> constants >> > declaration. >> > Also I think there is no need for another global function named >> > `namespace_declare` if we had namespace file >> > then we can utilise declare for that. >> > Lets imagine such syntax: >> > >> > // src/Acme/Animal/ns.php >> > > > >> > namespace Acme\Animal declare(strict_types=1,another_option=0); >> > const CAT = 1; >> > function createCat() : Cat {} >> >> This means you are changing the meaning of existing declare() to apply >> to the entire namespace? >> Or to the entire directory? >> >> > To entire namespace just like: > > > namespace_declare('Acme\Animal', [ > 'strict_types' => 1, > 'dynamic_object_properties' => 0, > ... > ]); > > namespace Acme\Animal declare( > strict_types=1, > dynamic_object_properties=0 > ); // <-- this works same as namespace_declare call above > > namespace Acme\Machines { > // here we have strict_types=0 turned off > } > > >> Or maybe the difference here is that there is no semicolon directly >> after the namespace declaration? >> >> I am not convinced by this syntax. >> But even if we would find a good syntax, the behavioral problems >> pointed out by Stanislav still apply. >> > > > > -- > regards / pozdrawiam, > -- > Michał Brzuchalski > about.me/brzuchal > brzuchalski.com >
-- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  101349
December 13, 2017 07:12 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
2017-12-13 6:04 GMT+01:00 Michał Brzuchalski <michal@brzuchalski.com>:

> > > 2017-12-13 5:38 GMT+01:00 Michał Brzuchalski <michal@brzuchalski.com>: > >> >> >> 2017-12-13 5:24 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>: >> >>> On 13 December 2017 at 05:04, Michał Brzuchalski <michal@brzuchalski.com> >>> wrote: >>> > >>> > If we're going to introduce someday a namespace file, then IMO it >>> should not >>> > be putted outside namespace folder. >>> > For eg class Acme\Animal\Cat in src/Acme/Animal/Cat.php should have >>> > namespace file in src/Acme/Aniimal/namespace.php >>> > or even more src/Acme/Animal/ns.php >>> > Why? Because users use PSR-4 so then they're src folder looks more >>> like: >>> > src/Cat.php <-- class Acme\Animal\Cat >>> > src/ns.php <-- that should be then a place for namespace declare or >>> even >>> > function and constants. >>> >>> You are right, my previous proposal src/Acme/Animal.namespace.php >>> would not work universally. >>> >>> But your proposal, src/Acme/Animal/ns.php clashes with the class file >>> for class \Acme\Animal\ns. >>> >>> We would need something other than NAME.php. >>> E.g. src/Acme/Animal/ns.inc.php >>> >> > Actually not true. `namespace` keyword is reserved and none class can have > such name. > This means this example couses a syntax error: > > namespace Acme\Animals; > class namespace {} > > That why we can assume we can utilise namespace.php file name. > And also why not trying to include it on autoload call? > For eg. > > new Acme\Animal\Cat(); // tries to load $file = "src/Acme/Animal/Cat.php" > > // but prevoiusly tries to load > require_once dirname($file) . DIRECTORY_SEPARATOR . 'namespace.php'; > > It's bad because it';s magic, but the `namespace.php` filename is still > available to use. > > Andreas, we're touching namespaces which is a hard subject, but if I could
fly away with my thoughts I'd propose to introduce something called for eg. a package and then divide it's name in class/function/const name with a single colon, for eg. Acme:Animal\Cat which gives additional information about package which then declares something and may be a good start for future scoped declarations. I've prepared a short gist to illustrate that https://gist.github.com/brzuchal/352ffce2717648f0d43f2d5a0c4bfb7b This solution doesn't require usage of Composer, but needs to pass an aotuloader function a type to load. There was a proposal on internal some time ago to pass additional parameter to load function.
> >>> But then Composer would still need to make sure that this file is >>> always included before any class files from this directory. >>> On language level we cannot assume that Composer is being used, and >>> that it is being used correctly. >>> >>> So again this would be fragile. >>> >>> > >>> > Such namespace file can be a good place for namespace function and >>> constants >>> > declaration. >>> > Also I think there is no need for another global function named >>> > `namespace_declare` if we had namespace file >>> > then we can utilise declare for that. >>> > Lets imagine such syntax: >>> > >>> > // src/Acme/Animal/ns.php >>> > >> > >>> > namespace Acme\Animal declare(strict_types=1,another_option=0); >>> > const CAT = 1; >>> > function createCat() : Cat {} >>> >>> This means you are changing the meaning of existing declare() to apply >>> to the entire namespace? >>> Or to the entire directory? >>> >>> >> To entire namespace just like: >> >> > >> namespace_declare('Acme\Animal', [ >> 'strict_types' => 1, >> 'dynamic_object_properties' => 0, >> ... >> ]); >> >> namespace Acme\Animal declare( >> strict_types=1, >> dynamic_object_properties=0 >> ); // <-- this works same as namespace_declare call above >> >> namespace Acme\Machines { >> // here we have strict_types=0 turned off >> } >> >> >>> Or maybe the difference here is that there is no semicolon directly >>> after the namespace declaration? >>> >>> I am not convinced by this syntax. >>> But even if we would find a good syntax, the behavioral problems >>> pointed out by Stanislav still apply. >>> >> >> >> >> -- >> regards / pozdrawiam, >> -- >> Michał Brzuchalski >> about.me/brzuchal >> brzuchalski.com >> > > > > -- > regards / pozdrawiam, > -- > Michał Brzuchalski > about.me/brzuchal > brzuchalski.com >
-- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  101390
December 16, 2017 23:18 rowan.collins@gmail.com (Rowan Collins)
On 13/12/2017 07:12, Michał Brzuchalski wrote:
> Andreas, we're touching namespaces which is a hard subject, but if I could > fly away with my thoughts > I'd propose to introduce something called for eg. a package
My thoughts were actually going along the same lines: basically, the current implementation of namespaces is, I think by design, very conservative in what a namespace represents. A namespace doesn't have any identity, and doesn't have any codified structure, it's just a shorthand for referring to similarly named classes (plus functions and constants). There are several things that I think would feel more natural with "packages" than namespaces as they exist today: - Declare directives, as discussed here - Visibility modifiers, e.g. a "private class", accessible only within its package - Per-package autoloaders, that only get called for classes in that package, rather than a global autoloader which parses out the prefixes it's interested in - An autoloader callback that fires once for each package, to setup these options Another difference is that namespaces tend to form deep hierarchies, but you're unlikely to set options at every level of the hierarchy. For instance, you probably don't want to set strict_types on for Acme\MyPackage\Input but off for Acme\MyPackage\Output. So a separate syntax to mark which level is the "package" would reduce the number of places a setting could be set, which would reduce the number of places you'd need to look - a bit like having Apache settings in a VirtualHost for the whole site, rather than .htaccess files in every directory. All that being said, I'm not 100% convinced this is solving a real problem. Yes, the option has to be set in every file, but that's because it effects the way that file is compiled, and the compiler sees one file at a time. The same applies to the "namespace" directive, which you might otherwise set against a particular directory. I don't see a pressing need to add the complexity. Regards, -- Rowan Collins [IMSoP]
  101351
December 13, 2017 10:43 TonyMarston@hotmail.com ("Tony Marston")
""Michal Brzuchalski""  wrote in message 
news:CABdc3WpomNLz+vX_m0B0wQ3uCiMiw8xw4Ea_sGD-PTDGfV-PbA@mail.gmail.com...
> >2017-12-13 1:16 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>: >
> >Why? Because users use PSR-4 so then they're src folder looks more like: ?
You are assuming that everybody is using PSR-4. That is a wrong assumption to make. -- Tony Marston
  101352
December 13, 2017 13:19 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
13.12.2017 11:44 "Tony Marston" <TonyMarston@hotmail.com> napisał(a):

""Michal Brzuchalski""  wrote in message news:CABdc3WpomNLz+vX_m0B0wQ3u
CiMiw8xw4Ea_sGD-PTDGfV-PbA@mail.gmail.com...


> 2017-12-13 1:16 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>: > >
> Why? Because users use PSR-4 so then they're src folder looks more like: > ?
You are assuming that everybody is using PSR-4. That is a wrong assumption to make. I didn't say everybody at all! Please read carefully. -- Tony Marston -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
  101353
December 13, 2017 13:53 TonyMarston@hotmail.com ("Tony Marston")
""Michal Brzuchalski""  wrote in message 
news:CABdc3Wrz8qu_hGsBTjGbWwcT8YpLxgChONeE9NiJKWcDEruySw@mail.gmail.com...
> >13.12.2017 11:44 "Tony Marston" <TonyMarston@hotmail.com> napisal(a): > >""Michal Brzuchalski"" wrote in message news:CABdc3WpomNLz+vX_m0B0wQ3u >CiMiw8xw4Ea_sGD-PTDGfV-PbA@mail.gmail.com... > > >> 2017-12-13 1:16 GMT+01:00 Andreas Hennings <andreas@dqxtech.net>: >> >> > > >> Why? Because users use PSR-4 so then they're src folder looks more like: >> >? > > >You are assuming that everybody is using PSR-4. That is a wrong assumption >to make. > > >I didn't say everybody at all! Please read carefully. >
I DID read carefully. You wrote "users use PSR-4" and not "those users who use PSR-4". You did not specify a subset of users, so this implies all users. -- Tony Marston
  101350
December 13, 2017 10:37 TonyMarston@hotmail.com ("Tony Marston")
"Andreas Hennings"  wrote in message 
news:CAH0Uv3FnYf_c7in4_6AmDO5pUZHsgU0m5scjU5oRz2kTAJ=+bw@mail.gmail.com...
> >I agree with all of Stanislav's emails in this thread so far. > >On 12 December 2017 at 14:43, Nikita Popov ppv@gmail.com> wrote: >> On Tue, Dec 12, 2017 at 8:46 AM, Stanislav Malyshev <smalyshev@gmail.com> >> wrote: >>
> >PHP as a language should not assume that someone is using Composer >correctly. >
Or even using Composer at all. -- Tony Marston
  101337
December 12, 2017 16:32 cmbecker69@gmx.de ("Christoph M. Becker")
On 11.12.2017 at 14:43, Nikita Popov wrote:

> Some time ago I introduced the following proposal for namespace-scoped > declares: > > https://wiki.php.net/rfc/namespace_scoped_declares > > The idea is to allow specifying declare directives for a whole library or > project using: > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > I've finally gotten around to implementing this proposal ( > https://github.com/php/php-src/pull/2972) and would like to move forward > with it.
Generally I'm not keen on letting the language diverge via whatever mechanism, but that has already happened and there seems to be some further need, and the namespace scoped declares appear to be preferable to per-file declares or even ini settings. What I don't like about the present proposal is that it introduces a proper function instead of a language construct. Is there a particular reason for this design decision? Couldn't we add the namespace scoped declares to the namespace declaration directly, e.g. namespace Foo declare strict_types=1; -- Christoph M. Becker
  101348
December 13, 2017 06:36 ivan.enderlin@hoa-project.net (Ivan Enderlin)
On 11.12.17 14:43, Nikita Popov wrote:
> Some time ago I introduced the following proposal for namespace-scoped > declares: > > https://wiki.php.net/rfc/namespace_scoped_declares > > The idea is to allow specifying declare directives for a whole library or > project using: > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > I've finally gotten around to implementing this proposal ( > https://github.com/php/php-src/pull/2972) and would like to move forward > with it. > > The reason why I'm picking it up again is some feedback I received for the > explicit call-time send-by-ref proposal. The main objection seems to be > that the feature has limited usefulness if it's optional rather than > required, because you still can't be sure that something is a by-value > pass, just because no & is present. At the same time, we can't make this > required anytime soon due to the large BC impact. > > Namespace-scoped declares are perfectly suited to resolve this problem. We > can introduce a require_explicit_send_by_ref declare directive to make the > call-site annotation required, and libraries/projects can easily opt-in to > it using namespace_declare(). There would be no BC impact, while at the > same time projects could benefit from the additional clarity and > performance improvements immediately. Thanks for the proposal.
While it seems comfortable for the user, and I understand the point you're trying to solve somehow, it can be a nightmare for the VM, the developer, and the user. I've few remarks and/or questions: * When parsing a file, the way the VM has to interprete/execute the file depends on a _runtime_ configuration defined in _another_ file. It makes things more implicit, and that's not good. * It can also be a nightmare for the developer. The behavior of their library can be changed by another library because there is no restriction about the location or usage of `namespace_declare`. If at least `namespace_declare` would only apply to the _current_ namespace, it might be better. * If `namespace_declare` is called twice for the same namespace, an error is raised, OK. It's easy to break someone's code by registering a file in an autoloader to load first, and call `namespace_declare` for that file. What the error will look like? It's important to prompt the correct culprit to the user. Any strategy to find which one is the culprit? * As you said in the RFC in the Proliferation of declare directives Section, it's not a good thing for the language to introduce more and more directives. PHP is living a time where it makes good things easier to do, and bad things harder to do. Everything I can imagine with `namespace_declare` is definitively not good for the language, the VM, and the ecosystem. Introducing a `strict` mode for the language is definitively a good thing to progressively make PHP stricter, yes, but I don't see a real need or a real motiviation behind `namespace_declare`, I for one see only dangers. So I'm sorry to say that —right now— I'm totally oppose to this RFC :-). Cheers. -Ivan.
  106295
July 25, 2019 13:34 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Dec 11, 2017 at 2:43 PM Nikita Popov ppv@gmail.com> wrote:

> Hi internals! > > Some time ago I introduced the following proposal for namespace-scoped > declares: > > https://wiki.php.net/rfc/namespace_scoped_declares > > The idea is to allow specifying declare directives for a whole library or > project using: > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > I've finally gotten around to implementing this proposal ( > https://github.com/php/php-src/pull/2972) and would like to move forward > with it. > > The reason why I'm picking it up again is some feedback I received for the > explicit call-time send-by-ref proposal. The main objection seems to be > that the feature has limited usefulness if it's optional rather than > required, because you still can't be sure that something is a by-value > pass, just because no & is present. At the same time, we can't make this > required anytime soon due to the large BC impact. > > Namespace-scoped declares are perfectly suited to resolve this problem. We > can introduce a require_explicit_send_by_ref declare directive to make the > call-site annotation required, and libraries/projects can easily opt-in to > it using namespace_declare(). There would be no BC impact, while at the > same time projects could benefit from the additional clarity and > performance improvements immediately. > > Thanks, > Nikita >
I plan to pick up this proposal again. I think the motivation here has become more pressing in the meantime. The strict_operators proposal is under discussion, and Rowan's "locked classes" essentially concluded with "doing this with a declare is better". I also think that this proposal will be helpful for the explicit call-site by-reference passing RFC. Based on the received feedback, I plan to change this proposal from being "namespace-scoped" to being "directory-scoped" instead. This should both resolve some of the concerns and make the proposal more flexible: Not all code sharing the same namespace is part of the same package (multiple packages may have classes in the same namespace), or not of the same type (code and tests may share a namespace) and sometimes people use incorrect namespaces out of ignorance (e.g. place their own Doctrine models under the Doctrine namespace). Making the declares apply to a directory avoids these issues. Nikita
  106302
July 27, 2019 08:33 rasmus@mindplay.dk (Rasmus Schultz)
On Thu, Jul 25, 2019 at 3:34 PM Nikita Popov ppv@gmail.com> wrote:

....
> Based on the received feedback, I plan to change this proposal from being > "namespace-scoped" to being "directory-scoped" instead
.... I think that's a better approach. But please consider doing this without adding another global function that defines more global (compiler) state? If it's going to be directory-scoped, using some kind of project root file, in my opinion, is the more traditional way to declare directory-scoped boundaries. For example, if you could just put a "php.json" file in the root of your namespace folder, this becomes very easy to spot next to your ".gitignore", "composer.json", "package.json", "tsconfig.json" and any other well-known project configuration files. Finding these in the root of any project/library is pretty intuitive - poking through bootstrapping scripts for a global function call, meh, not so much. (This file could be used to declare other repetitive things too - for one, why is the declare() situation any different from having to repeat a namespace declaration in every file? This is just another global setting that could just as well be declared once in a project root file. If this file were parsable or reflectable by e.g. Composer, this could probably be used to make the "autoload" section in Composer optional, for one.) (You could expand upon this feature in the future to add other compiler options - for instance, toggling global functions, which would allow for things like correcting inconsistent argument order in the standard libraries, toggling incompatible language features, toggling improved defaults for certain functions, a proper strict type-checking mode at some point in the future, etc...)
  106303
July 27, 2019 22:53 markyr@gmail.com (Mark Randall)
On 27/07/2019 09:33, Rasmus Schultz wrote:
> On Thu, Jul 25, 2019 at 3:34 PM Nikita Popov ppv@gmail.com> wrote: >> Based on the received feedback, I plan to change this proposal from being >> "namespace-scoped" to being "directory-scoped" instead
Personally I'm not a huge fan of using implicit hierarchical directories for managing defines. It's certainly better than global state functions, but in general I don't feel comfortable with the idea that an arbitrary file can effect the behaviour of the VM for particular files that does not directly reference it. Obviously there's lots of functions that do that, set_error_handler etc, but I think it is best avoided wherever possible. I think what the situation calls for is the PHP equivalent of #include - we need to be able to explicitly import the definitions from another file. ====== MyProject\Models\Test\SomeClass.php ======= ```
  106306
July 28, 2019 03:17 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

> I plan to pick up this proposal again. I think the motivation here has > become more pressing in the meantime. The strict_operators proposal is > under discussion, and Rowan's "locked classes" essentially concluded with > "doing this with a declare is better". I also think that this proposal will > be helpful for the explicit call-site by-reference passing RFC.
I think it's a wrong way to make semantics of the language to be different in different libraries. We add one option, than another, than another, and soon we have 512 variants of the language, all slightly different, and you have to keep track which one you are dealing with on each file, and God forbid you move a piece of code from one library to another, you have to rewrite half of it because it was written in PHP #243 but this library is actually PHP #116. I know there is not a consensus about usage of some newly proposed language features. I think introducing this kind of fragmentation to deal with it is exactly wrong way to deal with it. It leads to code from different codebases being incompatible with each other on source level (even if they work together at runtime). We've decided not to do INI options to change language behavior, and I think we should also not make declare() options to change language behavior, because they are - especially when applying to more than local file - essentially low-grade INI options. They create non-local context that changes behavior of the language, and they make it impossible, reading the local code, to know what it actually means and what would happen if you run it. I think it's much better for the language not to have some features than to fragment the language and make it incompatible with itself.
> Based on the received feedback, I plan to change this proposal from being > "namespace-scoped" to being "directory-scoped" instead. This should both
I don't think it improves much, in my opinion, and it also introduces hard dependency in the language on specific placing of files and directories - i.e. if you move file on the filesystem, it can actually work differently, which was never the case before. Having concepts like filenames change the core functionality of the language looks to me like an example of leaky abstraction and a hack which may solve a particular problem now but at the cost of making the whole design more messy and introducing more problems in the future. I said before that moving code between libraries in this setup could break it - but with this change, even copying a file to a different directory may break it! Moreover, change in one part of project, changing some behavioral option, would propagate and influence code about which the changer may not even know (e.g. if parts of the project are developed and tested separately and only deployed in final form on a production filesystem). This looks like potential to create an environment that is very hard to navigate and keep straight which of the options is in play for each peace of code. -- Stas Malyshev smalyshev@gmail.com
  106310
July 28, 2019 17:56 Danack@basereality.com (Dan Ackroyd)
On Sun, 28 Jul 2019 at 04:17, Stanislav Malyshev <smalyshev@gmail.com> wrote:
> > We've decided not to do INI options to change language behavior,... > because they are - especially when applying to more than local > file - essentially low-grade INI options
I don't agree. What's being proposed are not low-grade INI options. The problems with ini settings is that: * Different libraries could require different settings. * There was no way to manage the settings individually per library. Those problems do not apply to the proposed RFC. As we've seen with the declare for strict types, libraries that want to have strict mode, and libraries that want to have more type juggling, are perfectly fine to live alongside each other, and can be used just fine.
> I think it's a wrong way to make semantics of the language to be > different in different libraries.
I can't agree. I look at the success of how strict types was brought in, in a way that allowed for zero breaking changes for existing code, and think it was a massively successful way of improving the language without causing problems. I can't understand why someone would make the argument that evolving the language like this is a bad thing.
> This looks like potential to create an environment that is > very hard to navigate and keep straight which of the options > is in play for each peace of code.
You're saying very negative things about a feature you would not have to use. There have been multiple problems/features over the years that would have been more solvable is we had this RFC implemented. It's fine that you're not convinced that this is a great idea, but please can you stop making the argument that because you don't want to use a feature, no-one should be allowed to use it? cheers Dan Ack
  106313
July 28, 2019 19:21 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

> As we've seen with the declare for strict types, libraries that want > to have strict mode, and libraries that want to have more type > juggling, are perfectly fine to live alongside each other, and can be > used just fine.
You are assuming all the code is neatly organized in "libraries" that are closed packages that never intersect, never exchange code and can be completely isolated from one another. This may be the case in some applications, but it's in no way universal and there's a lot of PHP code that does not look like that.
> I can't understand why someone would make the argument that evolving > the language like this is a bad thing.
It's not evolving the language, it's splitting the language into a myriad of mutually incompatible sub-languages, so each piece of code has its own unique bitmask of settings it works with. Moreover, even that bitmask is not local - when you're reading the code, you'd have to check all encompassing filesystem hierarchy (with current idea) to see where the options bitmask if defined for every particular piece of code.
> You're saying very negative things about a feature you would not have to use.
I think it's time to retire the argument of "you don't have to use it". It's not some function that you don't have to call (and BTW I am seeing a massive push to eliminate those for some reason, and never hear that argument there!). It's a language level feature. PHP is an ecosystem, and if you introduce the feature, everybody is influenced by it, because now you have to be aware of the whole bitmask of options and consider every time which bitmask of options a particular code piece is requiring. If it was an ecosystem where code is compiled into binary packages, like C or Java, it won't be such a big deal - you just use the compiled package and never look and the code that produced it and couldn't be happier. But in an open-source ecosystem what encourages exchanging actual source code, this kind of language fragmentation is bad.
> It's fine that you're not convinced that this is a great idea, but > please can you stop making the argument that because you don't want to > use a feature, no-one should be allowed to use it?
Did you actually try to read my argument, of just decided to strawman it from the start? I never said anything like that. I said this feature is bad because it creates fragmentation in the language, not because "I don't want to use it". Please, at least try to address *my* arguments. I am spelling them out pretty verbosely, no need to put words in my mouth. -- Stas Malyshev smalyshev@gmail.com
  106358
July 30, 2019 20:11 Danack@basereality.com (Dan Ackroyd)
On Sun, 28 Jul 2019 at 20:21, Stanislav Malyshev <smalyshev@gmail.com> wrote:
> > Did you actually try to read my argument, of just > decided to strawman it from the start?
I strongly doubt that there is anything that people could say that would alleviate your concern. I could say how nicely my IDE gives me a warning when I edit files from a library that don't belong to the project. I could point to how the Javascript community are deliberately going out of their way to make it easier to 'fracture' the language, by using pre-compilers, and they have used that to drive evolution in the JS ecosystem.* But from previous RFC discussions, you only want to discuss possible concerns; not in an attempt to try to make the RFC better, or to come up with a better idea, but to just put pressure onto the RFC author to withdraw the RFC. The suggestion that RFC authors need to win you over in a discussion, otherwise they aren't doing their job properly appears to be another level of trying to shout down RFCs by making life less pleasant for those RFC authors. While it's admirable that you are strong in your beliefs, it is incredibly tiring engaging in RFC discussions with you, when so often don't it seems to be a waste of time, due to there being nothing that could be said that would change your mind. You can't expect people to respond to all of your points, when it doesn't seem to be a useful use of their time. cheers Dan Ack * And that the PHP community will have an easier time than JS devs do, as PHP devs are completely in control of the computer where their code will run, but JS devs have to run their code in a browser which they have no control over.
  106359
July 30, 2019 20:33 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

> I strongly doubt that there is anything that people could say that > would alleviate your concern.
There's a lot of things people could say - for example, a proposal that does not have the same flaws. If you are fixed on having this proposal unmodified, then yes, my concerns are not addressed, but I am surprised that this is somehow presented as my fault, as if I am being unreasonable voicing my concern about a proposal because these concerns can not be dismissed with mere rhetoric. I would expect that if that is the case, the proposal would have to be improved - but seems like somehow the conclusion being made is that I am unreasonable and the proposal is just fine.
> I could say how nicely my IDE gives me a warning when I edit files > from a library that don't belong to the project.
Not sure how this relates to anything, sorry.
> I could point to how the Javascript community are deliberately going > out of their way to make it easier to 'fracture' the language, by > using pre-compilers, and they have used that to drive evolution in the > JS ecosystem.*
As I pointed out in my other emails, I don't remember any Javascript change that makes the engine reject code written for any of the past 20+ years of Javascript existence. So I am not sure how you see it as a point in your favor. You're not arguing for making pre-compiler for PHP (if you want to make one, I am the last person to argue against - full steam ahead!), you are arguing for making deep changes to the fabric of PHP itself. This is way different.
> But from previous RFC discussions, you only want to discuss possible > concerns; not in an attempt to try to make the RFC better, or to come > up with a better idea, but to just put pressure onto the RFC author to > withdraw the RFC.
That *is* trying to make RFC better - by evaluating concerns and trying to resolve them - or concluding they can not be resolved. I am not pressuring anybody into anything, that is plain false. I am stating where the proposal is lacking. Of course there are many areas where it is not - it would be stupid to write a long email outlining where I agree with RFC author, what use would that be? You seem to be under the impression that if I see a fault in the proposal, it is on me to produce a perfect proposal to does exactly the same (even if I am not yet convinced it should be done at all), and RFC author owes no effort to anyone to try and address the concerns - either I or anybody who raises the concerns do it for him and fix his RFC, or we are unreasonable negative jerks that just want to shut down everything, and should keep quiet about any problems we see until we find a solution that does the same but perfectly. I don't think this is how RFC process should work. I think raising concerns and expecting the proposal author to address them is absolutely normal - moreover, vital - part of the process. If the author needs any help in that, they should ask for it - if it's possible (in some cases the result could be "this proposal doesn't work" and this is normal too, I proposed tons of things that didn't work out).
> The suggestion that RFC authors need to win you over in a discussion, > otherwise they aren't doing their job properly appears to be another > level of trying to shout down RFCs by making life less pleasant for > those RFC authors.
Not me personally, of course, but most of the people with serious technical concerns, yes. That I think is what RFC is about. Did you expect the process to be "Somebody writes RFC, everybody shower them in praise and adoration, no concerns are raised, the code is committed and everybody goes to party happily"? That can happen, but with complex things like completely overhauling 20+-year-old language on a syntax level, it is unrealistic to expect that. For some RFCs, the decision is clear and unanimous, I personally votes "yes" on countless number of them, but sometimes it is more complex than that. If that makes author's life intolerably unpleasant, I am sorry to hear that but this is not the reason to give up on proper process because somebody feels they don't have it in them to support their point of view.
> While it's admirable that you are strong in your beliefs, it is > incredibly tiring engaging in RFC discussions with you, when so often > don't it seems to be a waste of time, due to there being nothing that > could be said that would change your mind.
This is obviously false, I've changed my mind many times. You just try to paint me as unreasonable jerk to save time on generating actual argument in defense of your position on technical merits - while I do spend this time and lay out technical arguments. Prudent strategy for you personally, but disastrous to the quality of the community discussion. Please let's move discussion for my personal faults as a human being to technical matters on PHP. -- Stas Malyshev smalyshev@gmail.com
  106315
July 28, 2019 20:39 rowan.collins@gmail.com (Rowan Collins)
On 28 July 2019 18:56:24 BST, Dan Ackroyd <Danack@basereality.com> wrote:
>I look at the success of how strict types was brought in, in a way >that allowed for zero breaking changes for existing code, and think it >was a massively successful way of improving the language without >causing problems.
I think it's a) very early and b) very subjective to call that decision "massively successful". I have had conversations online and in person asking both why there are two modes, and why the mode can't be selected globally. It was a classic compromise that neither side of the debate hated, but neither really loved either, and the subtleties of how each mode works are likely to be sources of confusion for years to come. I am far from convinced that it is a model we should enthusiastically seek to repeat. Regards, -- Rowan Collins [IMSoP]
  106316
July 28, 2019 21:07 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

>> I look at the success of how strict types was brought in, in a way
> It was a classic compromise that neither side of the debate hated, > but neither really loved either, and the subtleties of how each mode > works are likely to be sources of confusion for years to come. I am > far from convinced that it is a model we should enthusiastically seek > to repeat.
It was OK as one local compromise, but I don't think it's OK as door opening to create unlimited number of options that modify any language behavior in any file and fragment the language. One switch is two options, not ideal but manageable. 10 switches is 1024 options. Not nearly manageable. I am not sure what "success" is there - is there any evidence of strict mode being widely embraced by companies with large legacy code base? By large frameworks used by thousands or millions, and successfully deployed and maintained in mixed settings environments? I think I agree - it's a bit early to proclaim "success". There's no visible disaster (that I think would result from pushing through all-strict mode without BC provisions) but I am not sure what base for proclaiming triumph there is. -- Stas Malyshev smalyshev@gmail.com
  106321
July 29, 2019 10:03 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi,

niedz., 28 lip 2019 o 05:17 Stanislav Malyshev <smalyshev@gmail.com>
napisał(a):

> > Based on the received feedback, I plan to change this proposal from being > > "namespace-scoped" to being "directory-scoped" instead. This should both > > I don't think it improves much, in my opinion, and it also introduces > hard dependency in the language on specific placing of files and > directories - i.e. if you move file on the filesystem, it can actually > work differently, which was never the case before. Having concepts like > filenames change the core functionality of the language looks to me like > an example of leaky abstraction and a hack which may solve a particular > problem now but at the cost of making the whole design more messy and > introducing more problems in the future. >
IMHO it would be impossible to find out what are the boundaries of namespace scope or package scope (whatever you call that) symbols without a root namespace file. I can imagine some can use explicit require to load library class to skip scoped declares, autoloads or whatever lands there. -- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  106308
July 28, 2019 10:14 rowan.collins@gmail.com (Rowan Collins)
On 25 July 2019 14:34:07 BST, Nikita Popov ppv@gmail.com> wrote:
>Based on the received feedback, I plan to change this proposal from >being >"namespace-scoped" to being "directory-scoped" instead. This should >both >resolve some of the concerns and make the proposal more flexible: Not >all >code sharing the same namespace is part of the same package (multiple >packages may have classes in the same namespace)
I think the word "package" here is significant, because all the other variants of this proposal come down to "how can we best emulate package/module support": - the original namespace form assumes that some level of the namespace hierarchy represents a package - the proposal to use a directory assumes that some structure on disk represents a package - Mark's alternative using an explicit include allows every file to say it's part of some package I think it would be better to skip these workarounds, and define a package directly. The differences I see between package and namespace are: 1) Packages should be non-hierarchical. Perhaps most simply a package name could have exactly two parts, like in composer, so it's clear that there is no implied relationship between two packages. 2) A package should be explicitly declared before first use, probably in a separate file. This definition would have scoped declares, and could also have autoload and preload definitions. Or perhaps a package would always be eagerly loaded, allowing us to do more cross-file analysis at compile-time. Obviously, there's a lot to figure out here. Do we define where the package definition lives on disk, or require the user to configure it in some way? Does a package name form the prefix for namespaces within that package, or can a package contain definitions for any namespace? However, I think we should make these design decisions, rather than letting an easy implementation lead us somewhere we'll regret later. I do also share the concern that a proliferation of package options would make the language harder to understand. Part of the motivation for removing short open tags seems to be to make the language more consistent. The ability for a package to configure the compiler as its author intended is certainly better than having to test every combination, but we need to think very carefully about what options we add. Regards, -- Rowan Collins [IMSoP]
  106335
July 30, 2019 08:11 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Rowan,

niedz., 28 lip 2019 o 12:15 Rowan Collins collins@gmail.com>
napisał(a):

> 1) Packages should be non-hierarchical. Perhaps most simply a package name > could have exactly two parts, like in composer, so it's clear that there is > no implied relationship between two packages. >
IMO this would create a lot of problems cause name in Composer Package doesn't reflect used namespace declared in autoload, for eg. Composer package name => used namespace --- ocramius/package-version => PackageVersions\ doctrine/collections => Doctrine\Common\Collections\ symfony/console => Symfony\Component\Console\ -- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  106336
July 30, 2019 08:20 kjarli@gmail.com (Lynn)
On Tue, Jul 30, 2019 at 10:12 AM Michał Brzuchalski <michal@brzuchalski.com>
wrote:

> IMO this would create a lot of problems cause name in Composer Package > doesn't reflect used namespace declared in autoload, for eg. > > Composer package name => used namespace > --- > ocramius/package-version => PackageVersions\ > doctrine/collections => Doctrine\Common\Collections\ > symfony/console => Symfony\Component\Console\ > > -- > regards / pozdrawiam, > -- > Michał Brzuchalski >
Hi, This might also cause issues in scenarios where packages would register autoloading of a conflicting namespace. If for some reason the namespace clashes with a vendor, or vica versa, or a vendor package "hides" an additional namespace in the package and overrides the declares that way, it could cause a lot of problems. Would it be an idea to first establish the concept of a package within PHP, rather than deducting it based off a namespace? If packages would be a thing in PHP, other than just a namespace, package private could also be implemented as visibility modifier for inheritance and class visibility. Regards, Lynn van der Berg
  106338
July 30, 2019 08:48 rowan.collins@gmail.com (Rowan Collins)
On Tue, 30 Jul 2019 at 09:12, Michał Brzuchalski <michal@brzuchalski.com>
wrote:

> 1) Packages should be non-hierarchical. Perhaps most simply a package name >> could have exactly two parts, like in composer, so it's clear that there is >> no implied relationship between two packages. >> > > IMO this would create a lot of problems cause name in Composer Package > doesn't reflect used namespace declared in autoload >
I'm not saying the package name has to match the composer name, just that the format might be similar; and I explicitly left the question open of how package names would relate to namespaces. It might be sensible to align the package/module name with one or other of the existing two naming schemes, but it's certainly not essential; names in Composer are essentially arbitrary anyway, not visible at all at run-time. On Tue, 30 Jul 2019 at 09:20, Lynn <kjarli@gmail.com> wrote:
> Would it be an idea to first establish the concept of a package within > PHP, rather than deducting it based off a namespace? If packages would be a > thing in PHP, other than just a namespace, package private could also be > implemented as visibility modifier for inheritance and class visibility. >
I think there's some confusion here, because establishing the concept of a package as separate from a namespace is exactly what I proposed. Here's a previous message (technically in the same thread, but from 18 months ago) where I also mentioned class visibility: https://externals.io/message/101323#101390 Regards, -- Rowan Collins [IMSoP]
  106342
July 30, 2019 10:13 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Rowan,

wt., 30 lip 2019 o 10:48 Rowan Collins collins@gmail.com> napisał(a):

> I think there's some confusion here, because establishing the concept of a > package as separate from a namespace is exactly what I proposed. > > Here's a previous message (technically in the same thread, but from 18 > months ago) where I also mentioned class visibility: > https://externals.io/message/101323#101390 >
Was thinking about similar, a package with own identity and a way to declare autoload and other stuff. Was even thinking it could use a double colon which I've proposed way back in the same thread and with a delimiter in name all related symbols could be stored in package individual symbol tables, it won't collide with namespaced and global ones and would be easier to detect if tried to use an internal symbol in another context like other package or in global code. It could introduce a few more keywords like: * "package" - for declaring package name and declares, * "export" - for explicit declare of publicly available symbols which then could be detectable etc. for visibility features, * "expect" - for explicit declare of required dependencies Last two are for future features and the first one could be enough for shaping how it could look like. For eg. some of my thoughts https://gist.github.com/brzuchal/c45010f0dd20642b470eeee8b9c56c5f I know it's out of the main topic but IMO we should start another one and I'm pretty sure I've mentioned that earlier. If we wanna shape package for PHP then the separate discussion could be a good idea. -- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  106345
July 30, 2019 14:08 nikita.ppv@gmail.com (Nikita Popov)
Thanks everyone for your responses!

I think the discussion resolves around two primary concerns, so let me
address them in turn.

The first is the general approach of using declares as a language-evolution
mechanism. The concern here is that each additional declare fragments the
language and increases the number of combinations of different options
there are.

What I ultimately want to achieve here is a way to evolve the language and
fix long-standing issues without breaking backwards compatibility or
causing ecosystem fragmentation. The only way we currently have to address
(nowadays) undesirable behavior is through deprecation and subsequent
removal. As people like to regularly remind me, this has a high cost on the
ecosystem, because millions of codebases that were running without a glitch
need to be updated, which not only takes a lot of effort, but also delays
adoption of new PHP versions for everyone. As such, the "deprecation and
removal" approach has to work over long time-frames and is only really
applicable to rather "minor" issues in the first place.

If we want to evolve the language without breaking backwards compatibility,
we need to provide a way for gradual migration of the ecosystem: A library
should be able to opt-in to breaking changes, while remaining usable by
downstream consumers. Conversely, an application should be able to opt-in
to breaking changes, while still being able to use an older library.

To achieve this, I believe it is unavoidable to have *some* kind of
mechanism to affect language-behavior on a per-library/project level. Of
course, the devil tends to be in the details...

What this RFC originally proposed is a fine-grained approach, where
individual changes are controlled by separate declare directives. However,
this is not the only possibility: As has been recently popularized with
Rust editions (https://doc.rust-lang.org/edition-guide/editions/index.html),
a coarse-grained approach where multiple changes have to be enabled
together as part of an "edition" is also possible.

The advantage of the coarse-grained "edition" approach is that it avoids a
combinatorial explosion of options: It's all or nothing, and it is easier
to keep in mind that a project uses "PHP 2020" rather than some specific
combination of declares.

The advantage of a fine-grained approach is that is also allows a
fine-grained migration. As a statically-typed language, Rust can provide
fairly reliable tooling to perform edition migrations. While such tooling
also exists for PHP (e.g. Rector), it does not have the same level of
reliability, especially for codebases that do not make pervasive use of
type annotations. Fine-grained declares allow a code-base to be updated one
step at a time.

It is possible to combine both approaches by providing both fine-grained
control and an overall "edition" that enables a larger set of language
declares. The end goal should be to move to the next edition, but
individual declares may be used during the migration, or to opt-out a
section of code. This is probably my preferred approach.

I should probably also highlight that this is somewhat different from the
existing strict_types directive: strict_types was only in part a mechanism
to control BC breakage (with regard to internal functions), but to a large
part exists because we couldn't agree on which semantics are preferable.

This is not what I'm going for here. I don't want declares to becomes a way
to resolve disagreements by just providing both options. Instead a declare
represents a change that we *want* to make and that codebases *should* make
eventually, but that is opt-in to maintain backwards compatibility and
library interoperability.

---

The second concern is around the technical details of opting-in to
BC-breaking language changes on the library level. Here is an overview of
some proposals that have been made:

1. Keep declares per-file. This is clearly incompatible with any
fine-grained (or optionally fine-grained) approach, because declares have
to be replicated across hundreds of files. I think this is a bad choice
also for a coarse-grained approach (or even for the existing strict_types
directive), because in all cases I've seen people want to enable the option
for the whole library, not individual files.

Replicating declares per-file is error prone (I regularly forget to add
strict_types declarations to newly created files) and complicates the
mental model of the programmer. While ostensibly per-file declares make
things explicit, I think the reality is that nobody actually double-checks
declares in each file they open and will instead assume that the project
default holds.

2. Support per-namespace declares. This is what I originally proposed. This
is based on the premise that a library will usually correspond to a
namespace. This approach has been extensively discussed in this thread -- I
think the main issue is that the premise just doesn't reliably hold up in
practice, e.g. because multiple packages publish under the same namespace.

3. Support per-directory declares, which is the direction I was planning to
explore next. This is based on the premise that all library files are part
of some top-level directory, which I think is a fairly safe premise (note
that the "directory" could also be a phar file).

The actual intended use (similar to the namespace-based variant) is that
people will specify their declares in the composer.json file, and composer
then includes a call to declare_directory() or similar as part of the
autoloader. Projects not using composer have the choice of issuing an
explicit call.

4. Specify declares in a special file, similar to how INI directives are
declared. The suggestion here has been that PHP could scan the path of an
included file upwards to find a declares.json (or similar).

The main advantage I see here (over a declare_directory() function) is that
there are no loading order issues. declare_directory() needs to be called
before any files from that directory have been included (which is part of
why an integration into the composer autoloader is useful), while for a
separate and implicitly processed file this falls out naturally.

Apart from that, I'm not a big fan of this proposal, mostly because of the
implicit loading it entails. I also don't think that having one more
configuration file for this buys us something over declaring things in
composer.json.

5. Introduce a first-class module/package concept and support per-package
declares. This is arguably the closest fit for what is needed, but also the
most complex solution. This is a fairly big problem space and I personally
do not want to pursue this outside a certain narrow scope.

In particular I have serious doubts about retrofitting (at this point in
time) an invasive module system that involves explicit export and import
management, along the lines of what Michal is describing. (Though I will be
happily surprised if someone comes forward with a proposal to do this in a
non-invasive way.)

What I think might be worth pursuing though, is a much weaker package
notion that essentially grants some language-integration to the existing
notion of composer packages. Instead of having a declare_directory() we'd
have declare_package(), which is bound to a certain path and can be used to
specify declares, but also used for other purposes, such as package-private
visibility.

If I may make another Rust analogy, this would be more like Rust crates
than Rust modules. The analogy being that this is a more coarse grained
level, and is fairly tightly integrated with the package manager (but of
course still usable without it).

Regards,
Nikita


On Tue, Jul 30, 2019 at 12:14 PM Michał Brzuchalski <michal@brzuchalski.com>
wrote:

> Hi Rowan, > > wt., 30 lip 2019 o 10:48 Rowan Collins collins@gmail.com> > napisał(a): > > > I think there's some confusion here, because establishing the concept of > a > > package as separate from a namespace is exactly what I proposed. > > > > Here's a previous message (technically in the same thread, but from 18 > > months ago) where I also mentioned class visibility: > > https://externals.io/message/101323#101390 > > > > Was thinking about similar, a package with own identity and a way to > declare autoload and other stuff. > Was even thinking it could use a double colon which I've proposed way back > in the same thread and > with a delimiter in name all related symbols could be stored in package > individual symbol tables, > it won't collide with namespaced and global ones and would be easier to > detect if tried to use an internal symbol > in another context like other package or in global code. > It could introduce a few more keywords like: > * "package" - for declaring package name and declares, > * "export" - for explicit declare of publicly available symbols which then > could be detectable etc. for visibility features, > * "expect" - for explicit declare of required dependencies > > Last two are for future features and the first one could be enough for > shaping how it could look like. > For eg. some of my thoughts > https://gist.github.com/brzuchal/c45010f0dd20642b470eeee8b9c56c5f > > I know it's out of the main topic but IMO we should start another one and > I'm pretty sure I've mentioned that earlier. > If we wanna shape package for PHP then the separate discussion could be a > good idea. > > -- > regards / pozdrawiam, > -- > Michał Brzuchalski > about.me/brzuchal > brzuchalski.com >
  106346
July 30, 2019 14:55 rowan.collins@gmail.com (Rowan Collins)
On Tue, 30 Jul 2019 at 15:08, Nikita Popov ppv@gmail.com> wrote:

> 5. Introduce a first-class module/package concept and support per-package > declares. This is arguably the closest fit for what is needed, but also the > most complex solution. This is a fairly big problem space and I personally > do not want to pursue this outside a certain narrow scope. > > In particular I have serious doubts about retrofitting (at this point in > time) an invasive module system that involves explicit export and import > management, along the lines of what Michal is describing. (Though I will be > happily surprised if someone comes forward with a proposal to do this in a > non-invasive way.) > > What I think might be worth pursuing though, is a much weaker package > notion that essentially grants some language-integration to the existing > notion of composer packages. Instead of having a declare_directory() we'd > have declare_package(), which is bound to a certain path and can be used to > specify declares, but also used for other purposes, such as package-private > visibility. > > If I may make another Rust analogy, this would be more like Rust crates > than Rust modules. The analogy being that this is a more coarse grained > level, and is fairly tightly integrated with the package manager (but of > course still usable without it). >
Yes, I was thinking of something more like Composer packages, rather than like JS modules: code would mostly work as presently, but with some notion of being "owned" by a particular package. This doesn't mean all the functions of Composer would be integrated, but could provide richer primitives, such as registering an autoload callback or preload list to a particular package, rather than having to perform string matches. Regards, -- Rowan Collins [IMSoP]
  106349
July 30, 2019 16:27 markyr@gmail.com (Mark Randall)
On 30/07/2019 15:55, Rowan Collins wrote:
> Yes, I was thinking of something more like Composer packages, rather than > like JS modules: code would mostly work as presently, but with some notion > of being "owned" by a particular package. This doesn't mean all the > functions of Composer would be integrated, but could provide richer > primitives, such as registering an autoload callback or preload list to a > particular package, rather than having to perform string matches.
A slight tangent: At the point we're talking about composer integrating callbacks, preloading lists etc, isn't it about time that an spl_autoload_list was added that accepted the standard composer classmap [class_id => path] and forewent most autoload callbacks entirely? Mark Randall
  106355
July 30, 2019 19:33 smalyshev@gmail.com (Stanislav Malyshev)
Hi!

> If we want to evolve the language without breaking backwards compatibility, > we need to provide a way for gradual migration of the ecosystem: A library > should be able to opt-in to breaking changes, while remaining usable by > downstream consumers. Conversely, an application should be able to opt-in > to breaking changes, while still being able to use an older library.
This assumes breaking changes are necessary for evolution. I am not sure this is actually true. Java is 21 years old, and I think the code I first wrote when I learned Java in late 90s would still compile today (even though nobody would probably want to run it :). Javascript is 23 years old, and the only thing I can think of that may break BC is "use strict", which is also local and just one option. C++ had a myriad of changes lately, and is 34 years old, but I am pretty sure code written a decade ago - discounting library changes, etc. - would still compile today. I think the assumption that we *must* break old code to be able to evolve is not correct. Surely, certain directions of evolution - like converting PHP to strictly-typed language - would require that, but that's not the only direction possible.
> 3. Support per-directory declares, which is the direction I was planning to > explore next. This is based on the premise that all library files are part > of some top-level directory, which I think is a fairly safe premise (note > that the "directory" could also be a phar file). > > The actual intended use (similar to the namespace-based variant) is that > people will specify their declares in the composer.json file, and composer > then includes a call to declare_directory() or similar as part of the > autoloader. Projects not using composer have the choice of issuing an > explicit call.
Note this also means that syntax and semantics for each file is runtime-defined and can change any time. With obvious implications for preloading and opcode caching. Moreover, there could be a situation where some code includes a file with one set of options, and then declare_directory() happens that changes the options and another code includes the same code assuming different set of options. I have no idea what is supposed to happen in this situation. Since declare_directory() is non-local, I see no way to ensure that require for certain file always happens with the same set of options. Of course, one can say "composer and autoloading will ensure that" - but composer and its autoloading, while undoubtedly popular, are libraries external to PHP, and there's a lot of PHP code that is either not using composer, or using composer in combination with other ways of autoloading. Unless we mandate use of composer for every PHP project, this still does not solve the issue. And, of course, IDEs/analyzers would somehow have to figure this out too - since composer.json is not actually defining the options (it is only an input for declare_directory call sometime later) they'd probably have to parse code and find declare_directory() calls? Or they'd just assume everything in composer.json? What if I just manually call declare_directory() then - would it break everything?
> 4. Specify declares in a special file, similar to how INI directives are > declared. The suggestion here has been that PHP could scan the path of an > included file upwards to find a declares.json (or similar).
This one at least ensures options for certain file are consistent, but at the cost of introducing magic config files that PHP never had before, and all complications coming with handling, caching and managing those files (if declares.json changed, do you have to invalidate all opcode caches for all files under this directory?).
> 5. Introduce a first-class module/package concept and support per-package > declares. This is arguably the closest fit for what is needed, but also the > most complex solution. This is a fairly big problem space and I personally > do not want to pursue this outside a certain narrow scope.
That actually would be a workable solution if package semantics were built into the language. That would ensure same code is always associated with same options, allow compile-time handling of options and would let IDEs, analyzers, etc. to have a consistent picture without trying to parse runtime code. If I had to choose among the options here, so far it's my favorite one, even though I recognize it may not be easy. -- Stas Malyshev smalyshev@gmail.com
  106365
July 31, 2019 09:17 nikita.ppv@gmail.com (Nikita Popov)
On Tue, Jul 30, 2019 at 4:08 PM Nikita Popov ppv@gmail.com> wrote:

> Thanks everyone for your responses! > > I think the discussion resolves around two primary concerns, so let me > address them in turn. > > The first is the general approach of using declares as a > language-evolution mechanism. The concern here is that each additional > declare fragments the language and increases the number of combinations of > different options there are. > > What I ultimately want to achieve here is a way to evolve the language and > fix long-standing issues without breaking backwards compatibility or > causing ecosystem fragmentation. The only way we currently have to address > (nowadays) undesirable behavior is through deprecation and subsequent > removal. As people like to regularly remind me, this has a high cost on the > ecosystem, because millions of codebases that were running without a glitch > need to be updated, which not only takes a lot of effort, but also delays > adoption of new PHP versions for everyone. As such, the "deprecation and > removal" approach has to work over long time-frames and is only really > applicable to rather "minor" issues in the first place. > > If we want to evolve the language without breaking backwards > compatibility, we need to provide a way for gradual migration of the > ecosystem: A library should be able to opt-in to breaking changes, while > remaining usable by downstream consumers. Conversely, an application should > be able to opt-in to breaking changes, while still being able to use an > older library. > > To achieve this, I believe it is unavoidable to have *some* kind of > mechanism to affect language-behavior on a per-library/project level. Of > course, the devil tends to be in the details... > > What this RFC originally proposed is a fine-grained approach, where > individual changes are controlled by separate declare directives. However, > this is not the only possibility: As has been recently popularized with > Rust editions (https://doc.rust-lang.org/edition-guide/editions/index.html), > a coarse-grained approach where multiple changes have to be enabled > together as part of an "edition" is also possible. > > The advantage of the coarse-grained "edition" approach is that it avoids a > combinatorial explosion of options: It's all or nothing, and it is easier > to keep in mind that a project uses "PHP 2020" rather than some specific > combination of declares. > > The advantage of a fine-grained approach is that is also allows a > fine-grained migration. As a statically-typed language, Rust can provide > fairly reliable tooling to perform edition migrations. While such tooling > also exists for PHP (e.g. Rector), it does not have the same level of > reliability, especially for codebases that do not make pervasive use of > type annotations. Fine-grained declares allow a code-base to be updated one > step at a time. > > It is possible to combine both approaches by providing both fine-grained > control and an overall "edition" that enables a larger set of language > declares. The end goal should be to move to the next edition, but > individual declares may be used during the migration, or to opt-out a > section of code. This is probably my preferred approach. > > I should probably also highlight that this is somewhat different from the > existing strict_types directive: strict_types was only in part a mechanism > to control BC breakage (with regard to internal functions), but to a large > part exists because we couldn't agree on which semantics are preferable. > > This is not what I'm going for here. I don't want declares to becomes a > way to resolve disagreements by just providing both options. Instead a > declare represents a change that we *want* to make and that codebases > *should* make eventually, but that is opt-in to maintain backwards > compatibility and library interoperability. > > --- > > The second concern is around the technical details of opting-in to > BC-breaking language changes on the library level. Here is an overview of > some proposals that have been made: > > 1. Keep declares per-file. This is clearly incompatible with any > fine-grained (or optionally fine-grained) approach, because declares have > to be replicated across hundreds of files. I think this is a bad choice > also for a coarse-grained approach (or even for the existing strict_types > directive), because in all cases I've seen people want to enable the option > for the whole library, not individual files. > > Replicating declares per-file is error prone (I regularly forget to add > strict_types declarations to newly created files) and complicates the > mental model of the programmer. While ostensibly per-file declares make > things explicit, I think the reality is that nobody actually double-checks > declares in each file they open and will instead assume that the project > default holds. > > 2. Support per-namespace declares. This is what I originally proposed. > This is based on the premise that a library will usually correspond to a > namespace. This approach has been extensively discussed in this thread -- I > think the main issue is that the premise just doesn't reliably hold up in > practice, e.g. because multiple packages publish under the same namespace.. > > 3. Support per-directory declares, which is the direction I was planning > to explore next. This is based on the premise that all library files are > part of some top-level directory, which I think is a fairly safe premise > (note that the "directory" could also be a phar file). > > The actual intended use (similar to the namespace-based variant) is that > people will specify their declares in the composer.json file, and composer > then includes a call to declare_directory() or similar as part of the > autoloader. Projects not using composer have the choice of issuing an > explicit call. >
After looking into the implementation side a bit, I think I remember why I didn't go down this route originally: Path canonicalization is tricky. Namespaces have the big advantage that they are fully controlled by PHP, with well-defined semantics. Plain filesystem paths have a well-defined realpath canonicalization, but things are less simple for general streams. The engine already needs to solve this problem for the purpose of include_once and require_once and provides the zend_resolve_path hook for this. However, it doesn't really support stream wrappers (phar has some support here, but the last time I looked into it I didn't get the impression that it's reliable). There's the additional question of caching: In an opcache'd scenario we wouldn't want to have any stat traffic. Opcache does perform caching of resolved paths, but I think this currently only extends to actually cached files, while we'd also need directories in this case. These problems can probably be overcome, possibly by making path resolution a first-class stream wrapper function, but it does make things a good bit more complicated relative to the namespace-based approach, and possibly also more fragile. I'm somewhat stumped at this point. The most reliable approach (and in a certain way also technically simplest) would be to have a package declaration in every file, as suggested by Michal ... but of course this does require adding the package declaration everywhere and complicates migration. Nikita 4. Specify declares in a special file, similar to how INI directives are
> declared. The suggestion here has been that PHP could scan the path of an > included file upwards to find a declares.json (or similar). > > The main advantage I see here (over a declare_directory() function) is > that there are no loading order issues. declare_directory() needs to be > called before any files from that directory have been included (which is > part of why an integration into the composer autoloader is useful), while > for a separate and implicitly processed file this falls out naturally. > > Apart from that, I'm not a big fan of this proposal, mostly because of the > implicit loading it entails. I also don't think that having one more > configuration file for this buys us something over declaring things in > composer.json. > > 5. Introduce a first-class module/package concept and support per-package > declares. This is arguably the closest fit for what is needed, but also the > most complex solution. This is a fairly big problem space and I personally > do not want to pursue this outside a certain narrow scope. > > In particular I have serious doubts about retrofitting (at this point in > time) an invasive module system that involves explicit export and import > management, along the lines of what Michal is describing. (Though I will be > happily surprised if someone comes forward with a proposal to do this in a > non-invasive way.) > > What I think might be worth pursuing though, is a much weaker package > notion that essentially grants some language-integration to the existing > notion of composer packages. Instead of having a declare_directory() we'd > have declare_package(), which is bound to a certain path and can be used to > specify declares, but also used for other purposes, such as package-private > visibility. > > If I may make another Rust analogy, this would be more like Rust crates > than Rust modules. The analogy being that this is a more coarse grained > level, and is fairly tightly integrated with the package manager (but of > course still usable without it). > > Regards, > Nikita > > > On Tue, Jul 30, 2019 at 12:14 PM Michał Brzuchalski < > michal@brzuchalski.com> wrote: > >> Hi Rowan, >> >> wt., 30 lip 2019 o 10:48 Rowan Collins collins@gmail.com> >> napisał(a): >> >> > I think there's some confusion here, because establishing the concept >> of a >> > package as separate from a namespace is exactly what I proposed. >> > >> > Here's a previous message (technically in the same thread, but from 18 >> > months ago) where I also mentioned class visibility: >> > https://externals.io/message/101323#101390 >> > >> >> Was thinking about similar, a package with own identity and a way to >> declare autoload and other stuff. >> Was even thinking it could use a double colon which I've proposed way back >> in the same thread and >> with a delimiter in name all related symbols could be stored in package >> individual symbol tables, >> it won't collide with namespaced and global ones and would be easier to >> detect if tried to use an internal symbol >> in another context like other package or in global code. >> It could introduce a few more keywords like: >> * "package" - for declaring package name and declares, >> * "export" - for explicit declare of publicly available symbols which then >> could be detectable etc. for visibility features, >> * "expect" - for explicit declare of required dependencies >> >> Last two are for future features and the first one could be enough for >> shaping how it could look like. >> For eg. some of my thoughts >> https://gist.github.com/brzuchal/c45010f0dd20642b470eeee8b9c56c5f >> >> I know it's out of the main topic but IMO we should start another one and >> I'm pretty sure I've mentioned that earlier. >> If we wanna shape package for PHP then the separate discussion could be a >> good idea. >> >> -- >> regards / pozdrawiam, >> -- >> Michał Brzuchalski >> about.me/brzuchal >> brzuchalski.com >> >
  106549
August 12, 2019 08:17 nicolas.grekas+php@gmail.com (Nicolas Grekas)
Le lun. 11 déc. 2017 à 14:44, Nikita Popov ppv@gmail.com> a écrit :

> Some time ago I introduced the following proposal for namespace-scoped > declares: > > https://wiki.php.net/rfc/namespace_scoped_declares > > The idea is to allow specifying declare directives for a whole library or > project using: > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > I've finally gotten around to implementing this proposal ( > https://github.com/php/php-src/pull/2972) and would like to move forward > with it. > > The reason why I'm picking it up again is some feedback I received for the > explicit call-time send-by-ref proposal. The main objection seems to be > that the feature has limited usefulness if it's optional rather than > required, because you still can't be sure that something is a by-value > pass, just because no & is present. At the same time, we can't make this > required anytime soon due to the large BC impact. > > Namespace-scoped declares are perfectly suited to resolve this problem. We > can introduce a require_explicit_send_by_ref declare directive to make the > call-site annotation required, and libraries/projects can easily opt-in to > it using namespace_declare(). There would be no BC impact, while at the > same time projects could benefit from the additional clarity and > performance improvements immediately. >
I've read discussions about the notion of a "package" and the way we should define its boundaries. What about the following? Individual files could declare their package using this style:
  106551
August 12, 2019 08:26 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Aug 12, 2019 at 10:17 AM Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:

> Le lun. 11 déc. 2017 à 14:44, Nikita Popov ppv@gmail.com> a > écrit : > >> Some time ago I introduced the following proposal for namespace-scoped >> declares: >> >> https://wiki.php.net/rfc/namespace_scoped_declares >> >> The idea is to allow specifying declare directives for a whole library or >> project using: >> >> namespace_declare('Vendor\Lib', ['strict_types' => 1]); >> >> I've finally gotten around to implementing this proposal ( >> https://github.com/php/php-src/pull/2972) and would like to move forward >> with it. >> >> The reason why I'm picking it up again is some feedback I received for the >> explicit call-time send-by-ref proposal. The main objection seems to be >> that the feature has limited usefulness if it's optional rather than >> required, because you still can't be sure that something is a by-value >> pass, just because no & is present. At the same time, we can't make this >> required anytime soon due to the large BC impact. >> >> Namespace-scoped declares are perfectly suited to resolve this problem. We >> can introduce a require_explicit_send_by_ref declare directive to make the >> call-site annotation required, and libraries/projects can easily opt-in to >> it using namespace_declare(). There would be no BC impact, while at the >> same time projects could benefit from the additional clarity and >> performance improvements immediately. >> > > I've read discussions about the notion of a "package" and the way we > should define its boundaries. > What about the following? > > Individual files could declare their package using this style: > > That would be enough to group a set of files together and make them share > eg some private classes, some optional PHP behaviors, etc. > > The right side "MyVendor\MyPackage" would also be a FQCN that PHP would > autoload as a regular class. The corresponding class would then be the > place where ppl would declare the engine behavior they want for their > package (strict types, etc). To enforce this, the engine could require that > the "MyPackage" class implements some interface/extend some base abstract > class. > > Of course, one could hijack a package and declare an unrelated file as > part of it, but I don't think that's an issue: the situation is the same as > for namespaces, where one can hijack a third party vendor namespace. In > practice, it proved not being an issue, and the original author's intent is > clear: "this is my namespace/package, if you mess with it, fine, but you're > on your own". > > Nicolas >
FTR I've created a draft-implementation for a package system here: https://github.com/php/php-src/pull/4490 It uses a slightly different approach in that it keeps the package name a string (that should usually match the Composer package name) and uses a function to register the package. The main annoyance is that this requires declaring the package in every file, something I would like to avoid. An alternative I played with is to allow specifying the package at include time, which would allow the autoloader to specify which package a file is part. However, while this is more ergonomic for the user, I'm afraid that this will make static analysis & IDE scenarios problematic, because they will not be able to easily know what the package is in cases that fall outside convention. So in the end, an explicit per-file package declaration may be the best we can do. Nikita
  106555
August 12, 2019 09:06 php-lists@koalephant.com (Stephen Reay)
> On 12 Aug 2019, at 15:26, Nikita Popov ppv@gmail.com> wrote: > > On Mon, Aug 12, 2019 at 10:17 AM Nicolas Grekas < > nicolas.grekas+php@gmail.com> wrote: > >> Le lun. 11 déc. 2017 à 14:44, Nikita Popov ppv@gmail.com> a >> écrit : >> >>> Some time ago I introduced the following proposal for namespace-scoped >>> declares: >>> >>> https://wiki.php.net/rfc/namespace_scoped_declares >>> >>> The idea is to allow specifying declare directives for a whole library or >>> project using: >>> >>> namespace_declare('Vendor\Lib', ['strict_types' => 1]); >>> >>> I've finally gotten around to implementing this proposal ( >>> https://github.com/php/php-src/pull/2972) and would like to move forward >>> with it. >>> >>> The reason why I'm picking it up again is some feedback I received for the >>> explicit call-time send-by-ref proposal. The main objection seems to be >>> that the feature has limited usefulness if it's optional rather than >>> required, because you still can't be sure that something is a by-value >>> pass, just because no & is present. At the same time, we can't make this >>> required anytime soon due to the large BC impact. >>> >>> Namespace-scoped declares are perfectly suited to resolve this problem. We >>> can introduce a require_explicit_send_by_ref declare directive to make the >>> call-site annotation required, and libraries/projects can easily opt-in to >>> it using namespace_declare(). There would be no BC impact, while at the >>> same time projects could benefit from the additional clarity and >>> performance improvements immediately. >>> >> >> I've read discussions about the notion of a "package" and the way we >> should define its boundaries. >> What about the following? >> >> Individual files could declare their package using this style: >> > >> That would be enough to group a set of files together and make them share >> eg some private classes, some optional PHP behaviors, etc. >> >> The right side "MyVendor\MyPackage" would also be a FQCN that PHP would >> autoload as a regular class. The corresponding class would then be the >> place where ppl would declare the engine behavior they want for their >> package (strict types, etc). To enforce this, the engine could require that >> the "MyPackage" class implements some interface/extend some base abstract >> class. >> >> Of course, one could hijack a package and declare an unrelated file as >> part of it, but I don't think that's an issue: the situation is the same as >> for namespaces, where one can hijack a third party vendor namespace. In >> practice, it proved not being an issue, and the original author's intent is >> clear: "this is my namespace/package, if you mess with it, fine, but you're >> on your own". >> >> Nicolas >> > > FTR I've created a draft-implementation for a package system here: > https://github.com/php/php-src/pull/4490 > > It uses a slightly different approach in that it keeps the package name a > string (that should usually match the Composer package name) and uses a > function to register the package. > > The main annoyance is that this requires declaring the package in every > file, something I would like to avoid. An alternative I played with is to > allow specifying the package at include time, which would allow the > autoloader to specify which package a file is part. However, while this is > more ergonomic for the user, I'm afraid that this will make static analysis > & IDE scenarios problematic, because they will not be able to easily know > what the package is in cases that fall outside convention. So in the end, > an explicit per-file package declaration may be the best we can do. > > Nikita
Is there some specific benefit to passing an array with `name` and `declares` keys, over a signature like either package_declare(‘nikic/php-parser’, strict_types=1, foo=bar); or even package_declare('nikic/php-parser’, [‘strict_types’ => 1, ‘foo’ => bar]); I realise this is the epitome of bike shedding, it just seems like a non-obvious choice (to me at least) to accept a specifically structured array? Cheers Stephen
  106558
August 12, 2019 13:23 thomas@nunninger.info (Thomas Nunninger)
Hi,

Am 12.08.19 um 10:26 schrieb Nikita Popov:
> On Mon, Aug 12, 2019 at 10:17 AM Nicolas Grekas < > nicolas.grekas+php@gmail.com> wrote: > >> Le lun. 11 déc. 2017 à 14:44, Nikita Popov ppv@gmail.com> a >> écrit : >> >>> Some time ago I introduced the following proposal for namespace-scoped >>> declares: >>> >>> https://wiki.php.net/rfc/namespace_scoped_declares >>> >>> The idea is to allow specifying declare directives for a whole library or >>> project using: >>> >>> namespace_declare('Vendor\Lib', ['strict_types' => 1]); >>> >>> I've finally gotten around to implementing this proposal ( >>> https://github.com/php/php-src/pull/2972) and would like to move forward >>> with it. >>> >>> The reason why I'm picking it up again is some feedback I received for the >>> explicit call-time send-by-ref proposal. The main objection seems to be >>> that the feature has limited usefulness if it's optional rather than >>> required, because you still can't be sure that something is a by-value >>> pass, just because no & is present. At the same time, we can't make this >>> required anytime soon due to the large BC impact. >>> >>> Namespace-scoped declares are perfectly suited to resolve this problem. We >>> can introduce a require_explicit_send_by_ref declare directive to make the >>> call-site annotation required, and libraries/projects can easily opt-in to >>> it using namespace_declare(). There would be no BC impact, while at the >>> same time projects could benefit from the additional clarity and >>> performance improvements immediately. >>> >> >> I've read discussions about the notion of a "package" and the way we >> should define its boundaries. >> What about the following? >> >> Individual files could declare their package using this style: >> > >> That would be enough to group a set of files together and make them share >> eg some private classes, some optional PHP behaviors, etc. >> >> The right side "MyVendor\MyPackage" would also be a FQCN that PHP would >> autoload as a regular class. The corresponding class would then be the >> place where ppl would declare the engine behavior they want for their >> package (strict types, etc). To enforce this, the engine could require that >> the "MyPackage" class implements some interface/extend some base abstract >> class. >> >> Of course, one could hijack a package and declare an unrelated file as >> part of it, but I don't think that's an issue: the situation is the same as >> for namespaces, where one can hijack a third party vendor namespace. In >> practice, it proved not being an issue, and the original author's intent is >> clear: "this is my namespace/package, if you mess with it, fine, but you're >> on your own". >> >> Nicolas >> > > FTR I've created a draft-implementation for a package system here: > https://github.com/php/php-src/pull/4490 > > It uses a slightly different approach in that it keeps the package name a > string (that should usually match the Composer package name) and uses a > function to register the package. > > The main annoyance is that this requires declaring the package in every > file, something I would like to avoid. An alternative I played with is to > allow specifying the package at include time, which would allow the > autoloader to specify which package a file is part. However, while this is > more ergonomic for the user, I'm afraid that this will make static analysis > & IDE scenarios problematic, because they will not be able to easily know > what the package is in cases that fall outside convention. So in the end, > an explicit per-file package declaration may be the best we can do.
I'm not sure if this was discussed/proposed before: Why not have a concept similar to autoloaders that resolves a given class name to some declare statements. That way you could implement a composer based resolver that takes a class name, (internally) resolves it to a package (based on the autoloader configuration in composer.json), and returns an array of the specified declare statements. Developers do not need to specify a package or any declare statements in PHP files at all, as PHP (or static analyzers) would be able to ask the class-name-to-declare-statements resolver which declare statements a PHP file defines. Alternatively, you could introduce the concept of a Package(Specification) class as proposed by Nicolas (but without the need to extend and implement it in each package). That way the resolver does not return the array of declare statements but an instance of the Package(Specification) class that was constructed by the composer-based resolver dynamically with the declare statements as defined in composer.json. Not sure: Perhaps you even do not need a new concept of a resolver but could extend the concept of autoloaders? Regards Thomas
  106559
August 12, 2019 13:45 larry@garfieldtech.com ("Larry Garfield")
On Mon, Aug 12, 2019, at 3:26 AM, Nikita Popov wrote:
> On Mon, Aug 12, 2019 at 10:17 AM Nicolas Grekas < > nicolas.grekas+php@gmail.com> wrote:
> > I've read discussions about the notion of a "package" and the way we > > should define its boundaries. > > What about the following? > > > > Individual files could declare their package using this style: > > > > > That would be enough to group a set of files together and make them share > > eg some private classes, some optional PHP behaviors, etc. > > > > The right side "MyVendor\MyPackage" would also be a FQCN that PHP would > > autoload as a regular class. The corresponding class would then be the > > place where ppl would declare the engine behavior they want for their > > package (strict types, etc). To enforce this, the engine could require that > > the "MyPackage" class implements some interface/extend some base abstract > > class. > > > > Of course, one could hijack a package and declare an unrelated file as > > part of it, but I don't think that's an issue: the situation is the same as > > for namespaces, where one can hijack a third party vendor namespace. In > > practice, it proved not being an issue, and the original author's intent is > > clear: "this is my namespace/package, if you mess with it, fine, but you're > > on your own". > > > > Nicolas > > > > FTR I've created a draft-implementation for a package system here: > https://github.com/php/php-src/pull/4490 > > It uses a slightly different approach in that it keeps the package name a > string (that should usually match the Composer package name) and uses a > function to register the package. > > The main annoyance is that this requires declaring the package in every > file, something I would like to avoid. An alternative I played with is to > allow specifying the package at include time, which would allow the > autoloader to specify which package a file is part. However, while this is > more ergonomic for the user, I'm afraid that this will make static analysis > & IDE scenarios problematic, because they will not be able to easily know > what the package is in cases that fall outside convention. So in the end, > an explicit per-file package declaration may be the best we can do. > > Nikita
I don't think declaring the package in each file is necessarily bad. Every language I've worked in either has you declare a package explicitly or implies it off of the file system and you don't get any say in the matter. Of the two, I prefer the former. My concern with using a function call is that it introduces/increases the potential for a file to say it uses a namespace that isn't yet defined. What happens then? Fatal? Autoload trigger? The code ends up un-packaged silently? (Please not the last one.) I don't feel like "it's Composer's problem" is a good answer here. Making the package name be a class name at least makes autoloading a natural and well understood way to handle the lookup. There may be other benefits to using a class/methods to define a package, or possibly even downsides; I'm not sure there. More research needed. Same for avoiding package name collisions; "it's FIG's problem" may be an answer, but it seems like a poor one. In practice I'd expect package definitions to be the obvious example of the preloader, but there will always be cases where the preloader is not available or cannot be used so we need to consider the performance impact of registering packages in every request. Has anyone done in-depth research into how other languages handle packages, and what advantages packages would have over just our existing nested namespaces? I feel like there's ample prior art here that we should not ignore. --Larry Garfield
  106560
August 12, 2019 14:00 markyr@gmail.com (Mark Randall)
On 12/08/2019 14:45, Larry Garfield wrote:
> I don't think declaring the package in each file is necessarily bad. Every language I've worked in either has you declare a package explicitly or implies it off of the file system and you don't get any say in the matter. Of the two, I prefer the former.
I concur with this, I would much rather specify a reference to external metadata once-per-file than have it controlled by invisible external state. If I send an individual file to a co-worker, IMO it should be immediately obvious at the top of that file that it belongs to package and may depend on that package definition to control its behaviour (especially if it gets loaded up with declares or editions). I'll simply be replacing my ubiquitous strict-types declare with whatever was used to reference this package. Mark Randall
  106561
August 12, 2019 17:13 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Larry,

pon., 12 sie 2019 o 15:45 Larry Garfield <larry@garfieldtech.com>
napisał(a):

> > Has anyone done in-depth research into how other languages handle > packages, and what advantages packages would have over just our existing > nested namespaces? I feel like there's ample prior art here that we should > not ignore. >
I did some writings on that https://brzuchal.com/posts/packages-in-programming-languages/ was a little hurry but tried my best to grasp key aspects of package / module concept in other languages. -- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  106552
August 12, 2019 08:27 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Nicolas,

pon., 12 sie 2019 o 10:17 Nicolas Grekas grekas+php@gmail.com>
napisał(a):

> Le lun. 11 déc. 2017 à 14:44, Nikita Popov ppv@gmail.com> a écrit > : > > > Some time ago I introduced the following proposal for namespace-scoped > > declares: > > > > https://wiki.php.net/rfc/namespace_scoped_declares > > > > The idea is to allow specifying declare directives for a whole library or > > project using: > > > > namespace_declare('Vendor\Lib', ['strict_types' => 1]); > > > > I've finally gotten around to implementing this proposal ( > > https://github.com/php/php-src/pull/2972) and would like to move forward > > with it. > > > > The reason why I'm picking it up again is some feedback I received for > the > > explicit call-time send-by-ref proposal. The main objection seems to be > > that the feature has limited usefulness if it's optional rather than > > required, because you still can't be sure that something is a by-value > > pass, just because no & is present. At the same time, we can't make this > > required anytime soon due to the large BC impact. > > > > Namespace-scoped declares are perfectly suited to resolve this problem. > We > > can introduce a require_explicit_send_by_ref declare directive to make > the > > call-site annotation required, and libraries/projects can easily opt-in > to > > it using namespace_declare(). There would be no BC impact, while at the > > same time projects could benefit from the additional clarity and > > performance improvements immediately. > > > > I've read discussions about the notion of a "package" and the way we should > define its boundaries. > What about the following? > > Individual files could declare their package using this style: > > That would be enough to group a set of files together and make them share > eg some private classes, some optional PHP behaviors, etc. > > Why suggesting use of declare for that?
Wouldn't a new "package" keyword be more appropriate for that? For eg.: > The right side "MyVendor\MyPackage" would also be a FQCN that PHP would > autoload as a regular class. The corresponding class would then be the > place where ppl would declare the engine behavior they want for their > package (strict types, etc). To enforce this, the engine could require that > the "MyPackage" class implements some interface/extend some base abstract > class. > > Of course, one could hijack a package and declare an unrelated file as part > of it, but I don't think that's an issue: the situation is the same as for > namespaces, where one can hijack a third party vendor namespace. In > practice, it proved not being an issue, and the original author's intent is > clear: "this is my namespace/package, if you mess with it, fine, but you're > on your own". > > Nicolas >
-- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com
  106565
August 13, 2019 09:02 rowan.collins@gmail.com (Rowan Collins)
On 12/08/2019 09:17, Nicolas Grekas wrote:
> Individual files could declare their package using this style: > > That would be enough to group a set of files together and make them share > eg some private classes, some optional PHP behaviors, etc. > > The right side "MyVendor\MyPackage" would also be a FQCN that PHP would > autoload as a regular class. The corresponding class would then be the > place where ppl would declare the engine behavior they want for their > package (strict types, etc). To enforce this, the engine could require that > the "MyPackage" class implements some interface/extend some base abstract > class.
I really like this approach. It allows a package definition file to exist, without either the language or the header of each file having to define its location. Importantly, although it would trigger the autoloader if not yet defined, the package could be defined in advance, for instance as part of a preload directive. It could even be possible to define multiple packages in one file, or define them dynamically, using all the existing features of the language like require and eval. On a bikeshedding note, I've never liked the way declare syntax looks, so would prefer a new keyword, but the symmetry of replacing
  106566
August 13, 2019 11:01 markyr@gmail.com (Mark Randall)
On 13/08/2019 10:02, Rowan Collins wrote:
> I really like this approach. It allows a package definition file to > exist, without either the language or the header of each file having to > define its location.
# # File: /lib/company/project1/a/b/MyClass.php #