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).
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.
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).
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, theuse 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 thatis_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;
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]
- 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]
Le 28 nov. 2019 à 10:35, Rowan Tommins rowan.collins@gmail.com a écrit :
- 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
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 explicituse
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]
Le 28 nov. 2019 à 12:56, Rowan Tommins rowan.collins@gmail.com a écrit :
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 explicituse
declaration that says otherwiseThat'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
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.
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).
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 theuse
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 addinguse 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:
- Whether a way to opt into exclusively checking global functions and constants should exist
- Whether this should be part of declare(lookup_functions_in_current_namespace=1) or a "use" statement, if it does exist
- The preferred "use" syntax, if there is not clear preference ("use global functions;" or "use function *;" or otherwise)
- 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. Withdeclare(lookup_functions_somewhere)
, you have now to look at an additional place.
—Claude
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.
Withuse 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.
-
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.
-
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]