L.S.,
I just noticed the following, which struck me as weird/inconsistent:
There are four different OO structures in PHP:
- Classes
- Interfaces
- Traits
- Enums
For all four, an *_exists()
function is available, i.e.
class_exists()
, interface_exists()
, trait_exists()
and
enum_exists()
[2].
But only for three out of the four, a get_declared_*()
function exists.
There is get_declared_classes()
, get_declared_interfaces()
,
get_declared_traits()
, but no get_declared_enums()
[2].
I'm aware that enums are internally considered classes and that
get_declared_classes()
will retrieve them [1], but the same could be
said about interfaces and traits, yet they do have their own
get_declared_*()
function.
Should a get_declared_enums()
function be added ?
And should the get_declared_classes()
function be adjusted to exclude
enums ?
I did check the enum RFC [3], but I couldn't find any mention or
discussion about this in the RFC.
Smile,
Juliette
1: https://3v4l.org/0ub6I
2: https://www.php.net/manual/en/ref.classobj.php
3: https://wiki.php.net/rfc/enumerations
L.S.,
I just noticed the following, which struck me as weird/inconsistent:
There are four different OO structures in PHP:
- Classes
- Interfaces
- Traits
- Enums
For all four, an
*_exists()
function is available, i.e.
class_exists()
,interface_exists()
,trait_exists()
and
enum_exists()
[2].But only for three out of the four, a
get_declared_*()
function
exists.
There isget_declared_classes()
,get_declared_interfaces()
,
get_declared_traits()
, but noget_declared_enums()
[2].I'm aware that enums are internally considered classes and that
get_declared_classes()
will retrieve them [1], but the same could be
said about interfaces and traits, yet they do have their own
get_declared_*()
function.Should a
get_declared_enums()
function be added ?
And should theget_declared_classes()
function be adjusted to exclude enums ?I did check the enum RFC [3], but I couldn't find any mention or
discussion about this in the RFC.Smile,
Juliette1: https://3v4l.org/0ub6I
2: https://www.php.net/manual/en/ref.classobj.php
3: https://wiki.php.net/rfc/enumerations
Conext: I can't remember the last time I used get_declared_classes()
(thanks to autoloading and class_exists()
it's a kinda pointless function), so when we were working on enums it never occurred to us to think about it. It wasn't a deliberate decision to omit, as far as I recall.
I think I'd be open to adding it; my concern would be the overlap with get_declared_classes()
, which as you note currently would include enums, and changing that is a BC break (even if a tiny one that I doubt would impact anyone).
--Larry Garfield
L.S.,
I just noticed the following, which struck me as weird/inconsistent:
There are four different OO structures in PHP:
- Classes
- Interfaces
- Traits
- Enums
For all four, an
*_exists()
function is available, i.e.
class_exists()
,interface_exists()
,trait_exists()
and
enum_exists()
[2].But only for three out of the four, a
get_declared_*()
function
exists.
There isget_declared_classes()
,get_declared_interfaces()
,
get_declared_traits()
, but noget_declared_enums()
[2].I'm aware that enums are internally considered classes and that
get_declared_classes()
will retrieve them [1], but the same could be
said about interfaces and traits, yet they do have their own
get_declared_*()
function.Should a
get_declared_enums()
function be added ?
And should theget_declared_classes()
function be adjusted to exclude enums ?I did check the enum RFC [3], but I couldn't find any mention or
discussion about this in the RFC.Smile,
Juliette1: https://3v4l.org/0ub6I
2: https://www.php.net/manual/en/ref.classobj.php
3: https://wiki.php.net/rfc/enumerations
Conext: I can't remember the last time I usedget_declared_classes()
(thanks to autoloading andclass_exists()
it's a kinda pointless function), so when we were working on enums it never occurred to us to think about it. It wasn't a deliberate decision to omit, as far as I recall.I think I'd be open to adding it; my concern would be the overlap with
get_declared_classes()
, which as you note currently would include enums, and changing that is a BC break (even if a tiny one that I doubt would impact anyone).--Larry Garfield
Thanks for the response. It was exactly a (custom) autoloader situation
which caused me to start wondering about this.
Should a
get_declared_enums()
function be added ?
My answer is "very much, yes."
Context: I can't remember the last time I used
get_declared_classes()
(...it's a kinda pointless function)
Your experience is one data point. Another data point is my experience that it is fortunate that get_declared_classes()
exists as I find it to be a very a useful function. The core library I use for most projects depends on it for class initialization.
Your comment made me curious to see if others use it, and there are 8.1k uses on GitHub:
Here is how I have used, in a greatly simplified example:
function autoload($class) {
$classCount = count(get_declared_classes());
if (!file_exists("src/{$class}.php")) {
return;
}
include_once "src/{$class}.php";
foreach (get_declared_classes() as $class) {
if (!method_exists($class,'init')) {
continue;
}
$class::init();
}
}
As an aside, for this use-case I have always wanted PHP to add both 1.) a get_declared_class_count()
function and 2) optional parameters for get_declared_classes()
that would allow requesting a subset of classes, such as just the newly added classes. How important this is depends on if PHP maintains that array internally and just returns a reference, or if it instantiates memory and populates the array of classes to return every time the function is called.
, so when we were working on enums it never occurred to us to think about it. It wasn't a deliberate decision to omit, as far as I recall.
I think I'd be open to adding it; my concern would be the overlap with
get_declared_classes()
, which as you note currently would include enums, and changing that is a BC break (even if a tiny one that I doubt would impact anyone).
Add another parameter — or include as part of the args array — something like this (though I am sure this could be bikeshed to be better):
get_declared_classes(DeclaredClasses:ONLY_CLASSES, ['starting_from'=>174]);
get_declared_classes(DeclaredClasses:ALL, ['recently_declared'=>3]);
Or even:
DeclaredSymbols::get_classes([$args]) <-- returns only classes, not enums
DeclaredSymbols::get_enums([$args])
DeclaredSymbols::get_interfaces([$args])
DeclaredSymbols::get_traits([$args])
#fwiw
Thanks for the response. It was exactly a (custom) autoloader situation which caused me to start wondering about this.
Seems Juliette's use-case may be at least somewhat similar to mine.
-Mike
Hey Juliette,
Should a
get_declared_enums()
function be added ?
Yes
And should the get_declared_classes()
function be adjusted to exclude
enums ?
No
You listed these entities: classes, interfaces, traits and enums.
But while there is a clear differentiation between what is considered a
class, an interface or a trait, enums are just a subtype of classes.
"all enums are classes, but not all classes are enums."
So for keeping this valid, get_declared_classes()
should return all
classes (including enums).
While get_declared_enums()
should return all enums.
If one really wants all classes except enums, they could use array_diff(
get_declared_classes(), get_declared_enums()
.
This would avoid the backward compatibility issues that Larry also
mentioned.
Regards,
Alex
Should a
get_declared_enums()
function be added ?
Here we go:
function get_declared_enums() {
$enums = [];
$exts = get_loaded_extensions(false);
foreach ($exts as $ext) {
$re = new ReflectionExtension($ext);
$classes = $re->getClasses();
foreach ($classes as $class) {
if ($class->isEnum()) {
$enums[] = $class->name;
}
}
}
return $enums;
}
Porting this to C is left as an excercise for the reader. ;) Hint:
https://github.com/php/php-src/blob/8853cf3ae950a1658054f286117bc8f77f724f00/Zend/zend_builtin_functions.c#L1371-L1399
Note that the terminating folding marker is backwards.
And should the
get_declared_classes()
function be adjusted to exclude
enums ?
For reasons that have been stated elsewhere in this thread, I don't
think so.
Cheers,
Christoph
Porting this to C is left as an excercise for the reader. ;) Hint:
https://github.com/php/php-src/blob/8853cf3ae950a1658054f286117bc8f77f724f00/Zend/zend_builtin_functions.c#L1371-L1399
I went ahead and created PR https://github.com/php/php-src/pull/15443
along with tests, UPGRADE notice, etc.
I think having a get_declared_enums
function will be helpful. The
implementation is simple and straightforward too.
Porting this to C is left as an excercise for the reader. ;) Hint:
https://github.com/php/php-src/blob/8853cf3ae950a1658054f286117bc8f77f724f00/Zend/zend_builtin_functions.c#L1371-L1399I went ahead and created PR https://github.com/php/php-src/pull/15443
along with tests, UPGRADE notice, etc.
Thank you very much!
I think having a
get_declared_enums
function will be helpful. The
implementation is simple and straightforward too.
I concur.
Christoph
I went ahead and created PR https://github.com/php/php-src/pull/15443
along with tests, UPGRADE notice, etc. I think having a
get_declared_enums
function will be helpful. The implementation is
simple and straightforward too.
Thanks for creating the PR Ayesh!
I'm presuming it's too late for PHP 8.4, what with feature freeze having
come & gone this week.
Based on the mostly supportive responses on the list, I wonder whether
an RFC is needed. If so, I'd be happy to create an initial draft (for
PHP 8.5).
Smile,
Juliette
I'm presuming it's too late for PHP 8.4, what with feature freeze having come & gone this week.
Based on the mostly supportive responses on the list, I wonder whether an RFC is needed. If so, I'd be happy to create an initial draft (for PHP 8.5).
Thank you!
We now have the Release Cycle Update RFC approved
(https://wiki.php.net/rfc/release_cycle_update#allow_features_that_do_not_require_an_rfc_in_the_beta_period),
and it does allow minor new features during the beta period.
Quoting from the RFC:
[snip]
It therefore seems illogical to allow RFC-requiring features to be merged during the beta period, but not minor improvements that do not require an RFC, such as adding constant, parameters, config options, or even small functions or methods.We propose to explicitly allow minor features, optimizations, and internal API / ABI changes, during the beta period.
I think the get_declared_enums
is small enough to be considered a
minor change given we don't change any engine internals. Given the
positive response on the mailing list, I think we can have this in PHP
8.4 as well, subject to RMs approval of course. There's also a PR from
yesterday (https://github.com/php/php-src/pull/15446) that added a new
Curl constant.
I think the
get_declared_enums
is small enough to be considered a
minor change given we don't change any engine internals. Given the
positive response on the mailing list, I think we can have this in PHP
8.4 as well, subject to RMs approval of course.
I'll keep my fingers crossed ;-)
I went ahead and created PR https://github.com/php/php-src/pull/15443 along with tests, UPGRADE notice, etc. I think having a
get_declared_enums
function will be helpful. The implementation is simple and straightforward too.
Thanks for creating the PR Ayesh!I'm presuming it's too late for PHP 8.4, what with feature freeze
having come & gone this week.Based on the mostly supportive responses on the list, I wonder whether
an RFC is needed. If so, I'd be happy to create an initial draft (for
PHP 8.5).Smile,
Juliette
I would prefer to have an RFC for this, even if it's short. I'm not against it, I just think there's enough non-trivial questions (eg, impact on get_declared_classes()
) that it warrants an RFC process/discussion.
--Larry Garfield
Le 17 août 2024 à 15:17, Larry Garfield larry@garfieldtech.com a écrit :
I went ahead and created PR https://github.com/php/php-src/pull/15443 along with tests, UPGRADE notice, etc. I think having a
get_declared_enums
function will be helpful. The implementation is simple and straightforward too.
Thanks for creating the PR Ayesh!I'm presuming it's too late for PHP 8.4, what with feature freeze
having come & gone this week.Based on the mostly supportive responses on the list, I wonder whether
an RFC is needed. If so, I'd be happy to create an initial draft (for
PHP 8.5).Smile,
JulietteI would prefer to have an RFC for this, even if it's short. I'm not against it, I just think there's enough non-trivial questions (eg, impact on
get_declared_classes()
) that it warrants an RFC process/discussion.--Larry Garfield
Hi,
I concur with Larry, it should not be rushed into PHP 8.4, but carefully considered if it is really the correct function to define. Enums were introduced three years ago, and until three days ago, there has been apparently no complaint of the missing function, and it has been proposed less because of a need than because of an apparent lack of symmetry with other get_declared_*()
functions. So, there is no urgency.
Now, the proposed design for get_declared_enums()
is problematic, because it will bring (or sustain) confusion. An enum is (imo rightfully) considered as a class for the purpose of class_exists(...)
; and following the same logic, the proposed get_declared_enums()
returns (rightfully) a subset of the names returned by get_declared_classes()
. On the other hand, in the mind of many people – as it can be seen in several messages in this thread, and citing the original poster –, there are four different OO structures in PHP: classes, interfaces, traits, and enums. For example, one is tempted to write naïvely:
function get_declared_symbols(): array {
return [ ...get_declared_classes(), ...get_declared_interfaces(), ...get_declared_traits(), ...get_declared_enums() ];
}
not realising that enums will appear twice in the returned list.
An alternative design that is less confusing could be the following. I am not really proposing it (as I am personally not convinced that get_declared_classes()
filtered by enum_exists()
is not sufficient), but I merely give it as an illustrative example of an api thought to avoid or reduce confusion. We could add an optional parameter to the existing get_declared_classes()
function, specifying what sort of classes they are interested in:
const SIMPLE_CLASS = 1;
const ABSTRACT_CLASS = 2;
const ANONYMOUS_CLASS = 4;
const ENUM = 8;
function get_declared_classes(int $type = SIMPLE_CLASS | ABSTRACT_CLASS | ANONYMOUS_CLASS | ENUM): array { /* ... */ }
—Claude
Should a
get_declared_enums()
function be added ?Here we go:
function get_declared_enums() { $enums = []; $exts = get_loaded_extensions(false); foreach ($exts as $ext) { $re = new ReflectionExtension($ext); $classes = $re->getClasses(); foreach ($classes as $class) { if ($class->isEnum()) { $enums[] = $class->name; } } } return $enums; }
Porting this to C is left as an excercise for the reader. ;) Hint:
<
https://github.com/php/php-src/blob/8853cf3ae950a1658054f286117bc8f77f724f00/Zend/zend_builtin_functions.c#L1371-L1399Note that the terminating folding marker is backwards.
And should the
get_declared_classes()
function be adjusted to exclude
enums ?For reasons that have been stated elsewhere in this thread, I don't
think so.
And here is a one liner:
function get_declared_enums() {
return array_filter(get_declared_classes(), 'enum_exists');
}
Should a
get_declared_enums()
function be added ?Here we go:
function get_declared_enums() { $enums = []; $exts = get_loaded_extensions(false); foreach ($exts as $ext) { $re = new ReflectionExtension($ext); $classes = $re->getClasses(); foreach ($classes as $class) { if ($class->isEnum()) { $enums[] = $class->name; } } } return $enums; }
And here is a one liner:
function get_declared_enums() { return array_filter(get_declared_classes(), 'enum_exists'); }
Nice!
Are you suggesting that there is no need for get_declared_enums() in the
core, since it can be implemented in userland with a single function call?
If so, I still would argue that it makes sense to implement the function
in the core for (a) consistency, and (b) performance reasons.
Cheers,
Christoph
On 16.08.2024 at 17:11, Nicolas
And here is a one liner:
function get_declared_enums() { return array_filter(get_declared_classes(), 'enum_exists'); }
Nice!
Are you suggesting that there is no need for get_declared_enums() in the
core, since it can be implemented in userland with a single function call?
I think he just compulsively golfs inefficient PHP code (I have the same
affliction).
Cheers,
Bilge
On 16.08.2024 at 17:11, Nicolas
And here is a one liner:
function get_declared_enums() { return array_filter(get_declared_classes(), 'enum_exists'); }
Nice!
Are you suggesting that there is no need for get_declared_enums() in the
core, since it can be implemented in userland with a single function call?
I think he just compulsively golfs inefficient PHP code (I have the same
affliction).
Possibly :D
I shared this as food for thoughts. I'm not convinced this function would
be useful personally, but that's not really an argument ;)
To everyone on this thread and thus nobody in specific (hence my top post):
Given that get_declared_*() populates a new ZEND_HASH each time the function is called[1] — which for classes can easily be hundreds of strings — would there be any appetite to:
-
Add an optional parameter — e.g.
get_declared_classes($startingFrom)
— that can limit the number of symbols starting with "n" where if n==100 then it would return array_slice($symbols,100), and -
Add a function —
get_declared_class_count()
— that returns the current number of declared symbols of whichever kind of symbol, so we can get the "n" before callinginclude
/require
to use with get_declared_*() after callinginclude
/require
?
Used in a (very simplistic) example:
function autoload($class) {
$startingFrom = get_declared_class_count();
if (!file_exists("src/{$class}.php")) {
return;
}
include_once "src/{$class}.php";
foreach (get_declared_classes($startingFrom) as $class) {
if (!method_exists($class,'init')) {
continue;
}
$class::init();
}
}
This would cut down on creating hashes with 100+ strings that get immediately thrown away, and also cut down on memory fragmentation/garbage collector churn.
-Mike
Should a
get_declared_enums()
function be added ?Here we go:
function get_declared_enums() {
$enums = [];
$exts = get_loaded_extensions(false);
foreach ($exts as $ext) {
$re = new ReflectionExtension($ext);
$classes = $re->getClasses();
foreach ($classes as $class) {
if ($class->isEnum()) {
$enums[] = $class->name;
}
}
}
return $enums;
}Porting this to C is left as an excercise for the reader. ;) Hint:
https://github.com/php/php-src/blob/8853cf3ae950a1658054f286117bc8f77f724f00/Zend/zend_builtin_functions.c#L1371-L1399Note that the terminating folding marker is backwards.
And should the
get_declared_classes()
function be adjusted to exclude
enums ?For reasons that have been stated elsewhere in this thread, I don't
think so.Cheers,
Christoph
To everyone on this thread and thus nobody in specific (hence my top post):
Given that get_declared_*() populates a new ZEND_HASH each time the function is called[1] — which for classes can easily be hundreds of strings — would there be any appetite to:
Add an optional parameter — e.g.
get_declared_classes($startingFrom)
— that can limit the number of symbols starting with "n" where if n==100 then it would return array_slice($symbols,100), andAdd a function —
get_declared_class_count()
— that returns the current number of declared symbols of whichever kind of symbol, so we can get the "n" before callinginclude
/require
to use with get_declared_*() after callinginclude
/require
?This would cut down on creating hashes with 100+ strings that get immediately thrown away, and also cut down on memory fragmentation/garbage collector churn.
-Mike
Mike,
While your suggestion does not directly relate to what triggered my mail
to the list, I very very much like the idea.
It's as if you are reading my mind or rather, reading one of the
autoloaders [1] I need to maintain, in which your above outlined
proposal should definitely make for more efficiency.
Having said that, the same parameter + extra function would then also be
needed for get_declared_interfaces()
, get_declared_traits()
- and,
if it would be added, get_declared_enums()
.
There is one problem I can see with this approach though: since PHP 7.4,
the return value of get_declared_classes()
(and friends) does not
guarantee any particular order anymore [2].
So, an array_slice($symbols, $n)
may not get you the latest classes
loaded, so I think this would only work if the order of classes is
guaranteed in some way.
Smile,
Juliette
1:
https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/6fbbc1078094d905f0773421f13830744a144d1e/autoload.php#L153-L247
2:
https://www.php.net/manual/en/function.get-declared-classes.php#refsect1-function.get-declared-classes-changelog
Hi Juliette,
To everyone on this thread and thus nobody in specific (hence my top post):
Given that get_declared_*() populates a new ZEND_HASH each time the function is called[1] — which for classes can easily be hundreds of strings — would there be any appetite to:
Add an optional parameter — e.g.
get_declared_classes($startingFrom)
— that can limit the number of symbols starting with "n" where if n==100 then it would return array_slice($symbols,100), andAdd a function —
get_declared_class_count()
— that returns the current number of declared symbols of whichever kind of symbol, so we can get the "n" before callinginclude
/require
to use with get_declared_*() after callinginclude
/require
?This would cut down on creating hashes with 100+ strings that get immediately thrown away, and also cut down on memory fragmentation/garbage collector churn.
-Mike
Mike,
While your suggestion does not directly relate to what triggered my mail to the list, I very very much like the idea.
Excellent!
It's as if you are reading my mind or rather, reading one of the autoloaders [1] I need to maintain, in which your above outlined proposal should definitely make for more efficiency.
Thank you for sharing that. Yes, your use-case is very similar to mine.
Having said that, the same parameter + extra function would then also be needed for
get_declared_interfaces()
,get_declared_traits()
- and, if it would be added,get_declared_enums()
.
That was my assumption and intention, but I did not say so explicitly. I should have.
Thanks to Ayesh's excellent PR, I was able to recognize that all of those functions in-fact are implemented via one internal function get_declared_class_impl()
so it only made sense — to me, at least — that modifying it would enable the same functionality for all four (4) userland functions.
There is one problem I can see with this approach though: since PHP 7.4, the return value of
get_declared_classes()
(and friends) does not guarantee any particular order anymore [2].
So, anarray_slice($symbols, $n)
may not get you the latest classes loaded, so I think this would only work if the order of classes is guaranteed in some way.
That is a super relevant insight and one I was actually not aware of.
Do you happen to know if it was triggered by an explicit RFC to that effect, or if it resulted because of a side-effect of some other implementation change? From my perspective, that is definitely a BC break and one that I am surprised I never noticed before? Do you also know if it is a case of "not guaranteed, but in-fact actual fact it always works that way, or not?"
I have not used my lib that needs that functionality to work in a few years, so maybe it is broken now and those who are now maintaining those apps have had to deal with it?
Given that you see value in the same use-case I had which was an autoloader, maybe it would be better to achieve the functionality we need in a different, more efficient way?
What if instead PHP were to implement an optional 2nd callback parameter to include()
/ require()
/ include_once()
/ require_once()
to allow us to capture the symbols loaded and their paths? The callback function could return void
and accept an array of $symbols
with the following guaranteed minimum structure?
$symbols = array(
'classes' => [],
'interfaces' => [],
'traits' => [],
'enums' => [],
);
With that you could reimplement all that PHP code you linked — at least for versions of PHP supporting the callback — to look like this:
function loadFile($path)
{
if (strpos(DIR, 'phar://') !== 0) {
$path = realpath($path);
if ($path === false) {
return false;
}
}
if (isset(self::$loadedClasses[$path]) === true) {
return self::$loadedClasses[$path];
}
include($path, function($symbols) use ($path) {
foreach ($symbols['classes'] as $className) {
self::$loadedClasses[$path] = $className;
self::$loadedFiles[$className] = $path;
}
});
return self::$loadedClasses[$path];
}//end loadFile()
This approach would be a less disruptive than my prior suggestion which would have added four (4) new *_count()
functions and new optional parameters for four (4) existing function vs. this proposal which just adds new optional parameters for four (4) other existing functions. Further, and the signature(s) required for parameters would be much easier to settle on.
I checked the code[2] and if I understand it correctly would (only?) require adding a Zend Observer[3] callback to be notified in zend_bind_class_in_slot()
although I still cannot figure out where that observer would need to be added.
Going this approach would also make the userland code in PHPCodeSniffer a lot more efficient than the code you currently must run for every included file simply to determine what symbols were loaded.
What do you think if this alternate approach?
-Mike
[1] https://github.com/php/php-src/blob/18d41502da0da1bb3928e60c41f1b821974c2c01/Zend/zend_builtin_functions.c#L1371
[2] https://github.com/php/php-src/blob/php-8.3.10/Zend/zend_compile.c#L1231-L1269
[3] https://www.linkedin.com/advice/3/what-some-best-practices-common-pitfalls-using-zend
Hi Mike,
There is one problem I can see with this approach though: since PHP 7.4, the return value of
get_declared_classes()
(and friends) does not guarantee any particular order anymore [2].
So, anarray_slice($symbols, $n)
may not get you the latest classes loaded, so I think this would only work if the order of classes is guaranteed in some way.
That is a super relevant insight and one I was actually not aware of.Do you happen to know if it was triggered by an explicit RFC to that effect, or if it resulted because of a side-effect of some other implementation change?
I honestly don't know, but if I look through the PHP 7.4 RFC list, I
have a niggling suspicion it may have something to do with the
Preloading RFC [1] ? I may very well be wrong though.
From my perspective, that is definitely a BC break and one that I am surprised I never noticed before? Do you also know if it is a case of "not guaranteed, but in-fact actual fact it always works that way, or not?"
Well, for the autoloader I previously linked to [2], it was a
problematic change which we did have to work around, see [3] and [4] for
details of the issues we saw (parent class vs child class order
reversals, issues when multiple classes were declared in one file).
I haven't deep-dived into the order change, other than to validate the
reported issues and proposed fixes.
What if instead PHP were to implement an optional 2nd callback parameter to
include()
/require()
/include_once()
/require_once()
to allow us to capture the symbols loaded and their paths? The callback function could returnvoid
and accept an array of$symbols
with the following guaranteed minimum structure?$symbols = array(
'classes' => [],
'interfaces' => [],
'traits' => [],
'enums' => [],
);
Except include()
and friends aren't function calls, but language
constructs/expressions and don't take parameters as such, so I don't see
how that would be possible without changing include()
and friends to
function calls (along the lines of what happened for exit
in PHP 8.4
with non-parenthesized use still possible to mitigate the otherwise huge
breaking change), or alternatively, introducing wrapper functions for
the language constructs - so no change to the existing functionality,
but new functions with a signature along the lines of
include_and_get_symbols( $path ): array
with the return value being
the symbols loaded from $path.
This approach would be a less disruptive than my prior suggestion
Not so sure about that considering the above ;-)
What do you think if this alternate approach?
This probably needs some more bike shedding ;-)
Smile,
Juliette
1: https://wiki.php.net/rfc/preload
2:
https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/6fbbc1078094d905f0773421f13830744a144d1e/autoload.php#L153-L247
3: https://github.com/squizlabs/PHP_CodeSniffer/pull/3130
4: https://github.com/squizlabs/PHP_CodeSniffer/issues/3145
What if instead PHP were to implement an optional 2nd callback parameter to
include()
/require()
/include_once()
/require_once()
to allow us to capture the symbols loaded and their paths? The callback function could returnvoid
and accept an array of$symbols
with the following guaranteed minimum structure?$symbols = array(
'classes' => [],
'interfaces' => [],
'traits' => [],
'enums' => [],
);Except
include()
and friends aren't function calls, but language constructs/expressions and don't take parameters as such, so I don't see how that would be possible without changinginclude()
and friends to function calls (along the lines of what happened forexit
in PHP 8.4 with non-parenthesized use still possible to mitigate the otherwise huge breaking change),
I know that those functions can be called as a function and return a value like the following:
$return_value = include($path);
I was making an assumption that since they be called like a function they could also accept optional parameters, although I admittedly was only assuming and do not know for sure. Do you know for a fact that they cannot take a 2nd parameter, or just assuming like I was?
Does anyone else know for certain it is would be possible for include()
et.al. to accept an optional 2nd parameter?
However, even if a 2nd parameter is not a viable option then this could be addressed by instead adding a single new function with a signature of spl_autoload_callback($fn callback):callback
where the callback has a signature of function ($symbols array)
which could be used like so:
function loadFile($path)
{
if (strpos(DIR, 'phar://') !== 0) {
$path = realpath($path);
if ($path === false) {
return false;
}
}
if (isset(self::$loadedClasses[$path]) === true) {
return self::$loadedClasses[$path];
}
$prior_callback = spl_autoload_callback(function($symbols) use ($path) {
foreach ($symbols['classes'] as $className) {
self::$loadedClasses[$path] = $className;
self::$loadedFiles[$className] = $path;
}
});
include($path);
spl_autoload_callback($prior_callback);
return self::$loadedClasses[$path];
}
This approach would be a less disruptive than my prior suggestion
Not so sure about that considering the above ;-)
Well then I think a single new function would be very low on the disruption scale, no?
This probably needs some more bike shedding ;-)
Shedding of bike undertaken. :-)
-Mike
I know that those functions can be called as a function and return a value like the following:
$return_value = include($path);
You are right that it has a return value, but wrong to put its argument in parentheses. This will not do what you expect:
$success = include($path) && somethingElse();
Because include is not actually a function, what is evaluated is $path && somethingElse()
, then either include true
or include false
(which will of course fail).
You instead need to write this:
$success = (include $path) && somethingElse();
I thought I'd added this example to the manual, but now can't find it.
Consequently, we can't just define optional parameters like we would a normal function. We could extend the syntax to allow "include $path, $whatever;" but it probably wouldn't feel very natural. It would be a syntax error to write "include($path, $whatever)" just as "echo $foo, $bar" is valid, but "echo($foo, $bar)" is not.
Regards,
Rowan Tommins
[IMSoP]
I know that those functions can be called as a function and return a value like the following:
$return_value = include($path);
You are right that it has a return value, but wrong to put its argument in parentheses. This will not do what you expect:
$success = include($path) && somethingElse();
Because include is not actually a function, what is evaluated is
$path && somethingElse()
, then eitherinclude true
orinclude false
(which will of course fail).You instead need to write this:
$success = (include $path) && somethingElse();
I thought I'd added this example to the manual, but now can't find it.
Consequently, we can't just define optional parameters like we would a normal function. We could extend the syntax to allow "include $path, $whatever;" but it probably wouldn't feel very natural. It would be a syntax error to write "include($path, $whatever)" just as "echo $foo, $bar" is valid, but "echo($foo, $bar)" is not.
Hi Rowan,
Thank you for confirming the nature of include, et. al.
So, if the functionality to be notified via a callback for all symbols loaded is a desired feature by other than me and maybe Juliette then the best approach I've come up with is to add an spl_autoload_callback()
function that can both get and set the callback to be called after include or its 3 other friends are called. Anyone (else?) disagree?
-Mike
Should a
get_declared_enums()
function be added ?
Yes
And should the
get_declared_classes()
function be adjusted to exclude
enums ?
and Yes.
I'd like to see an RFC with these two questions.
--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com
L.S.,
I just noticed the following, which struck me as weird/inconsistent:
There are four different OO structures in PHP:
- Classes
- Interfaces
- Traits
- Enums
For all four, an
*_exists()
function is available, i.e.
class_exists()
,interface_exists()
,trait_exists()
and
enum_exists()
[2].But only for three out of the four, a
get_declared_*()
function exists.
There isget_declared_classes()
,get_declared_interfaces()
,
get_declared_traits()
, but noget_declared_enums()
[2].I'm aware that enums are internally considered classes and that
get_declared_classes()
will retrieve them [1], but the same could be
said about interfaces and traits, yet they do have their own
get_declared_*()
function.Should a
get_declared_enums()
function be added ?
And should theget_declared_classes()
function be adjusted to
exclude enums ?I did check the enum RFC [3], but I couldn't find any mention or
discussion about this in the RFC.Smile,
Juliette1: https://3v4l.org/0ub6I
2: https://www.php.net/manual/en/ref.classobj.php
3: https://wiki.php.net/rfc/enumerations
Not responding to anyone in particular, but just letting you all know
that Ayesh and me will work up an RFC for this.
I will email the list when we've published it to open the official
discussion period.
Smile,
Juliette