Opt-in "use function *;" for skipping check for function/const in alternate namespace

php.internals

tyson andre

6 years ago
What are your thoughts about syntax such as the following, similar to the syntax for Java (only for use of functions/constants from the root scope) <?php namespace My\NS; use function *; use const *; // can use functions/constants from namespaces that aren't the global namespace to override '*' // "use *" is not valid. printf("PHP Version is: %s\n", PHP_VERSION); A similar opt-in directive syntax "use function root;" was mentioned as an alternative to the "Deprecation of fallback to root scope" RFC https://marc.info/?l=php-internals&m=151788196501507&w=2 . (I couldn't find any other mentions after a quick search) Benefits of "use function *": - Visually cleaner and less prone to merge conflicts than "use" for dozens of functions/constants all from the root scope. (or writing \printf("PHP Version is: %s\n", \PHP_VERSION), or mixes of the above) - Due to the absence of function autoloading (not supported in php to performance overhead), functions outside the global scope are rare in practice. - This would make it easier to make edits to a project (if the project's code style involves the unambiguous use of functions/global constants) - This makes using unambiguous functions/global constants easier. (for a small performance boost due to the php interpreter not needing to check both namespaces, opcache knowing exactly which function/constant it is, etc) - Does not break third-party code The other RFC https://wiki.php.net/rfc/fallback-to-root-scope-deprecation had the following notable objections, and I'm not sure of the current status of it: - Deprecating the behavior of existing code would break (possibly unmaintained) third-party libraries and require a lot of code changes, discouraging updates "use function *;" wouldn't. - Some mocking libraries will declare functions in namespaces being tested, with the same name as the global function. My proposal has similar drawbacks - third party code that started using "use function *" would break those libraries (but so would manually adding the same namespace uses). But there are alternatives to those libraries, such as uopz, runkit7, and SoftMocks (SoftMocks changes the class autoloader to replace code with instrumented code).

Benjamin Eberlei

6 years ago
On Thu, Nov 28, 2019 at 1:02 AM tyson andre <tysonandre775@hotmail.com> wrote:
> > What are your thoughts about syntax such as the following, similar to the > syntax for Java (only for use of functions/constants from the root scope) > > <?php > namespace My\NS; > use function *; > use const *; > // can use functions/constants from namespaces that aren't the global > namespace to override '*' > // "use *" is not valid. > printf("PHP Version is: %s\n", PHP_VERSION); > >
I love the idea. it is a small detail that needs fixing, especially since more and more open source libraries start to import *all* internal functions they use (which I personally find quite ugly) to get the small perf boost.

tyson andre

6 years ago
I implemented a PR for this if you wanted to know how this will work and how edge cases will be handled. https://github.com/php/php-src/pull/4951 > I was assuming that the ultimate goal was to remove (after deprecation) the fallback to global namespace. > In that regard, the `use function *` declaration (or whatever variation of that) is both a way to programmer to say the preferred semantics regarding not-fully-qualified function names, and a tool to let most legacy scripts survive without too much effort. Deprecations aren't part of this proposal - anyone who wants to can create follow-up proposals. My proposal is different from https://wiki.php.net/rfc/fallback-to-root-scope-deprecation (it wouldn't prevent someone else from doing that or something similar, later) My goal is a feature that can start being used in php 8.0 with no breaking changes - If implemented, the other RFC would probably have to be a deprecation in 8.x, and change in behavior in 9.0 > Re: Benjamin Eberlei > If this is a valid justification, perhaps we should do the opposite of the > "deprecate fallback to global scope" RFC you mentioned, and make the "use > function *" behaviour the default in some future version. The "deprecate fallback to global scope" is doing exactly what you're proposing. it's deprecating the runtime checking \MyNS\CONST_NAME, not \CONST_NAME > Re: Rowan Tommins > That still doesn't really explain what's happening, because in code that > doesn't use any namespaced functions, the line has no user-visible effect > at all - functions are *always* used from the global namespace. What it > actually does is switch *off* a language feature, so perhaps it should be > something more like: > > declare(lookup_functions_in_current_namespace=false); It does have some impact on the user: - In a large codebase with 'use function *' in a given file, the user can now be certain that is_string() is actually the global function in code and tests, instead of mostly certain. - The code has slightly better performance I considered declare(), and could include that as a voting option for an RFC. Explanations of what this does would be available as docs on php.net if this was merged. Some parts of syntax are bound to be non-obvious to people learning any language (e.g. "use function is_string" is useful, "use is_string" isn't.) My reasons for this syntax instead of that are: - This syntax can go in one block of code, instead of separated by dozens of lines: <?php declare(strict_types=1); // dozens of lines of class uses, file comments, etc. use function otherns\my_function; use function *; - There are no per-namespace declare() directives right now, for code with multiple namespace blocks > Re: Rowan Tommins > An opt-in mechanism is certainly easier to migrate to, but it would be a > shame if every PHP file in 10 years time included a line like this: > > use function *; // don't know what this does, > but apparently it's good for performance ¯\_(ツ)_/¯ I'm not sure what assumptions you're making about php in 10 years (e.g. do you assume the other RFC will pass and this would become a no-op) Deprecating unqualified function/constant uses could/should be added independently of my proposal, and anyone who resume the other rfc could. ( https://wiki.php.net/rfc/fallback-to-root-scope-deprecation ) - From the discussion on the other RFC, I didn't get the impression it'd get enough votes because of the RFC break. - To make upgrading to 8.0 easier, it's probably make sense to deprecate that in 8.1 instead. If "use function *;" still changes behavior in 10 years, then without a feature like this, if someone were to fully qualify all function calls for a library for performance, there'd be around 10 lines per file instead of 1, (automatically or manually added) which is why this was proposed. (it'd also be likely that unnecessary uses would be left in) // I think that's all of them ¯\_(ツ)_/¯ // ... use function is_float; use function is_int; use function is_string; > Re: Claude Pache > The issue indeed is that the meaning is far from self-evident. > Maybe one could use something that everyone and their mother could understand at first reading, such as: > > use function from global namespace; They could understand that, but memorizing and typing the phrase (and possibly searching online for what phrase it was) would be less convenient, e.g. if english was a second language. Module uses, imports, etc. in most languages tend towards conciseness, and * was the most common option I've seen (in java, python. etc.). I still prefer "use function *;", but a shorter option might be (similar to C++): use/using global functions; use global consts;

Rowan Collins

6 years ago
On 28/11/2019 18:52, tyson andre wrote:
>> An opt-in mechanism is certainly easier to migrate to, but it would be a >> shame if every PHP file in 10 years time included a line like this: >> >> use function *; // don't know what this does, >> but apparently it's good for performance ¯\_(ツ)_/¯ > I'm not sure what assumptions you're making about php in 10 years > (e.g. do you assume the other RFC will pass and this would become a no-op)
My assumptions weren't particularly about the future of PHP, but about people's usage patterns. If nobody uses namespaced functions, then everyone will just turn on this option all the time, without ever needing to know what it does. The effect of adding it to 99% of PHP files would be nothing but a "magical" performance boost.
> if someone were to fully qualify all function calls for a library for performance, > there'd be around 10 lines per file instead of 1
Only if they did it by adding use statements, rather than prefixing the function calls with backslashes. I'm confused why anyone would bother keeping a long list of use statements up to date, rather than just making "use leading backslashes on global functions" part of the project's coding style. I can see that adding a single statement to each file would be better than either, though.
> My reasons for this syntax instead of that are: > > - This syntax can go in one block of code, instead of separated by dozens of lines: > > <?php declare(strict_types=1); > // dozens of lines of class uses, file comments, etc. > use function otherns\my_function; > use function *; > - There are no per-namespace declare() directives right now, for code with multiple namespace blocks
I think it's more than just a different choice of syntax, it's a different way of looking at the feature: rather than a special namespace import, it's an option for how the language behaves. My thinking is that if unprefixed functions *always* referred to the global namespace, we wouldn't say "the global namespace is imported by default", we'd just describe the behaviour. Looked at from that perspective, it seems natural to say "in this file, I want the behaviour where unprefixed functions mean global by default"; and in future, maybe be able to say "in this entire module, I want that behaviour". A more cosmetic consideration is that the more generic the syntax looks, the more people will want it to work for other use cases. I can imagine "use function * works but use Foo\* doesn't" ending up on websites like PHPSadness. A more specific statement, like a declare, or "use global functions", is more obviously a one-off. Regards,
-- Rowan Tommins (né Collins) [IMSoP]

Rowan Collins

6 years ago
On Thu, 28 Nov 2019 at 00:02, tyson andre <tysonandre775@hotmail.com> wrote:
> - Due to the absence of function autoloading (not supported in php to > performance overhead), functions outside the global scope are rare in > practice. >
If this is a valid justification, perhaps we should do the opposite of the "deprecate fallback to global scope" RFC you mentioned, and make the "use function *" behaviour the default in some future version. That would provide us longer term benefits, such as being able to introduce function autoloading. An opt-in mechanism is certainly easier to migrate to, but it would be a shame if every PHP file in 10 years time included a line like this: use function *; // don't know what this does, but apparently it's good for performance ¯\_(ツ)_/¯ Regards,
-- Rowan Tommins [IMSoP]

Claude Pache

6 years ago
> Le 28 nov. 2019 à 10:35, Rowan Tommins <rowan.collins@gmail.com> a écrit : > > On Thu, 28 Nov 2019 at 00:02, tyson andre <tysonandre775@hotmail.com> wrote: > >> - Due to the absence of function autoloading (not supported in php to >> performance overhead), functions outside the global scope are rare in >> practice. >> > > > If this is a valid justification, perhaps we should do the opposite of the > "deprecate fallback to global scope" RFC you mentioned, and make the "use > function *" behaviour the default in some future version. That would > provide us longer term benefits, such as being able to introduce function > autoloading.
I think it is better to keep the language as regular as possible, and consider that every non-fully-qualified name is relative to the current namespace, unless there is an explicit `use` declaration that says otherwise
> > An opt-in mechanism is certainly easier to migrate to, but it would be a > shame if every PHP file in 10 years time included a line like this: > > use function *; // don't know what this does, but apparently it's good for > performance ¯\_(ツ)_/¯
Or: “don't know what this does, but apparently it silences deprecation notices...” The issue indeed is that the meaning is far from self-evident. Maybe one could use something that everyone and their mother could understand at first reading, such as: use function from global namespace; —Claude

Rowan Collins

6 years ago
On Thu, 28 Nov 2019 at 11:01, Claude Pache <claude.pache@gmail.com> wrote:
> I think it is better to keep the language as regular as possible, and
consider that every non-fully-qualified name is relative to the current namespace, unless there is an explicit `use` declaration that says otherwise That's already false, and will only be true if we deprecate and remove the fallback to global scope.
> use function *; // don't know what this does, but apparently it's good for > > performance ¯\_(ツ)_/¯ > > Or: “don't know what this does, but apparently it silences deprecation > notices...” >
Silencing deprecation notices is always a temporary side-effect of fixing code to work *at all* in some future release. Or rather, it should be - I am 100% against any notion of "indefinitely deprecated and may never be removed", that should always be some other type of notice. If we were deprecating unqualified functions from being looked up in the current namespace, we would not need to add this new directive, we could raise deprecation notices whenever the current lookup logic succeeded in finding a namespaced function. Silencing the deprecation notice would require either fully qualifying the function name, or explicitly adding a "use" statement. The issue indeed is that the meaning is far from self-evident. Maybe one
> could use something that everyone and their mother could understand at > first reading, such as: > > use function from global namespace; >
That still doesn't really explain what's happening, because in code that doesn't use any namespaced functions, the line has no user-visible effect at all - functions are *always* used from the global namespace. What it actually does is switch *off* a language feature, so perhaps it should be something more like: declare(lookup_functions_in_current_namespace=false); That would also mean that it can be covered by any future way of setting language directives, such as settings per "module", bundling into "editions", etc. Regards,
-- Rowan Tommins [IMSoP]

Claude Pache

6 years ago
> Le 28 nov. 2019 à 12:56, Rowan Tommins <rowan.collins@gmail.com> a écrit : > > On Thu, 28 Nov 2019 at 11:01, Claude Pache <claude.pache@gmail.com> wrote: >> I think it is better to keep the language as regular as possible, and > consider that every non-fully-qualified name is relative to the current > namespace, unless there is an explicit `use` declaration that says otherwise > > That's already false, and will only be true if we deprecate and remove the > fallback to global scope. > > >> use function *; // don't know what this does, but apparently it's good for >>> performance ¯\_(ツ)_/¯ >> >> Or: “don't know what this does, but apparently it silences deprecation >> notices...” >> > > > Silencing deprecation notices is always a temporary side-effect of fixing > code to work *at all* in some future release. Or rather, it should be - I > am 100% against any notion of "indefinitely deprecated and may never be > removed", that should always be some other type of notice. > > If we were deprecating unqualified functions from being looked up in the > current namespace, we would not need to add this new directive, we could > raise deprecation notices whenever the current lookup logic succeeded in > finding a namespaced function. Silencing the deprecation notice would > require either fully qualifying the function name, or explicitly adding a > "use" statement. > > > > The issue indeed is that the meaning is far from self-evident. Maybe one >> could use something that everyone and their mother could understand at >> first reading, such as: >> >> use function from global namespace; >> > > > That still doesn't really explain what's happening, because in code that > doesn't use any namespaced functions, the line has no user-visible effect > at all - functions are *always* used from the global namespace. What it > actually does is switch *off* a language feature, so perhaps it should be > something more like: > > declare(lookup_functions_in_current_namespace=false); > > That would also mean that it can be covered by any future way of setting > language directives, such as settings per "module", bundling into > "editions", etc. > > Regards, > -- > Rowan Tommins > [IMSoP]
I wasn’t clear enough in my previous post: I was assuming that the ultimate goal was to remove (after deprecation) the fallback to global namespace. In that regard, the `use function *` declaration (or whatever variation of that) is both a way to programmer to say the preferred semantics regarding not-fully-qualified function names, and a tool to let most legacy scripts survive without too much effort. But probably I made a too strong assumption about the intended goal of the proposed feature? —Claude

Robert Hickman

6 years ago
> > What are your thoughts about syntax such as the following, similar to the syntax for Java (only for use of functions/constants from the root scope) > > <?php > namespace My\NS; > use function *; > use const *; > // can use functions/constants from namespaces that aren't the global namespace to override '*' > // "use *" is not valid. > printf("PHP Version is: %s\n", PHP_VERSION); >
I am in support of this, and my personal code still doesn't use namespaces at all due to things like this, relating to the handling of functions in the language. I personally think that the whole 'autoloading' concept is a bad idea, and ahead of time import and compilation to bytecode, as offered by preloading is a far better idea. On Thu, 28 Nov 2019 at 00:02, tyson andre <tysonandre775@hotmail.com> wrote:

tyson andre

6 years ago
My previous message can be seen at https://news-web.php.net/php.internals/107887 (marc.info seems to show it as base 64 encoded text.) It links to a PR https://github.com/php/php-src/pull/4951 implementing this >A more cosmetic consideration is that the more generic the syntax looks, >the more people will want it to work for other use cases. I can imagine >"use function * works but use Foo\* doesn't" ending up on websites like >PHPSadness. A more specific statement, like a declare, or "use global >functions", is more obviously a one-off. Good point. My original thinking was that it'd allow `use function Foo\*;` to be used in the future, but: - With the way namespaces work, using more than one "wildcard" would result in unintuitive behavior. (based on what files were loaded at call time, and what the order of the `use` was. Also, instead of checking 2 namespaces, php would have to check an arbitrary list of namespaces. - People new to the language would assume `count` would keep working despite adding `use function Foo\*;` (e.g. seeing that require/print/isset/empty/eval resemble functions, and would continue to work) Also, there's the large difference between PHP and Python/Java. The functions in the module are known at the time of import for those, but not for "use" in PHP. So rethinking it, something non-generic like "use global functions;" would be my preference. Initial plan for a voting structure: 1. Whether a way to opt into exclusively checking global functions and constants should exist 2. Whether this should be part of declare(lookup_functions_in_current_namespace=1) or a "use" statement, if it does exist 3. The preferred "use" syntax, if there is not clear preference ("use global functions;" or "use function *;" or otherwise)

Claude Pache

6 years ago
> > 2. Whether this should be part of declare(lookup_functions_in_current_namespace=1) or a "use" statement, if it does exist
Hi, I don’t think it should be a declare() directive, because: * It changes incompatibly the meaning of valid (non-error-throwing) code. The current existing declare() directive, declare(strict_types=*), has the nice property that code that does not throw error in *both* modes does not change behaviour. * When you want to know the FQN of a random identifier, it is necessary and sufficient to look at the “namespace” and the “use” declarations. With `use function *`, that won’t change. With `declare(lookup_functions_somewhere)`, you have now to look at an additional place. —Claude

Rowan Collins

6 years ago
On 29 November 2019 19:48:37 GMT+00:00, Claude Pache <claude.pache@gmail.com> wrote:
>I don’t think it should be a declare() directive, because: > >* It changes incompatibly the meaning of valid (non-error-throwing) >code. The current existing declare() directive, >declare(strict_types=*), has the nice property that code that does not >throw error in *both* modes does not change behaviour. > >* When you want to know the FQN of a random identifier, it is necessary >and sufficient to look at the “namespace” and the “use” declarations. >With `use function *`, that won’t change. With >`declare(lookup_functions_somewhere)`, you have now to look at an >additional place.
Hi Claude, I understand both of these points, but both are subtly inaccurate. 1) strict_types is not the first or only declare directive. Declaring an encoding fundamentally changes the meaning of a source file; probably it would be invalid in a different encoding, but errors are not the primary purpose of the directive. Declaring a value of "ticks" actually changes the *runtime* behaviour of the code. The manual defines declare vaguely as "used to set execution directives", so it's not particularly clear to me that changing namespace resolution would be outside of its purpose. 2) The existing fallback to global scope means that looking at the use statements of a file is not sufficient to resolve the ambiguity of an unprefixed function name. Indeed, the same line of code can execute two different functions within a running program if the namespaced function is defined at the right time. I'm generally not convinced that beginning the special directive with the word "use" automatically makes it easier to find or understand. Given some code mentioning "strpos", you wouldn't be able to scan the list of use statements for the word "strpos", you'd have to understand that there are two modes of execution, and look for a line that switches between those modes. I would expect that if it was worded as a use statement, coding standards would place it at the top of that list, so we're talking about the difference between: declare(strict_types=1); namespace Foo; use global functions; ... and declare(strict_types=1); declare(global_functions=1); namespace Foo; ... Regards,
-- Rowan Tommins [IMSoP]