[PHP-DEV] [Concept] Flip relative function lookup order (global, then local)

On 02/08/2024 23:00, Nick Lockheart wrote:

If class A relies on class B, but you want to swap out
class B with a stub to test class A in isolation,
is there a way to make every call to class B,
from class A, actually call a different class
during the test, without modifying class A's code?

There are libraries that do exactly this, such as Mockery's "overload" and "alias"; and some that do other transparent manipulations, like GitHub - dg/bypass-finals: Removes `final` and `readonly` keywords from source code on-the-fly and allows mocking of final methods and classes. It can be used together with any test tool such as PHPUnit or Mockery.

They work by generating code dynamically, based on the real code, and executing it before the real definition is loaded. The same approach could definitely be taken to replace every call to a global function, and would actually be more reliable than shadowing, because it could rewrite even calls with a leading "\" or "use function" statement.

Obviously, shadowing a function in one namespace is currently a lot easier than setting up such a rewriter; but I don't think we should let that convenience for a few use cases outweigh the benefits in performance that a change in behaviour could bring, particularly when combined with function autoloading.

--
Rowan Tommins
[IMSoP]

On Aug 22, 2024, at 5:32 PM, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:

I was thinking about this earlier, and how the migration is pretty much the same (and equally automatable) in either direction:

  • If unqualified calls become always local, then every global function call needs a use statement or prefixing with "".
  • If they become always global, then every local function call needs a use statement or prefixing with "namespace".

But the first option probably requires changes in the majority of PHP files in use anywhere; whereas the second only affects a small minority of code bases, and a small minority of code in those.

BUT, if people already complain about "" being ugly, having to write "namespace" is going to make them REALLY grumpy…

So maybe at the same time (or, probably, in advance) we need to come up with a nicer syntax for explicitly referencing the current namespace.

Unfortunately, finding unused syntax is hard, which is why we have "" in the first place (and for the record, I think it works just fine), but maybe something like "_" could work? Giving us:

namespace Foo;

$native_length = strlen(‘hello’); # same as \strlen(‘hello’)
$foo_length = _\strlen(‘hello’); # same as \Foo\strlen(‘hello’)

If having to type \strlen() is ugly — and I agree that is it — then having to type _\strlen() is what in university we would call “fugly,” to emphasize just how much worse something was vs. just run-of-the-mill “ugly.”

Having to prefix with a name like Foo, e.g. Foo\strlen() is FAR PREFERABLE to _\strlen() because at least it provides satiating information rather than the empty calories of a cryptic shorthand. #jmtcw, anyway.

If I had a time machine, I’d campaign for “unqualified means local” in PHP 5.3, and we’d all be used to writing “\strlen” by now; but “unqualified means global” feels much more achievable from where we are.

If I had a time machine I would campaign for real packages instead of what namespaces turned out to me, and that used sigils that do not double as the escape character for strings, but then both of us digress.

-Mike

BUT, if people already complain about "\" being ugly, having to write
"namespace\" is going to make them REALLY grumpy...
So maybe at the same time (or, probably, in advance) we need to come
up with a nicer syntax for explicitly referencing the current
namespace.
Unfortunately, finding unused syntax is hard, which is why we have
"\" in the first place (and for the record, I think it works just
fine), but maybe something like "_\" could work? Giving us:
namespace Foo;
$native_length = strlen('hello'); # same as \strlen('hello')
$foo_length = _\strlen('hello'); # same as \Foo\strlen('hello')

    namespace foo using global functions;

- or -

    namespace foo using local functions;

Tell PHP what you want at the per-file level.

On 23 August 2024 00:15:19 BST, Mike Schinkel <mike@newclarity.net> wrote:

Having to prefix with a name like Foo, e.g. Foo\strlen() is FAR PREFERABLE to _\strlen() because at least it provides satiating information rather than the empty calories of a cryptic shorthand. #jmtcw, anyway.

I knew I'd regret keeping the example short. Realistically, it's not a substitute for "\Foo\strlen", it's a substitute for "\AcmeComponents\SplineReticulator\Utilities\Text\strlen".

Having a syntax for "relative to current" is incredibly common in other path-like syntaxes. The most common marker is ".", and ".\foo" is literally how you'd refer to something in the current directory under DOS/Windows. But unfortunately, we don't have "." available, so I wondered if "_" would feel similar enough.

Another option would be to find a shorter keyword than "namespace" to put it in front. "ns\strlen(...)" is an obvious step from what we have currently, but it's not very obvious what it means, so maybe there's a different word we could use.

Rowan Tommins
[IMSoP]

On Fri, 2024-08-23 at 07:39 +0100, Rowan Tommins [IMSoP] wrote:

On 23 August 2024 00:15:19 BST, Mike Schinkel <mike@newclarity.net>
wrote:
> Having to prefix with a name like Foo, e.g. Foo\strlen() is FAR
> PREFERABLE to _\strlen() because at least it provides satiating
> information rather than the empty calories of a cryptic shorthand.
> #jmtcw, anyway.

I knew I'd regret keeping the example short. Realistically, it's not
a substitute for "\Foo\strlen", it's a substitute for
"\AcmeComponents\SplineReticulator\Utilities\Text\strlen".

Having a syntax for "relative to current" is incredibly common in
other path-like syntaxes. The most common marker is ".", and ".\foo"
is literally how you'd refer to something in the current directory
under DOS/Windows. But unfortunately, we don't have "." available, so
I wondered if "_" would feel similar enough.

Another option would be to find a shorter keyword than "namespace" to
put it in front. "ns\strlen(...)" is an obvious step from what we
have currently, but it's not very obvious what it means, so maybe
there's a different word we could use.

Rowan Tommins
[IMSoP]

Could be mistaken, but I think the way PHP handles namespaces
internally is sort of the same as a long string, rather than as a
tree/hierarchy.

ie. \AcmeComponents\SplineReticulator\Utilities\Text\strlen

is really like:

class AcmeComponentsSplineReticulatorUtilitiesTextstrlen {

   public function __construct(){

   }

}

And the "AcmeComponentsSplineReticulatorUtilitiesText" just kind of
gets appended to the front when the class name is registered.

I haven't done work on the namespace code, but I recall reading this
somewhere recently.

On Fri, Aug 23, 2024, at 09:27, Nick Lockheart wrote:

On Fri, 2024-08-23 at 07:39 +0100, Rowan Tommins [IMSoP] wrote:

On 23 August 2024 00:15:19 BST, Mike Schinkel <mike@newclarity.net>

wrote:

Having to prefix with a name like Foo, e.g. Foo\strlen() is FAR

PREFERABLE to _\strlen() because at least it provides satiating

information rather than the empty calories of a cryptic shorthand.

#jmtcw, anyway.

I knew I’d regret keeping the example short. Realistically, it’s not

a substitute for “\Foo\strlen”, it’s a substitute for

“\AcmeComponents\SplineReticulator\Utilities\Text\strlen”.

Having a syntax for “relative to current” is incredibly common in

other path-like syntaxes. The most common marker is “.”, and “.\foo”

is literally how you’d refer to something in the current directory

under DOS/Windows. But unfortunately, we don’t have “.” available, so

I wondered if “_” would feel similar enough.

Another option would be to find a shorter keyword than “namespace” to

put it in front. “ns\strlen(…)” is an obvious step from what we

have currently, but it’s not very obvious what it means, so maybe

there’s a different word we could use.

Rowan Tommins

[IMSoP]

Could be mistaken, but I think the way PHP handles namespaces

internally is sort of the same as a long string, rather than as a

tree/hierarchy.

ie. \AcmeComponents\SplineReticulator\Utilities\Text\strlen

is really like:

class AcmeComponentsSplineReticulatorUtilitiesTextstrlen {

public function __construct(){

}

}

And the “AcmeComponentsSplineReticulatorUtilitiesText” just kind of

gets appended to the front when the class name is registered.

I haven’t done work on the namespace code, but I recall reading this

somewhere recently.

This is mostly correct, the only thing missing from your strings is the \ character. I believe this even happens during compilation. Meaning it sees your namespace/uses and then rewrites the function/class calls during compile time. Thus an unqualified call is prepended with the current namespace defined at the top of the file.

If we were to go with any major change in the current lookup where it is perf or nothing, this is what I would propose for php 9.0 (starting with an immediate deprecation):

  1. any unqualified call simply calls the current namespace

  2. = php 9.0: no fallback to global

  3. < php 9.0: emit deprecation notice if falls back to global

This is how classes work (pretty sure), so it would be consistent.

Going the other way (global first) doesn’t really make sense because it is inconsistent, IMHO. Will it suck? Probably. Will it be easy to fix? Probably via Rector.

— Rob

If we were to go with any major change in the current lookup where it
is perf or nothing, this is what I would propose for php 9.0
(starting with an immediate deprecation):
1. any unqualified call simply calls the current namespace
2. >= php 9.0: no fallback to global
3. < php 9.0: emit deprecation notice if falls back to global
This is how classes work (pretty sure), so it would be consistent.

Going the other way (global first) doesn't really make sense because
it is inconsistent, IMHO. Will it suck? Probably. Will it be easy to
fix? Probably via Rector.

— Rob

A third option, which I haven't seen come up on the list yet, is that
unqualified functions that are PHP built-ins are treated as global, and
using a function having the same name as a built-in, in a namespace
scope, requires a fully qualified name to override the built-in.

It seems that if someone is writing `array_key_exists()` or similar
they probably mean the built-in function, and in the rare cases where
they do mean `\foo\array_key_exists()`, they can write it explicitly.

Functions that are *not* on the built-in function list could default to
the local namespace.

On 23 August 2024 01:42:38 BST, Nick Lockheart <lists@ageofdream.com> wrote:

BUT, if people already complain about "\" being ugly, having to write
"namespace\" is going to make them REALLY grumpy...
So maybe at the same time (or, probably, in advance) we need to come
up with a nicer syntax for explicitly referencing the current
namespace.

   namespace foo using global functions;

- or -

   namespace foo using local functions;

Tell PHP what you want at the per-file level.

This doesn't seem mutually exclusive to me. If you have a file where you've opted for "using global functions", you might want a way to reference a function in the current namespace.

It also doesn't address my other point, that having global as the default mode (even if we provide an option for local) is much less disruptive to existing code.

Regards,
Rowan Tommins
[IMSoP]

On Fri, 23 Aug 2024, at 08:27, Nick Lockheart wrote:

Could be mistaken, but I think the way PHP handles namespaces
internally is sort of the same as a long string, rather than as a
tree/hierarchy.

Just to be clear, PHP already has a syntax for explicitly resolving a name relative to the current namespace, it's just not needed very often. See e.g. Online PHP editor | output for Xfma5 and Online PHP editor | output for 3o2TD (You're right that underneath it's all just string concatenation, but that's all you need in this case.)

All I was talking about was alternative syntax that would behave in exactly the same way that "namespace\Foo" already does.

--
Rowan Tommins
[IMSoP]

On Fri, Aug 23, 2024, at 10:08, Nick Lockheart wrote:

If we were to go with any major change in the current lookup where it

is perf or nothing, this is what I would propose for php 9.0

(starting with an immediate deprecation):

  1. any unqualified call simply calls the current namespace
  1. = php 9.0: no fallback to global

  1. < php 9.0: emit deprecation notice if falls back to global

This is how classes work (pretty sure), so it would be consistent.

Going the other way (global first) doesn’t really make sense because

it is inconsistent, IMHO. Will it suck? Probably. Will it be easy to

fix? Probably via Rector.

— Rob

A third option, which I haven’t seen come up on the list yet, is that

unqualified functions that are PHP built-ins are treated as global, and

using a function having the same name as a built-in, in a namespace

scope, requires a fully qualified name to override the built-in.

It seems that if someone is writing array_key_exists() or similar

they probably mean the built-in function, and in the rare cases where

they do mean \foo\array_key_exists(), they can write it explicitly.

Functions that are not on the built-in function list could default to

the local namespace.

I was actually thinking of doing something like this for function autoloading, where extensions could register global functions that bypass the autoloader and go straight to global if it isn’t defined in the local namespace already. I decided not to even bring it up because it felt controversial (it would effectively be global first, except for user functions). Though, it might be a nice compromise?

— Rob

On Fri, 2024-08-23 at 09:16 +0100, Rowan Tommins [IMSoP] wrote:

On 23 August 2024 01:42:38 BST, Nick Lockheart <lists@ageofdream.com>
wrote:
>
> >
> > BUT, if people already complain about "\" being ugly, having to
> > write
> > "namespace\" is going to make them REALLY grumpy...
> > So maybe at the same time (or, probably, in advance) we need to
> > come
> > up with a nicer syntax for explicitly referencing the current
> > namespace.
>
> namespace foo using global functions;
>
> - or -
>
> namespace foo using local functions;
>
>
> Tell PHP what you want at the per-file level.

This doesn't seem mutually exclusive to me. If you have a file where
you've opted for "using global functions", you might want a way to
reference a function in the current namespace.

Correct, so if you use the example:

    namespace foo using global functions;

you can write:

    array_key_exists();

and it will be resolved as global without a namespace lookup and will
use the dedicated opcode.

But if you need to use a local function you can do:

    \foo\sort();

The proposed global/local declaration as part of the namespace
declaration just turns off namespace lookups and sets the default
resolution for **unqualified** names.

Fully qualified names are not affected.

It also doesn't address my other point, that having global as the
default mode (even if we provide an option for local) is much less
disruptive to existing code.

They are compatible, but related decisions.

I think it would be easier for people to accept a new PHP version where
unqualified names were always global, if we also had an option to make
local/namespaced the default resolution for *unqualified* names, on a
per-file basis, for those who need that.

Thus, there are multiple decision points:

1. Should we do namespace lookups on unqualified function calls at all?

2. If yes to 1, should we lookup in global first or local first?

3. Regardless of 1 or 2, should we let developers explicitly specify a
behavior for unqualified calls in the namespace declaration?

4. If yes to 1, should the behavior of namespace lookups change for
user-defined functions vs PHP built-in function names?

These aren't mutually exclusive, but they all work together to create a
complete behavior.

There are several ways that the above options could be combined:

### OPTION ONE ###

Using a regular namespace declaration still does an NS lookup, in the
same order, just like it normally works now.

That means that code that uses:

    namespace foo;

will behave exactly the same as today, with no BC breaks.

Developers using the new PHP version could opt-in to explicit namespace
behavior with:

    namespace foo using global functions;

or

    namespace foo using local functions;

In both cases, *fully-qualified* names still work the same.

Only *unqualified* names are affected by this directive, and they use
local only or global only, depending on the declaration.

### OPTION TWO ###

Namespace lookup is removed from a future version of PHP.

Code that uses the current namespace declaration:

    namespace foo;

will assume that all unqualified function calls are global scope.

To use a function in the local namespace, it can be fully qualified
with:

    \foo\MyFunction();

But, developers could also write:

     namespace foo using local functions;

And all unqualified function names would be resolved to local at
compile time. Global functions could still be accessed with a `\` if
this directive was used:

    \array_key_exists();

### OPTION THREE ###

Namespace lookup is removed from a future version of PHP.

Code that uses the current namespace declaration:

    namespace foo;

...will assume that an *unqualified* function name is a global function
*IF* it is a PHP built-in function.

Otherwise, *unqualified* function names that are *not* PHP built-in
functions will be presumed to be local to the namespace.

With Option Three, developers can still fully-qualify their functions:

    \foo\array_key_exists();

...to override a built-in name with a user function in the current
namespace.

Likewise, a fully-qualified:

    \MyFunction();

called from inside a namespace will still call the global function.

Only unqualified names are affected.

As an additional optional feature of Option Three, developers can
change this behavior with:

    namespace foo using global functions;

or

    namespace foo using local functions;

Only *unqualified* names are affected by this directive, and they use
local only or global only, depending on the namespace declaration.

In both cases, *fully-qualified* names still work the same.

Of course, there are many other possibilities that can be mixed-and-
matched.

>
>
> A third option, which I haven't seen come up on the list yet, is
> that
> unqualified functions that are PHP built-ins are treated as global,
> and
> using a function having the same name as a built-in, in a namespace
> scope, requires a fully qualified name to override the built-in.
>
> It seems that if someone is writing `array_key_exists()` or similar
> they probably mean the built-in function, and in the rare cases
> where
> they do mean `\foo\array_key_exists()`, they can write it
> explicitly.
>
> Functions that are *not* on the built-in function list could
> default to
> the local namespace.

I was actually thinking of doing something like this for function
autoloading, where extensions could register global functions that
bypass the autoloader and go straight to global if it isn't defined
in the local namespace already. I decided not to even bring it up
because it felt controversial (it would effectively be global first,
except for user functions). Though, it might be a nice compromise?

— Rob

I think we are all trying to achieve the same thing here.

There's different ways to go about it, each with pros and cons, but I
think we need to think through how all the parts fit together, because
I think if we can reach consensus, then several RFCs become compatible
with each other.

A change to how function namespace lookup works would affect function
auto-loading, and it may even make it simpler, depending on the other
options that we end up going with.

On 23 Aug 2024, at 15:29, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:

having global as the default mode (even if we provide an option for local) is much less disruptive to existing code.

Hi Rowan,

I don't disagree with this summary of the current state, but I think this misses an important factor: namespaced functions are currently nowhere near as popular as namespaced classes, and a significant part of that is almost certainly because we don't have function autoloading, nor any kind of visibility controls for functions (eg package private).

Making relative function names do the opposite of relative class names sounds like a great way to permanently kill any prospects of encouraging developers to use regular namespaced functions in place of static classes as "bag of functions", which is what we keep hearing we should use - most notably on a recent RFC to embody the concept of a static class.

So we're told "no don't use classes for static functions like that, use proper functions".

We already can't autoload them which makes them less appealing, and less practical.

In a world where global functions take precedence over local ones because some people don't like writing a single \ character, autoloading would be a moot point because if you preference global functions you're implicitly telling developers they shouldn't write namespaced functions, by making them harder and less intuitive to use.

Cheers

Stephen

Am 23.08.2024 um 11:34 schrieb Nick Lockheart <lists@ageofdream.com>:

I think we are all trying to achieve the same thing here.

I'm not sure who "we" and what "same thing" here exactly is.

I recall the following arguments for changing the current situation about function look ups:
- Performance
- Function autoloading
- Consistency

Did I miss something big?

First of all I don't think the performance argument holds enough weight as I'm very doubtful this impacts performance of a real world application in a significant way. And for people *really* hitting this problem there is a solution already.
Secondly I am a bit confused about the whole function autoloading discussion: There is already a good-enough mechanism (putting them as static functions inside a tool class). I just don't consider the hoops we have to jump through to get a more "pure" or fine-grained solution for a special problem not worth it. As for the "don't use classes for static functions" I've yet to see a good argument apart from personal preference.
As far as consistency goes I've yet to encounter someone being confused about function resolution. But then again I'm not reaching namespaces for PHP classes.

While modern tooling possibly can adapt source code to the new style efficiently I have to maintain too many installations of PHP projects on various hosters to looking forward to that. And the argument that "you can just stay on an old PHP version" is just not a feasible solution either..

Maybe we should take a step back and reevaluate the pros and cons.

- Chris

On Fri, 23 Aug 2024, 11:02 Stephen Reay, <php-lists@koalephant.com> wrote:

On 23 Aug 2024, at 15:29, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:

having global as the default mode (even if we provide an option for local) is much less disruptive to existing code.

Hi Rowan,

I don’t disagree with this summary of the current state, but I think this misses an important factor: namespaced functions are currently nowhere near as popular as namespaced classes, and a significant part of that is almost certainly because we don’t have function autoloading, nor any kind of visibility controls for functions (eg package private).

Making relative function names do the opposite of relative class names sounds like a great way to permanently kill any prospects of encouraging developers to use regular namespaced functions in place of static classes as “bag of functions”, which is what we keep hearing we should use - most notably on a recent RFC to embody the concept of a static class.

So we’re told “no don’t use classes for static functions like that, use proper functions”.

We already can’t autoload them which makes them less appealing, and less practical.

In a world where global functions take precedence over local ones because some people don’t like writing a single \ character, autoloading would be a moot point because if you preference global functions you’re implicitly telling developers they shouldn’t write namespaced functions, by making them harder and less intuitive to use.

Cheers

Stephen

I’ve taken the time to carefully read Ilija’s proposal and all followup messages.

This is a great proposal, Ilija, it will immediately benefit 95%+ userbase.

It looks to me that the pro’s outweigh the con’s, as well as Ilija having done good research here already.

As for next steps, I’m suggesting that Ilija reach out to the core team at Symfony, Zend, Laravel as well as WordPress, Magento, Drupal teams … for their consideration and input, and bring it back here/RFC.

The latter group of project’s codebases can be quite “quirky”, and quite creative, in how they use PHP compared to a traditional “framework”.

We need to understand how this could negatively impact how their systems are put together, and we definitely want to, upfront, identify anything we wouldn’t normally think of, that they can spot, since they know their own codebases better than we do.

We already have the composer analysis, so after we have these framework/project analysis then we can make a strongly informed decision on the impact, positively or negatively, this change will make.

Many thanks,
Paul

On Fri, Aug 23, 2024, at 12:14, Christian Schneider wrote:

Am 23.08.2024 um 11:34 schrieb Nick Lockheart <lists@ageofdream.com>:

I think we are all trying to achieve the same thing here.

I’m not sure who “we” and what “same thing” here exactly is.

I recall the following arguments for changing the current situation about function look ups:

  • Performance

  • Function autoloading

  • Consistency

Did I miss something big?

Nick was replying to me :p, judging by the quoted paragraph.

First of all I don’t think the performance argument holds enough weight as I’m very doubtful this impacts performance of a real world application in a significant way. And for people really hitting this problem there is a solution already.

Secondly I am a bit confused about the whole function autoloading discussion: There is already a good-enough mechanism (putting them as static functions inside a tool class). I just don’t consider the hoops we have to jump through to get a more “pure” or fine-grained solution for a special problem not worth it. As for the “don’t use classes for static functions” I’ve yet to see a good argument apart from personal preference.

As far as consistency goes I’ve yet to encounter someone being confused about function resolution. But then again I’m not reaching namespaces for PHP classes.

As far as function overloading goes, I recommend checking out a draft RFC I’ve been working on a very, very long time: https://wiki.php.net/rfc/records. In some off-list discussions, it was clear that if I wanted this syntax, I would need to pursue function autoloading. Further, function autoloading is a clearly missing feature that would be useful in many situations. If function autoloading doesn’t work out, I will need to take a different approach to that syntax (which is fine, but not something I want because I chose the syntax for a very good reason). That being said, I’m not ready to discuss records here, so this is the first and last time I’ll mention it on the thread. There is a Reddit post in r/php and a GitHub repo if you are interested in discussing records. There are very many things to work out still, and it is very much work-in-progress.

While modern tooling possibly can adapt source code to the new style efficiently I have to maintain too many installations of PHP projects on various hosters to looking forward to that. And the argument that “you can just stay on an old PHP version” is just not a feasible solution either…

Maybe we should take a step back and reevaluate the pros and cons.

  • Chris

— Rob

On Fri, 23 Aug 2024, at 10:58, Stephen Reay wrote:

Making relative function names do the opposite of relative class names
sounds like a great way to permanently kill any prospects of
encouraging developers to use regular namespaced functions in place of
static classes as "bag of functions", which is what we keep hearing we
should use - most notably on a recent RFC to embody the concept of a
static class.

That's why I brought up the point about making it easy to explicitly say "relative to current", just as you can explicitly say "relative to global" with a single "\".

It's also worth remembering that this whole discussion has no effect on using functions that are defined in a *different* namespace. The below code might benefit from function autoloading, but would not be helped, hurt, or changed in any way by any of the proposals in this thread:

namespace Acme\Foo\Controller;

use function Acme\StringUtils\better_strlen;
use Acme\StandardUtils as Std;

$foo = Std\generate_something();
$len = better_strlen($foo);
\Acme\Debug\out($len);

Regards,
--
Rowan Tommins
[IMSoP]

On Fri, 23 Aug 2024, at 10:58, Stephen Reay wrote:

In a world where global functions take precedence over local ones
because some people don't like writing a single \ character,
autoloading would be a moot point because if you preference global
functions you're implicitly telling developers they shouldn't write
namespaced functions, by making them harder and less intuitive to use.

Sorry to reply to the same message twice, but as a concrete example, consider this code:

// Definition
namespace Acme\Foo;
class Utils {
    public static function magic(string $x): int {
        return \strlen($x);
    }
    public static function more_magic(string $x): int {
         return self::magic($x) * 2;
    }
}

// Caller
namespace Acme\MyApp\SearchPage;
use Acme\Foo\Utils;
echo Utils::more_magic($_GET['query']);

Rewritten as namespaced functions, with current PHP:

// Definition
namespace Acme\Foo\Utils;
function magic(string $x): int {
     return strlen($x);
}
function more_magic(string $x): int {
      return magic($x) * 2;
}

// Caller
namespace Acme\MyApp\SearchPage;
use Acme\Foo\Utils;
echo Utils\more_magic($_GET['query']);

With "unqualified names are global", but a new "_\" shorthand for "relative to current", the caller is completely unaffected, but the definition becomes:

namespace Acme\Foo\Utils;
function magic(string $x): int {
     return strlen($x);
}
function more_magic(string $x): int {
      return _\magic($x) * 2;
}

Note how the "_\" is used in all the same places as "self::" was in the "static class" version.

With "unqualified names are local", the change is very similar, but "the other way around":

namespace Acme\Foo\Utils;
function magic(string $x): int {
     return \strlen($x);
}
function more_magic(string $x): int {
      return magic($x) * 2;
}

Regards,
--
Rowan Tommins
[IMSoP]

Hi Rowan,

On Aug 23, 2024, at 2:39 AM, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:
On 23 August 2024 00:15:19 BST, Mike Schinkel <mike@newclarity.net> wrote:

Having to prefix with a name like Foo, e.g. Foo\strlen() is FAR PREFERABLE to _\strlen() because at least it provides satiating information rather than the empty calories of a cryptic shorthand. #jmtcw, anyway.

I knew I'd regret keeping the example short. Realistically, it's not a substitute for "\Foo\strlen", it's a substitute for "\AcmeComponents\SplineReticulator\Utilities\Text\strlen".

And similarly, I too regret keeping my answer short. I was assuming what I omitted would be obvious.

(And I am not being snarky, I literally thought about including this next but then felt I did not need to. Hindsight!)

So, long namespaces is why PHP has the `use` statement, making references in functions short and sweet, e.g:

namespace \AcmeComponents\SplineReticulator\Utilities\Text
use \AcmeComponents\SplineReticulator\Utilities\Text

function Foo():int {
  return Text\strlen("Hello World");
}

(Of course, that is a lot of redundant boilerplate.)

Another option would be to find a shorter keyword than "namespace" to put it in front. "ns\strlen(...)" is an obvious step from what we have currently, but it's not very obvious what it means, so maybe there's a different word we could use.

So rather than all that boilerplate, and rather than yet another special set of characters developers would need to learn and remember — and tooling would need to adjust to — we could instead easily add an automatic `use` statement for every namespace, as long as no existing use statement conflicts with it.

An automatic `use` would then give us the following, which provides a strong information sent and is really consistent with the nature of the PHP language:

namespace \AcmeComponents\SplineReticulator\Utilities\Text

function Foo():int {
  return Text\strlen("Hello World");
}

The above of course could result in BC breaks IF there happened to be existing code that referenced Text\strlen() where Text was a top-level namespace, AND that code was not remediated when this change takes place. However, I am guessing those collisions would be pretty rare as both the namespace and the symbol would have to match to be in conflict.

Having a syntax for "relative to current" is incredibly common in other path-like syntaxes. The most common marker is ".", and ".\foo" is literally how you'd refer to something in the current directory under DOS/Windows. But unfortunately, we don't have "." available, so I wondered if "_" would feel similar enough.

I'll be honest, the association with the relative path of `.\` did not occur to me when you presented `_\` so after you stating this I pondered if from that perspective.

However, frankly, I am not sold on that perspective. Something about it does not feel right. I can't currently give any more objective arguments than that, so I will just leave it as #jmctw.

I will say if we were going with relative path, I think `\\strlen()` would be preferable to `_\strlen()`. Subjectively `\\` is easier for me to "see" and thus does not look so out of place to me.

OTOH the objective arguments for `\\` over `_\` are it is much easier to type: slash+slash vs. shift-underscore+nonshift-slash. There is a precedent in URIs with `//`, albeit not exactly equivalent. And finally, it also does not use a sigil that could be better used elsewhere in some as-yet-to be agreed or envisioned future use. #fwiw

-Mike

On Fri, 23 Aug 2024, at 12:29, Mike Schinkel wrote:

namespace \AcmeComponents\SplineReticulator\Utilities\Text

function Foo():int {
  return Text\strlen("Hello World");
}

The above of course could result in BC breaks IF there happened to be
existing code that referenced Text\strlen() where Text was a top-level
namespace

It wouldn't be a top-level namespace that would cause a conflict, but a nested one: currently the above code resolves the function name as "AcmeComponents\SplineReticulator\Utilities\Text\Text\strlen" (note the "...\Text\Text\...").

It's an interesting suggestion, but I'm not totally sold on "use the end of the current namespace" being easier to remember than "use this symbol or keyword".

return namespace\strlen("Hello World"); # current syntax, rather long and unclear
return _\strlen("Hello World"); # short, but maybe a bit cryptic
return Text\strlen("Hello World"); # variable length, relies on current context
return NS\strlen("Hello World"); # shortening of current keyword
return self\strlen("Hello World"); # maybe confusing to reuse a keyword?
return current\strlen("Hello World"); # clear, but a bit long

--
Rowan Tommins
[IMSoP]