From 32d7763125156b2d4c1d6c57caf3f50e5694f7b0 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 11:40:40 +0200 Subject: [PATCH 01/20] move function autoloading to its own branch --- drafts/function-autoloading.md | 173 ++++++++++++++++++++++++++++ published/function-autoloading.ptxt | 150 ++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 drafts/function-autoloading.md create mode 100644 published/function-autoloading.ptxt diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md new file mode 100644 index 0000000..3c6635b --- /dev/null +++ b/drafts/function-autoloading.md @@ -0,0 +1,173 @@ +# PHP RFC: Function Autoloading v4 + +* Version: 1.0 +* Date: 2024-08-15 +* Author: Robert Landers, landers.robert@gmail.com +* Status: Under Discussion (or Accepted or Declined) +* First Published at: + +## Introduction + +The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential +implementation which would be consistent with what we have for autoloading classes. + +## Proposal + +Before getting into the details, +there are a few terms worth acknowledging so that the proposal can be easily discussed without getting confused: + +1. **Defined function**: A function that the engine has knowledge of, such as in a previously included/required file. +2. **Undefined function**: A function that the engine does not have knowledge of. +3. **Function autoloading**: The process of loading a function that is not defined. +4. **Written function**: A function that exists in a file that the engine may or may not have knowledge of. +5. **Local scope**: The current namespace +6. **Global scope**: The global namespace (`\`) + +The suggested change would be pretty straightforward and backwards-compatible: + +1. Add two new constants to spl: SPL_AUTOLOAD_CLASS, SPL_AUTOLOAD_FUNCTION. +2. Add a fourth optional parameter for spl_autoload_register, with a default value of SPL_AUTOLOAD_CLASS. +3. The type for the missing token should also be passed to the $autoload_function callback as a second param. (e.g., + SPL_AUTOLOAD_CLASS for classes, SPL_AUTOLOAD_FUNCTION for functions) +4. Change the current class autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_CLASS types. +5. Add the function autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_FUNCTION types. + +There won’t be any changes to the current autoloading mechanism when it comes to classes. +However, if a function + +1. is called in a fully qualified form (e.g., a `use` statement or `\` prefix is used), +2. is not defined, +3. and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type + +then the autoloader will be called with the function name as the first parameter (with the initial slash removed) and +SPL_AUTOLOAD_FUNCTION as the second parameter. + +However, if a function + +1. is called in an unqualified form (e.g., `strlen()`), +2. is not defined locally +3. and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type + +then the autoloader will be called with the current namespace prepended to the function name. +If the autoloader chooses to look up the "basename" of the function, it may do so. +If the function is still undefined in the local scope, +then it will fall back to the global scope—unless the local scope is the global scope. +The function autoloader will not be called again. + +This provides an opportunity +for an autoloader to check for the existence of a function in the local scope and define it, +as well as defer to the global scope if it is not defined. + +Example "`PSR-4-style`" (except the last part of the namespace is the file it is in) function autoloader: + +```php + + * Yes + * No + + +## Patches and Tests + +Not yet. + +## Implementation + +After the project is implemented, this section should contain - the +version(s) it was merged into - a link to the git commit(s) - a link to +the PHP manual entry for the feature - a link to the language +specification section (if any) + +## References + +- [autofunc](https://wiki.php.net/rfc/autofunc): This heavily influenced this RFC. (declined in 2011) +- [function_autoloading](https://wiki.php.net/rfc/function_autoloading): This RFC was declined in 2011. +- [function_autoloading_v2](https://wiki.php.net/rfc/function_autoloading2): This RFC was declined in 2012. + +Thank you for all of those that contributed to the discussions back then. I hope that this RFC will be successful. + +## Rejected Features + +Keep this updated with features that were discussed on the mail lists. diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt new file mode 100644 index 0000000..f605938 --- /dev/null +++ b/published/function-autoloading.ptxt @@ -0,0 +1,150 @@ +====== PHP RFC: Function Autoloading v4 ====== + + * Version: 1.0 + * Date: 2024-08-15 + * Author: Robert Landers, + * Status: Under Discussion (or Accepted or Declined) + * First Published at: http://wiki.php.net/rfc/function_autoloading4 + +===== Introduction ===== + +The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. + +===== Proposal ===== + +Before getting into the details, there are a few terms worth acknowledging so that the proposal can be easily discussed without getting confused: + + - **Defined function**: A function that the engine has knowledge of, such as in a previously included/required file. + - **Undefined function**: A function that the engine does not have knowledge of. + - **Function autoloading**: The process of loading a function that is not defined. + - **Written function**: A function that exists in a file that the engine may or may not have knowledge of. + - **Local scope**: The current namespace + - **Global scope**: The global namespace (''%%\%%'') + +The suggested change would be pretty straightforward and backwards-compatible: + + - Add two new constants to spl: SPL_AUTOLOAD_CLASS, SPL_AUTOLOAD_FUNCTION. + - Add a fourth optional parameter for spl_autoload_register, with a default value of SPL_AUTOLOAD_CLASS. + - The type for the missing token should also be passed to the $autoload_function callback as a second param. (e.g., SPL_AUTOLOAD_CLASS for classes, SPL_AUTOLOAD_FUNCTION for functions) + - Change the current class autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_CLASS types. + - Add the function autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_FUNCTION types. + +There won’t be any changes to the current autoloading mechanism when it comes to classes. However, if a function + + - is called in a fully qualified form (e.g., a ''%%use%%'' statement or ''%%\%%'' prefix is used), + - is not defined, + - and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type + +then the autoloader will be called with the function name as the first parameter (with the initial slash removed) and SPL_AUTOLOAD_FUNCTION as the second parameter. + +However, if a function + + - is called in an unqualified form (e.g., ''%%strlen()%%''), + - is not defined locally + - and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type + +then the autoloader will be called with the current namespace prepended to the function name. If the autoloader chooses to look up the "basename" of the function, it may do so. If the function is still undefined in the local scope, then it will fall back to the global scope—unless the local scope is the global scope. The function autoloader will not be called again. + +This provides an opportunity for an autoloader to check for the existence of a function in the local scope and define it, as well as defer to the global scope if it is not defined. + +Example "''%%PSR-4-style%%''" (except the last part of the namespace is the file it is in) function autoloader: + + + + +Performance-wise, this should have minimal impact on existing codebases as there is no default function autoloader. + +For codebases that want to take advantage of function autoloading, it may be desirable to stick with FQNs for functions and/or employ caches and other techniques where possible. + +==== spl_autoload ==== + +''%%spl_autoload%%'''s second argument will be updated to accept ''%%int|string|null%%'' as the second parameter so that it can use the new callback signature. If the second parameter is an int, and it is not ''%%SPL_AUTOLOAD_CLASS%%'', an ''%%Error%%'' is thrown: 'Default autoloader can only load classes.' + +There will not be a default function autoloader. + +==== spl_autoload_call ==== + +The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one, (but not both) of the new constants, with the default value set to SPL_AUTOLOAD_CLASS. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. + + +spl_autoload_call('\Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader +spl_autoload_call('\Some\func'); // Calls the class autoloader +spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader +spl_autoload_call('Some\func'); // Calls the class autoloader +spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Error: Cannot autoload multiple types + + +If the user wants to call multiple autoloaders, they can do so manually. + +==== function_exists ==== + +The ''%%function_exists%%'' function will be updated to include a boolean option (''%%$autoload%%'') as the second parameter, which will default to ''%%true%%''. If set to ''%%true%%'', the function autoloader will be called if the function is not defined, otherwise, it will not be called. + +===== Backward Incompatible Changes ===== + +==== Mismatched arguments ==== + +If an autoloader was registered that can accept more than one argument, it may fail or perform unexpected behavior when it receives a second argument of ''%%SPL_AUTOLOAD_CLASS%%''. + +===== Proposed PHP Version(s) ===== + +8.5 or later. + +===== RFC Impact ===== + +==== To Opcache ==== + +To be determined. + +==== New Constants ==== + +Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AUTOLOAD_FUNCTION. + +===== Open Issues ===== + +To be determined. + +===== Future Scope ===== + +Potentially, constants and stream wrappers can be added in a similar fashion. + +===== Proposed Voting Choices ===== + + + + * Yes + * No + + + +===== Patches and Tests ===== + +Not yet. + +===== Implementation ===== + +After the project is implemented, this section should contain - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature - a link to the language specification section (if any) + +===== References ===== + + * [[https://wiki.php.net/rfc/autofunc|autofunc]]: This heavily influenced this RFC. (declined in 2011) + * [[https://wiki.php.net/rfc/function_autoloading|function_autoloading]]: This RFC was declined in 2011. + * [[https://wiki.php.net/rfc/function_autoloading2|function_autoloading_v2]]: This RFC was declined in 2012. + +Thank you for all of those that contributed to the discussions back then. I hope that this RFC will be successful. + +===== Rejected Features ===== + +Keep this updated with features that were discussed on the mail lists. From 3b92a1ea1e563e5dab93de5e90d744155e43f882 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 11:59:34 +0200 Subject: [PATCH 02/20] try disabling intentionally --- drafts/function-autoloading.md | 3 ++- published/function-autoloading.ptxt | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 3c6635b..a9c0789 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -143,11 +143,12 @@ To be determined. Potentially, constants and stream wrappers can be added in a similar fashion. ## Proposed Voting Choices - + * Yes * No + ## Patches and Tests diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index f605938..e470d66 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -123,10 +123,13 @@ Potentially, constants and stream wrappers can be added in a similar fashion. ===== Proposed Voting Choices ===== + + * Yes * No + ===== Patches and Tests ===== From e54c5d66a2030670cda103c1313214e950167e31 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 12:02:35 +0200 Subject: [PATCH 03/20] add implementation link --- drafts/function-autoloading.md | 2 +- published/function-autoloading.ptxt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index a9c0789..ff831ef 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -152,7 +152,7 @@ Potentially, constants and stream wrappers can be added in a similar fashion. ## Patches and Tests -Not yet. +Review the implementation [on GitHub #15471](https://github.com/php/php-src/pull/15471) ## Implementation diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index e470d66..461dbfc 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -134,7 +134,7 @@ Potentially, constants and stream wrappers can be added in a similar fashion. ===== Patches and Tests ===== -Not yet. +Review the implementation [[https://github.com/php/php-src/pull/15471|on GitHub #15471]] ===== Implementation ===== From 1a57a5ccaaed5cf85e51f3f2c8fbbee888cee370 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 12:05:16 +0200 Subject: [PATCH 04/20] start investigating opcache implementation details --- drafts/function-autoloading.md | 2 +- published/function-autoloading.ptxt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index ff831ef..98a98b5 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -128,7 +128,7 @@ it may fail or perform unexpected behavior when it receives a second argument of ### To Opcache -To be determined. +Update: Analyze potential Opcache changes or optimizations needed for function autoloading. ### New Constants diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 461dbfc..1cf6ebe 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -106,7 +106,7 @@ If an autoloader was registered that can accept more than one argument, it may f ==== To Opcache ==== -To be determined. +Update: Analyze potential Opcache changes or optimizations needed for function autoloading. ==== New Constants ==== From 6b671082d8c382d6cd21091e129c4f6a6d2fb72a Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 12:12:04 +0200 Subject: [PATCH 05/20] remove html comments --- published/function-autoloading.ptxt | 4 ++-- src/convert-from-md.sh | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 1cf6ebe..70ddf9e 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -123,13 +123,13 @@ Potentially, constants and stream wrappers can be added in a similar fashion. ===== Proposed Voting Choices ===== - + * Yes * No - + ===== Patches and Tests ===== diff --git a/src/convert-from-md.sh b/src/convert-from-md.sh index d98c6c5..9da9758 100755 --- a/src/convert-from-md.sh +++ b/src/convert-from-md.sh @@ -4,3 +4,6 @@ docker run -v "$(pwd)":/data --pull always --user "$(id -u)":"$(id -g)" pandoc/l # remove all and tags sed -i 's///g' "$2" sed -i 's/<\/HTML>//g' "$2" + +# remove all html comments +sed -i 's///g' "$2" From 478f50ee301293229f96a5fc94b9e6c786c48591 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 15:18:55 +0200 Subject: [PATCH 06/20] specify the original case is passed --- drafts/function-autoloading.md | 5 +++-- published/function-autoloading.ptxt | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 98a98b5..f0aa00a 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -39,7 +39,8 @@ However, if a function 2. is not defined, 3. and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type -then the autoloader will be called with the function name as the first parameter (with the initial slash removed) and +then the autoloader will be called with the function name, +with its original case as the first parameter (with the initial slash removed) and SPL_AUTOLOAD_FUNCTION as the second parameter. However, if a function @@ -48,7 +49,7 @@ However, if a function 2. is not defined locally 3. and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type -then the autoloader will be called with the current namespace prepended to the function name. +then the autoloader will be called, with its original case, with the current namespace prepended to the function name. If the autoloader chooses to look up the "basename" of the function, it may do so. If the function is still undefined in the local scope, then it will fall back to the global scope—unless the local scope is the global scope. diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 70ddf9e..72a6eb8 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -35,7 +35,7 @@ There won’t be any changes to the current autoloading mechanism when it comes - is not defined, - and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type -then the autoloader will be called with the function name as the first parameter (with the initial slash removed) and SPL_AUTOLOAD_FUNCTION as the second parameter. +then the autoloader will be called with the function name, with its original case as the first parameter (with the initial slash removed) and SPL_AUTOLOAD_FUNCTION as the second parameter. However, if a function @@ -43,7 +43,7 @@ However, if a function - is not defined locally - and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type -then the autoloader will be called with the current namespace prepended to the function name. If the autoloader chooses to look up the "basename" of the function, it may do so. If the function is still undefined in the local scope, then it will fall back to the global scope—unless the local scope is the global scope. The function autoloader will not be called again. +then the autoloader will be called, with its original case, with the current namespace prepended to the function name. If the autoloader chooses to look up the "basename" of the function, it may do so. If the function is still undefined in the local scope, then it will fall back to the global scope—unless the local scope is the global scope. The function autoloader will not be called again. This provides an opportunity for an autoloader to check for the existence of a function in the local scope and define it, as well as defer to the global scope if it is not defined. From 8045f86ad3a1624cbbfba2914bc856cfbbd3f348 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 22:02:24 +0200 Subject: [PATCH 07/20] update opcache impact --- drafts/function-autoloading.md | 20 ++++++++++---------- published/function-autoloading.ptxt | 17 ++++++++++------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index f0aa00a..7c6ff87 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -98,10 +98,9 @@ with the default value set to SPL_AUTOLOAD_CLASS. The name of the first parameter will be changed to `$name` to reflect that it can be a class or function name. ```php -spl_autoload_call('\Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader -spl_autoload_call('\Some\func'); // Calls the class autoloader -spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader +spl_autoload_call('Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader spl_autoload_call('Some\func'); // Calls the class autoloader +spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Error: Cannot autoload multiple types ``` @@ -129,7 +128,7 @@ it may fail or perform unexpected behavior when it receives a second argument of ### To Opcache -Update: Analyze potential Opcache changes or optimizations needed for function autoloading. +- Potential changes to JIT helpers to call the autoloader instead of reading from the function table directly. ### New Constants @@ -137,7 +136,7 @@ Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AU ## Open Issues -To be determined. +None at this time. ## Future Scope @@ -157,10 +156,9 @@ Review the implementation [on GitHub #15471](https://github.com/php/php-src/pull ## Implementation -After the project is implemented, this section should contain - the -version(s) it was merged into - a link to the git commit(s) - a link to -the PHP manual entry for the feature - a link to the language -specification section (if any) +- Implentation: [PR #15471](https://github.com/php/php-src/pull/15471) +- Version: TBD +- PHP Manual Entry: TODO ## References @@ -172,4 +170,6 @@ Thank you for all of those that contributed to the discussions back then. I hope ## Rejected Features -Keep this updated with features that were discussed on the mail lists. +### Autoloading constants + +Autoloading of other types such as constants and stream wrappers will come in a later RFC. diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 72a6eb8..922e8eb 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -79,10 +79,9 @@ There will not be a default function autoloader. The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one, (but not both) of the new constants, with the default value set to SPL_AUTOLOAD_CLASS. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. -spl_autoload_call('\Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader -spl_autoload_call('\Some\func'); // Calls the class autoloader -spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader +spl_autoload_call('Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader spl_autoload_call('Some\func'); // Calls the class autoloader +spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Error: Cannot autoload multiple types @@ -106,7 +105,7 @@ If an autoloader was registered that can accept more than one argument, it may f ==== To Opcache ==== -Update: Analyze potential Opcache changes or optimizations needed for function autoloading. + * Potential changes to JIT helpers to call the autoloader instead of reading from the function table directly. ==== New Constants ==== @@ -114,7 +113,7 @@ Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AU ===== Open Issues ===== -To be determined. +None at this time. ===== Future Scope ===== @@ -138,7 +137,9 @@ Review the implementation [[https://github.com/php/php-src/pull/15471|on GitHub ===== Implementation ===== -After the project is implemented, this section should contain - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature - a link to the language specification section (if any) + * Implentation: [[https://github.com/php/php-src/pull/15471|PR #15471]] + * Version: TBD + * PHP Manual Entry: TODO ===== References ===== @@ -150,4 +151,6 @@ Thank you for all of those that contributed to the discussions back then. I hope ===== Rejected Features ===== -Keep this updated with features that were discussed on the mail lists. +==== Autoloading constants ==== + +Autoloading of other types such as constants and stream wrappers will come in a later RFC. From 8b932032b116b7737149707837fa66e6ee6b0bb4 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 22:09:08 +0200 Subject: [PATCH 08/20] add spl_autoload_unregister --- drafts/function-autoloading.md | 6 ++++++ published/function-autoloading.ptxt | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 7c6ff87..30568b9 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -90,6 +90,11 @@ an `Error` is thrown: 'Default autoloader can only load classes.' There will not be a default function autoloader. +### spl_autoload_unregister + +`spl_autoload_unregister` will be updated to accept the new constants as the second parameter to unregister an +autoloader from either mode. + ### spl_autoload_call The `spl_autoload_call` function will be modified to accept a second parameter of one, @@ -143,6 +148,7 @@ None at this time. Potentially, constants and stream wrappers can be added in a similar fashion. ## Proposed Voting Choices + * Yes diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 922e8eb..0867cec 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -74,6 +74,10 @@ For codebases that want to take advantage of function autoloading, it may be des There will not be a default function autoloader. +==== spl_autoload_unregister ==== + +''%%spl_autoload_unregister%%'' will be updated to accept the new constants as the second parameter to unregister an autoloader from either mode. + ==== spl_autoload_call ==== The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one, (but not both) of the new constants, with the default value set to SPL_AUTOLOAD_CLASS. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. From 2165b7c9ce7e386130d032a583e3b84ef2f4f8e6 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 22:26:47 +0200 Subject: [PATCH 09/20] spl_autoload_functions --- drafts/function-autoloading.md | 5 +++++ published/function-autoloading.ptxt | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 30568b9..f895ba5 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -95,6 +95,11 @@ There will not be a default function autoloader. `spl_autoload_unregister` will be updated to accept the new constants as the second parameter to unregister an autoloader from either mode. +### spl_autoload_functions + +`spl_autoload_functions` will be updated to accept one of the new constants as the first parameter. Passing both (i.e., +`SPL_AUTOLOAD_CLASS | SPL_AUTOLOAD_FUNCTION`) will result in an error. + ### spl_autoload_call The `spl_autoload_call` function will be modified to accept a second parameter of one, diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 0867cec..311d4b1 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -78,6 +78,10 @@ There will not be a default function autoloader. ''%%spl_autoload_unregister%%'' will be updated to accept the new constants as the second parameter to unregister an autoloader from either mode. +==== spl_autoload_functions ==== + +''%%spl_autoload_functions%%'' will be updated to accept one of the new constants as the first parameter. Passing both (i.e., ''%%SPL_AUTOLOAD_CLASS | SPL_AUTOLOAD_FUNCTION%%'') will result in an error. + ==== spl_autoload_call ==== The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one, (but not both) of the new constants, with the default value set to SPL_AUTOLOAD_CLASS. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. From 8af3667765d5a08a964baa7dd638f914c49e419e Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 18 Aug 2024 22:56:55 +0200 Subject: [PATCH 10/20] add better introduction --- drafts/function-autoloading.md | 10 ++++++++++ published/function-autoloading.ptxt | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index f895ba5..fadd499 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -11,6 +11,16 @@ The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. +By adding function autoload support, we can make it easier for new developers to use the language. + +By using autoloaders (such as composer), +users can already get quickly up to speed when it comes to classes, +but the language currently lacks a way to do the same for functions. +This requires users to manually (and carefully) include files that must be included on every request. +For 'functional' codebases, +they lose the ability to use autoloaders, or they must write their functions as static methods on classes. +This isn’t ideal, and this RFC seeks to close the gap between functions and classes. + ## Proposal Before getting into the details, diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 311d4b1..c112508 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -10,6 +10,10 @@ The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. +By adding function autoload support, we can make it easier for new developers to use the language. + +By using autoloaders (such as composer), users can already get quickly up to speed when it comes to classes, but the language currently lacks a way to do the same for functions. This requires users to manually (and carefully) include files that must be included on every request. For 'functional' codebases, they lose the ability to use autoloaders, or they must write their functions as static methods on classes. This isn’t ideal, and this RFC seeks to close the gap between functions and classes. + ===== Proposal ===== Before getting into the details, there are a few terms worth acknowledging so that the proposal can be easily discussed without getting confused: From 453765eabe757751ccb475d5f16769c8c5a5e227 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Mon, 19 Aug 2024 20:42:02 +0200 Subject: [PATCH 11/20] use enums instead of constants --- drafts/function-autoloading.md | 4 +++- published/function-autoloading.ptxt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index fadd499..9cf8769 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -156,7 +156,9 @@ Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AU ## Open Issues -None at this time. +### Enums instead of constants + +Enums could be used instead of constants. ## Future Scope diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index c112508..b0c744d 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -125,7 +125,9 @@ Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AU ===== Open Issues ===== -None at this time. +==== Enums instead of constants ==== + +Enums could be used instead of constants. ===== Future Scope ===== From 4c6e47ed07db257ea34974b2baa23a1eaf741bee Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 3 Sep 2024 22:47:09 +0200 Subject: [PATCH 12/20] better wording --- drafts/function-autoloading.md | 99 ++++++++++------------------- published/function-autoloading.ptxt | 69 ++++++-------------- 2 files changed, 55 insertions(+), 113 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 9cf8769..584acbc 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -11,8 +11,6 @@ The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. -By adding function autoload support, we can make it easier for new developers to use the language. - By using autoloaders (such as composer), users can already get quickly up to speed when it comes to classes, but the language currently lacks a way to do the same for functions. @@ -23,51 +21,23 @@ This isn’t ideal, and this RFC seeks to close the gap between functions and cl ## Proposal -Before getting into the details, -there are a few terms worth acknowledging so that the proposal can be easily discussed without getting confused: - -1. **Defined function**: A function that the engine has knowledge of, such as in a previously included/required file. -2. **Undefined function**: A function that the engine does not have knowledge of. -3. **Function autoloading**: The process of loading a function that is not defined. -4. **Written function**: A function that exists in a file that the engine may or may not have knowledge of. -5. **Local scope**: The current namespace -6. **Global scope**: The global namespace (`\`) - -The suggested change would be pretty straightforward and backwards-compatible: - -1. Add two new constants to spl: SPL_AUTOLOAD_CLASS, SPL_AUTOLOAD_FUNCTION. -2. Add a fourth optional parameter for spl_autoload_register, with a default value of SPL_AUTOLOAD_CLASS. -3. The type for the missing token should also be passed to the $autoload_function callback as a second param. (e.g., - SPL_AUTOLOAD_CLASS for classes, SPL_AUTOLOAD_FUNCTION for functions) -4. Change the current class autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_CLASS types. -5. Add the function autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_FUNCTION types. +This RFC proposes to add two new constants to the SPL extension: `SPL_AUTOLOAD_CLASS`, `SPL_AUTOLOAD_FUNCTION`. +These constants may be passed to `spl_autoload_register` as the fourth parameter +to register an autoloader for classes or functions, respectively. There won’t be any changes to the current autoloading mechanism when it comes to classes. -However, if a function -1. is called in a fully qualified form (e.g., a `use` statement or `\` prefix is used), -2. is not defined, -3. and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type +### Function Autoloading -then the autoloader will be called with the function name, -with its original case as the first parameter (with the initial slash removed) and -SPL_AUTOLOAD_FUNCTION as the second parameter. +The function autoloader will be called with the fully qualified undefined function name. +This will allow the function autoloader to determine how to load or generate the function. -However, if a function - -1. is called in an unqualified form (e.g., `strlen()`), -2. is not defined locally -3. and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type - -then the autoloader will be called, with its original case, with the current namespace prepended to the function name. -If the autoloader chooses to look up the "basename" of the function, it may do so. -If the function is still undefined in the local scope, -then it will fall back to the global scope—unless the local scope is the global scope. -The function autoloader will not be called again. - -This provides an opportunity -for an autoloader to check for the existence of a function in the local scope and define it, -as well as defer to the global scope if it is not defined. +PHP allows programmers to call an unqualified function name. +Traditionally, this means that PHP would first search in the current namespace for the function +and then fall back to the global namespace if the function is not found. +This behavior will be preserved. +However, the function autoloader will be called **only once** for the current namespace; +thus, the function autoloader will not be called again if the function is found in the global namespace. Example "`PSR-4-style`" (except the last part of the namespace is the file it is in) function autoloader: @@ -86,19 +56,19 @@ spl_autoload_register(function ($function, $type) { }, false, false, SPL_AUTOLOAD_FUNCTION); ``` -Performance-wise, this should have minimal impact on existing codebases as there is no default function autoloader. +### Performance Impact -For codebases that want to take advantage of function autoloading, -it may be desirable to stick with FQNs for functions and/or employ caches and other techniques where possible. +Function autoloading doesn’t appear to have a significant impact on performance; however, the function autoloader itself +(depending upon its implementation) may have a performance impact. -### spl_autoload +To help mitigate any potential performance impact of function autoloading many unqualified functions, +a function will only be searched for once per namespace. -`spl_autoload`'s second argument will be updated to accept `int|string|null` as the second parameter so that it can use -the new callback signature. -If the second parameter is an int, and it is not `SPL_AUTOLOAD_CLASS`, -an `Error` is thrown: 'Default autoloader can only load classes.' +### spl_autoload -There will not be a default function autoloader. +The `spl_autoload` function will not be modified. +It may be used as a function autoloader if the programmer desires, +though it will limit the programmer to a single function per file. ### spl_autoload_unregister @@ -108,24 +78,24 @@ autoloader from either mode. ### spl_autoload_functions `spl_autoload_functions` will be updated to accept one of the new constants as the first parameter. Passing both (i.e., -`SPL_AUTOLOAD_CLASS | SPL_AUTOLOAD_FUNCTION`) will result in an error. +`SPL_AUTOLOAD_CLASS | SPL_AUTOLOAD_FUNCTION`) will result in all registered functions. ### spl_autoload_call -The `spl_autoload_call` function will be modified to accept a second parameter of one, -(but not both) of the new constants, -with the default value set to SPL_AUTOLOAD_CLASS. +The `spl_autoload_call` function will be modified to accept a second parameter of one or both of the constants, +with the default value set to `SPL_AUTOLOAD_CLASS`. The name of the first parameter will be changed to `$name` to reflect that it can be a class or function name. +In the event that both constants are passed, it will attempt to autoload both types. +This may be useful in the case where functions and invocable classes are used interchangeably. + ```php spl_autoload_call('Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader spl_autoload_call('Some\func'); // Calls the class autoloader spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader -spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Error: Cannot autoload multiple types +spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Calls both autoloaders with the name 'func' ``` -If the user wants to call multiple autoloaders, they can do so manually. - ### function_exists The `function_exists` function will be updated to include a boolean option (`$autoload`) as the second parameter, @@ -135,10 +105,7 @@ called. ## Backward Incompatible Changes -### Mismatched arguments - -If an autoloader was registered that can accept more than one argument, -it may fail or perform unexpected behavior when it receives a second argument of `SPL_AUTOLOAD_CLASS`. +There shouldn’t be any backward incompatible changes. ## Proposed PHP Version(s) @@ -156,9 +123,7 @@ Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AU ## Open Issues -### Enums instead of constants - -Enums could be used instead of constants. +None. ## Future Scope @@ -166,6 +131,10 @@ Potentially, constants and stream wrappers can be added in a similar fashion. ## Proposed Voting Choices +As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. + +Voting started on 2023-XX-XX and will end on 2023-XX-XX. + * Yes diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index b0c744d..920835e 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -10,46 +10,19 @@ The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. -By adding function autoload support, we can make it easier for new developers to use the language. - By using autoloaders (such as composer), users can already get quickly up to speed when it comes to classes, but the language currently lacks a way to do the same for functions. This requires users to manually (and carefully) include files that must be included on every request. For 'functional' codebases, they lose the ability to use autoloaders, or they must write their functions as static methods on classes. This isn’t ideal, and this RFC seeks to close the gap between functions and classes. ===== Proposal ===== -Before getting into the details, there are a few terms worth acknowledging so that the proposal can be easily discussed without getting confused: - - - **Defined function**: A function that the engine has knowledge of, such as in a previously included/required file. - - **Undefined function**: A function that the engine does not have knowledge of. - - **Function autoloading**: The process of loading a function that is not defined. - - **Written function**: A function that exists in a file that the engine may or may not have knowledge of. - - **Local scope**: The current namespace - - **Global scope**: The global namespace (''%%\%%'') - -The suggested change would be pretty straightforward and backwards-compatible: - - - Add two new constants to spl: SPL_AUTOLOAD_CLASS, SPL_AUTOLOAD_FUNCTION. - - Add a fourth optional parameter for spl_autoload_register, with a default value of SPL_AUTOLOAD_CLASS. - - The type for the missing token should also be passed to the $autoload_function callback as a second param. (e.g., SPL_AUTOLOAD_CLASS for classes, SPL_AUTOLOAD_FUNCTION for functions) - - Change the current class autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_CLASS types. - - Add the function autoloading to only call the autoloaders which match with the SPL_AUTOLOAD_FUNCTION types. +This RFC proposes to add two new constants to the SPL extension: ''%%SPL_AUTOLOAD_CLASS%%'', ''%%SPL_AUTOLOAD_FUNCTION%%''. These constants may be passed to ''%%spl_autoload_register%%'' as the fourth parameter to register an autoloader for classes or functions, respectively. -There won’t be any changes to the current autoloading mechanism when it comes to classes. However, if a function +There won’t be any changes to the current autoloading mechanism when it comes to classes. - - is called in a fully qualified form (e.g., a ''%%use%%'' statement or ''%%\%%'' prefix is used), - - is not defined, - - and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type +==== Function Autoloading ==== -then the autoloader will be called with the function name, with its original case as the first parameter (with the initial slash removed) and SPL_AUTOLOAD_FUNCTION as the second parameter. +The function autoloader will be called with the fully qualified undefined function name. This will allow the function autoloader to determine how to load or generate the function. -However, if a function - - - is called in an unqualified form (e.g., ''%%strlen()%%''), - - is not defined locally - - and an autoloader is registered with the SPL_AUTOLOAD_FUNCTION type - -then the autoloader will be called, with its original case, with the current namespace prepended to the function name. If the autoloader chooses to look up the "basename" of the function, it may do so. If the function is still undefined in the local scope, then it will fall back to the global scope—unless the local scope is the global scope. The function autoloader will not be called again. - -This provides an opportunity for an autoloader to check for the existence of a function in the local scope and define it, as well as defer to the global scope if it is not defined. +PHP allows programmers to call an unqualified function name. Traditionally, this means that PHP would first search in the current namespace for the function and then fall back to the global namespace if the function is not found. This behavior will be preserved. However, the function autoloader will be called **only once** for the current namespace; thus, the function autoloader will not be called again if the function is found in the global namespace. Example "''%%PSR-4-style%%''" (except the last part of the namespace is the file it is in) function autoloader: @@ -68,15 +41,15 @@ spl_autoload_register(function ($function, $type) { }, false, false, SPL_AUTOLOAD_FUNCTION); -Performance-wise, this should have minimal impact on existing codebases as there is no default function autoloader. +==== Performance Impact ==== -For codebases that want to take advantage of function autoloading, it may be desirable to stick with FQNs for functions and/or employ caches and other techniques where possible. +Function autoloading doesn’t appear to have a significant impact on performance; however, the function autoloader itself (depending upon its implementation) may have a performance impact. -==== spl_autoload ==== +To help mitigate any potential performance impact of function autoloading many unqualified functions, a function will only be searched for once per namespace. -''%%spl_autoload%%'''s second argument will be updated to accept ''%%int|string|null%%'' as the second parameter so that it can use the new callback signature. If the second parameter is an int, and it is not ''%%SPL_AUTOLOAD_CLASS%%'', an ''%%Error%%'' is thrown: 'Default autoloader can only load classes.' +==== spl_autoload ==== -There will not be a default function autoloader. +The ''%%spl_autoload%%'' function will not be modified. It may be used as a function autoloader if the programmer desires, though it will limit the programmer to a single function per file. ==== spl_autoload_unregister ==== @@ -84,30 +57,28 @@ There will not be a default function autoloader. ==== spl_autoload_functions ==== -''%%spl_autoload_functions%%'' will be updated to accept one of the new constants as the first parameter. Passing both (i.e., ''%%SPL_AUTOLOAD_CLASS | SPL_AUTOLOAD_FUNCTION%%'') will result in an error. +''%%spl_autoload_functions%%'' will be updated to accept one of the new constants as the first parameter. Passing both (i.e., ''%%SPL_AUTOLOAD_CLASS | SPL_AUTOLOAD_FUNCTION%%'') will result in all registered functions. ==== spl_autoload_call ==== -The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one, (but not both) of the new constants, with the default value set to SPL_AUTOLOAD_CLASS. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. +The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one or both of the constants, with the default value set to ''%%SPL_AUTOLOAD_CLASS%%''. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. + +In the event that both constants are passed, it will attempt to autoload both types. This may be useful in the case where functions and invocable classes are used interchangeably. spl_autoload_call('Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader spl_autoload_call('Some\func'); // Calls the class autoloader spl_autoload_call('Some\func', SPL_AUTOLOAD_CLASS); // Calls the class autoloader -spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Error: Cannot autoload multiple types +spl_autoload_call('func', SPL_AUTOLOAD_FUNCTION | SPL_AUTOLOAD_CLASS); // Calls both autoloaders with the name 'func' -If the user wants to call multiple autoloaders, they can do so manually. - ==== function_exists ==== The ''%%function_exists%%'' function will be updated to include a boolean option (''%%$autoload%%'') as the second parameter, which will default to ''%%true%%''. If set to ''%%true%%'', the function autoloader will be called if the function is not defined, otherwise, it will not be called. ===== Backward Incompatible Changes ===== -==== Mismatched arguments ==== - -If an autoloader was registered that can accept more than one argument, it may fail or perform unexpected behavior when it receives a second argument of ''%%SPL_AUTOLOAD_CLASS%%''. +There shouldn’t be any backward incompatible changes. ===== Proposed PHP Version(s) ===== @@ -125,9 +96,7 @@ Two new constants will be added to the SPL extension: SPL_AUTOLOAD_CLASS, SPL_AU ===== Open Issues ===== -==== Enums instead of constants ==== - -Enums could be used instead of constants. +None. ===== Future Scope ===== @@ -135,6 +104,10 @@ Potentially, constants and stream wrappers can be added in a similar fashion. ===== Proposed Voting Choices ===== +As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. + +Voting started on 2023-XX-XX and will end on 2023-XX-XX. + From 33ec8cb2a4467df954fa8d43194b55c90fa2df39 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 3 Sep 2024 23:05:29 +0200 Subject: [PATCH 13/20] specify default value --- drafts/function-autoloading.md | 7 ++++--- published/function-autoloading.ptxt | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 584acbc..813b63b 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -11,10 +11,10 @@ The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. -By using autoloaders (such as composer), -users can already get quickly up to speed when it comes to classes, +By using autoloaders, +programmers can already get quickly up to speed when it comes to classes, but the language currently lacks a way to do the same for functions. -This requires users to manually (and carefully) include files that must be included on every request. +This requires programmers to manually (and carefully) include files that must be included on every request. For 'functional' codebases, they lose the ability to use autoloaders, or they must write their functions as static methods on classes. This isn’t ideal, and this RFC seeks to close the gap between functions and classes. @@ -24,6 +24,7 @@ This isn’t ideal, and this RFC seeks to close the gap between functions and cl This RFC proposes to add two new constants to the SPL extension: `SPL_AUTOLOAD_CLASS`, `SPL_AUTOLOAD_FUNCTION`. These constants may be passed to `spl_autoload_register` as the fourth parameter to register an autoloader for classes or functions, respectively. +If not specified, the default value `SPL_AUTOLOAD_CLASS` will be used to retain backward compatibility. There won’t be any changes to the current autoloading mechanism when it comes to classes. diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index 920835e..ef305a8 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -10,11 +10,11 @@ The topic of supporting function autoloading was brought up many times in the past, this RFC introduces a potential implementation which would be consistent with what we have for autoloading classes. -By using autoloaders (such as composer), users can already get quickly up to speed when it comes to classes, but the language currently lacks a way to do the same for functions. This requires users to manually (and carefully) include files that must be included on every request. For 'functional' codebases, they lose the ability to use autoloaders, or they must write their functions as static methods on classes. This isn’t ideal, and this RFC seeks to close the gap between functions and classes. +By using autoloaders, programmers can already get quickly up to speed when it comes to classes, but the language currently lacks a way to do the same for functions. This requires programmers to manually (and carefully) include files that must be included on every request. For 'functional' codebases, they lose the ability to use autoloaders, or they must write their functions as static methods on classes. This isn’t ideal, and this RFC seeks to close the gap between functions and classes. ===== Proposal ===== -This RFC proposes to add two new constants to the SPL extension: ''%%SPL_AUTOLOAD_CLASS%%'', ''%%SPL_AUTOLOAD_FUNCTION%%''. These constants may be passed to ''%%spl_autoload_register%%'' as the fourth parameter to register an autoloader for classes or functions, respectively. +This RFC proposes to add two new constants to the SPL extension: ''%%SPL_AUTOLOAD_CLASS%%'', ''%%SPL_AUTOLOAD_FUNCTION%%''. These constants may be passed to ''%%spl_autoload_register%%'' as the fourth parameter to register an autoloader for classes or functions, respectively. If not specified, the default value ''%%SPL_AUTOLOAD_CLASS%%'' will be used to retain backward compatibility. There won’t be any changes to the current autoloading mechanism when it comes to classes. From 36dfb64f1be5850dc25df433ff3bdb65285f1cb8 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 3 Sep 2024 23:33:32 +0200 Subject: [PATCH 14/20] remove weird line --- drafts/function-autoloading.md | 1 - published/function-autoloading.ptxt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 813b63b..79f3fa4 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -88,7 +88,6 @@ with the default value set to `SPL_AUTOLOAD_CLASS`. The name of the first parameter will be changed to `$name` to reflect that it can be a class or function name. In the event that both constants are passed, it will attempt to autoload both types. -This may be useful in the case where functions and invocable classes are used interchangeably. ```php spl_autoload_call('Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader diff --git a/published/function-autoloading.ptxt b/published/function-autoloading.ptxt index ef305a8..f95e558 100644 --- a/published/function-autoloading.ptxt +++ b/published/function-autoloading.ptxt @@ -63,7 +63,7 @@ The ''%%spl_autoload%%'' function will not be modified. It may be used as a func The ''%%spl_autoload_call%%'' function will be modified to accept a second parameter of one or both of the constants, with the default value set to ''%%SPL_AUTOLOAD_CLASS%%''. The name of the first parameter will be changed to ''%%$name%%'' to reflect that it can be a class or function name. -In the event that both constants are passed, it will attempt to autoload both types. This may be useful in the case where functions and invocable classes are used interchangeably. +In the event that both constants are passed, it will attempt to autoload both types. spl_autoload_call('Some\func', SPL_AUTOLOAD_FUNCTION); // Calls the function autoloader From 94e242e5795fe6dd76384ccb345637b620ab93d2 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Wed, 4 Sep 2024 21:05:58 +0200 Subject: [PATCH 15/20] fix example --- drafts/function-autoloading.md | 14 ++++++-------- published/function-autoloading.ptxt | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drafts/function-autoloading.md b/drafts/function-autoloading.md index 79f3fa4..0ed6a72 100644 --- a/drafts/function-autoloading.md +++ b/drafts/function-autoloading.md @@ -45,14 +45,12 @@ Example "`PSR-4-style`" (except the last part of the namespace is the file it is ```php From 09e77946a070d2c6831c47f9062bf0484a055bea Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Wed, 4 Sep 2024 21:06:31 +0200 Subject: [PATCH 16/20] remove docker container --- src/convert-from-md.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convert-from-md.sh b/src/convert-from-md.sh index 9da9758..84222f2 100755 --- a/src/convert-from-md.sh +++ b/src/convert-from-md.sh @@ -1,6 +1,6 @@ #!/bin/sh -docker run -v "$(pwd)":/data --pull always --user "$(id -u)":"$(id -g)" pandoc/latex -t dokuwiki -f gfm "$1" -o "$2" +docker run --rm -v "$(pwd)":/data --user "$(id -u)":"$(id -g)" pandoc/latex -t dokuwiki -f gfm "$1" -o "$2" # remove all and tags sed -i 's///g' "$2" sed -i 's/<\/HTML>//g' "$2" From eea5fcc721269fbae8b88e60341879e4b37c1ddd Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 6 Sep 2024 19:24:33 +0200 Subject: [PATCH 17/20] add type aliases --- drafts/typed-aliases.md | 323 +++++++++++++++++++++++++++++++++++ published/typed-aliases.ptxt | 287 +++++++++++++++++++++++++++++++ 2 files changed, 610 insertions(+) create mode 100644 drafts/typed-aliases.md create mode 100644 published/typed-aliases.ptxt diff --git a/drafts/typed-aliases.md b/drafts/typed-aliases.md new file mode 100644 index 0000000..d030fc0 --- /dev/null +++ b/drafts/typed-aliases.md @@ -0,0 +1,323 @@ +# PHP RFC: Typed Aliases + +* Version: 1.0 +* Date: 2024-09-06 +* Author: Robert Landers, landers.robert@gmail.com +* Status: Under Discussion (or Accepted or Declined) +* First Published at: + +## Introduction + +There are many times where you may need to write out a union/intersection type in PHP. This can be cumbersome and +error-prone. +This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, +but may be used in other files as well. + +Here is a brief example: + +```php + +namespace MyLibrary; + +class Second {} +class Minute {} +class Hour {} + +use Time as alias Second|Minute|Hour; + +// alternatively + +\type_alias('Time', 'Second|Minute|Hour'); + +// in another file + +use MyLibrary\Time; +``` + +## Proposal + +This RFC proposes to implement aliases as a "special" +class type (similar to final, abstract, etc.) under the hood of the same name as its alias. +Thus, the alias `Time` would be a class named `Time` in the namespace `MyLibrary`, in the example above. +This prevents collisions with other classes and defined "types" in the same namespace. + +When the engine sees a class with this special type, it expands the alias into its actual type, +and continues with type checking. + +### Aliases Classes + +There are several classes of aliases: + +1. **primitive** - An alias for a single primitive data type (int, float, string, bool, array, etc.) +2. **simple** - An alias for a single class or interface +3. **complex** - An alias for a union or intersection of other types + +### Creating Aliases + +There are two ways to create an alias and neither trigger autoloading of their inner types until the types are used. +This is to allow "type libraries" or "type files" +to be self-sufficient from their implementation and be defined without loading entire libraries. + +#### Using the use statement + +A use statement on the top-level of the file will create an alias for the file +and may be reused throughout the project once defined. + +It is created by using the `use` keyword, +followed by the desired alias name, `as alias`, and then the union/intersection or single type. + +```php +namespace MyLibrary; + +use Time as alias Second|Minute|Hour; +use Number as alias int|float; +use Stringy as alias string|Stringable; +use FancyString as alias string; +``` + +#### Using the type_alias function + +A new function called `type_alias` will be introduced in the global scope. This function will take two arguments: + +1. The alias name as a string +2. The union/intersection or single type as a string. + +The use case for this function is for dynamically creating aliases during runtime, similar to `class_alias`. + +```php +type_alias('\MyLibrary\Time', 'Second|Minute|Hour'); +type_alias('\MyLibrary\Number', 'int|float'); +type_alias('\MyLibrary\Stringy', 'string|Stringable'); +type_alias('\MyLibrary\FancyString', 'string'); +``` + +### Using Aliases + +Since an alias is essentially a class, under the hood, it can be used in the same way as a class. +This allows you to define an alias in one part of a project and use it in another: + +```php +namespace MyProject; + +use MyLibrary\Time; +use MyLibrary\Number; +use MyLibrary\Stringy; +use MyLibrary\FancyString; + +function sleepFor(Time $time) {} + +function retryTimes(Number $times) {} + +function logMessage(Stringy|FancyString|string $message) {} // Not a fatal error +``` + +#### Intersections and Unions + +A type alias may be a union or intersection of other types (including other aliases), +event if they contain the same types in their alias. +It will not be a fatal error as it currently is when a type is a union or intersection with itself. +This allows libraries to declare type aliases that are specific to their own library +and be reused in other projects that may also have similar aliases. +For example, +a library may define a `Stringy` alias that is a union of `string` +and `Stringable` and another library may define a `ConstantString` alias +that is a union of `Stringable` and `MyString`. +A project using both libraries would be able +to use `Stringy` and `ConstantString` in its own type alias or function type. + +#### Nesting + +Aliases may also be aliases of other aliases: + +```php +namespace MyLibrary; + +use Time as alias Second|Minute|Hour; +use Duration as alias Time; +``` + +#### Argument Lists and Return Types + +The primary usage for aliases is in argument lists and return types: + +```php +use MyLibrary\Time; + +function sleep(Time $time): Time {} + +class Alarm { + public function __construct(Time $time) {} + + public function getTime(): Time {} +} +``` + +#### Extending and Implementing + +For simple aliases of other classes, `type_alias` behaves exactly like `class_alias` and `autoload` set to `false`. +Thus, these types of aliases can be used in class extension and implementation: + +```php +class A {} + +type_alias('B', 'A'); + +class C extends B {} +``` + +However, trying to extend or implement a complex or primitive alias will result in the expected fatal error: + +```php +class A {} +class B {} + +type_alias('C', 'A|B'); + +class D extends C {} // Fatal error: cannot extend a complex type alias +``` + +#### Calling new on Aliases + +Aliases may be used in the `new` keyword, but only if the alias is a simple alias of a class: + +```php +class A {} + +type_alias('B', 'A'); + +new B(); +``` + +#### Static calls on Aliases + +Aliases may be used in static calls, but only if the alias is a simple alias of a class: + +```php +class A { + public static function test() {} +} + +type_alias('B', 'A'); + +B::test(); +``` + +## Reflection + +It will be possible to use reflection to determine the type of alias. +When using `ReflectionClass` on an alias, it will see an object with one of the following base classes: + +- `PrimitiveTypeAlias` +- `ComplexTypeAlias` + +These classes will have the following structure: + +```php + +enum PrimitiveType { + case int; + case float; + case string; + case bool; + case array; + case object; + case callable; + case iterable; + case void; + case null; +} + +abstract class PrimitiveTypeAlias { + public const PrimitiveType $aliasOf; +} + +abstract class ComplexTypeAlias { + public const ReflectionUnionType|ReflectionIntersectionType $aliasOf; +} +``` + +For simple aliases, using ReflectionClass will return the original class name, just like with `class_alias`. + +Developers may access the `aliasOf` property to ascertain the alias’s underlying type. + +## Backward Incompatible Changes + +A project using `\type_alias` as a global function will need to update their code. + +## Proposed PHP Version(s) + +List the proposed PHP versions that the feature will be included in. Use +relative versions such as "next PHP 8.x" or "next PHP 8.x.y". + +## RFC Impact + +### To SAPIs + +N/A + +### To Existing Extensions + +N/A + +### To Opcache + +TBD + +### New Constants + +Describe any new constants so they can be accurately and comprehensively +explained in the PHP documentation. + +### php.ini Defaults + +If there are any php.ini settings then list: \* hardcoded default values +\* php.ini-development values \* php.ini-production values + +## Open Issues + +Make sure there are no open issues when the vote starts! + +## Unaffected PHP Functionality + +List existing areas/features of PHP that will not be changed by the RFC. + +This helps avoid any ambiguity, shows that you have thought deeply about +the RFC's impact, and helps reduces mail list noise. + +## Future Scope + +This section details areas where the feature might be improved in +future, but that are not currently proposed in this RFC. + +## Proposed Voting Choices + +Include these so readers know where you are heading and can discuss the +proposed voting options. + +## Patches and Tests + +Links to any external patches and tests go here. + +If there is no patch, make it clear who will create a patch, or whether +a volunteer to help with implementation is needed. + +Make it clear if the patch is intended to be the final patch, or is just +a prototype. + +For changes affecting the core language, you should also provide a patch +for the language specification. + +## Implementation + +After the project is implemented, this section should contain - the +version(s) it was merged into - a link to the git commit(s) - a link to +the PHP manual entry for the feature - a link to the language +specification section (if any) + +## References + +Links to external references, discussions or RFCs + +## Rejected Features + +Keep this updated with features that were discussed on the mail lists. diff --git a/published/typed-aliases.ptxt b/published/typed-aliases.ptxt new file mode 100644 index 0000000..0890687 --- /dev/null +++ b/published/typed-aliases.ptxt @@ -0,0 +1,287 @@ +====== PHP RFC: Typed Aliases ====== + + * Version: 1.0 + * Date: 2024-09-06 + * Author: Robert Landers, + * Status: Under Discussion (or Accepted or Declined) + * First Published at: http://wiki.php.net/rfc/typed-aliases + +===== Introduction ===== + +There are many times where you may need to write out a union/intersection type in PHP. This can be cumbersome and error-prone. This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, but may be used in other files as well. + +Here is a brief example: + + + +namespace MyLibrary; + +class Second {} +class Minute {} +class Hour {} + +use Time as alias Second|Minute|Hour; + +// alternatively + +\type_alias('Time', 'Second|Minute|Hour'); + +// in another file + +use MyLibrary\Time; + + +===== Proposal ===== + +This RFC proposes to implement aliases as a "special" class type (similar to final, abstract, etc.) under the hood of the same name as its alias. Thus, the alias ''%%Time%%'' would be a class named ''%%Time%%'' in the namespace ''%%MyLibrary%%'', in the example above. This prevents collisions with other classes and defined "types" in the same namespace. + +When the engine sees a class with this special type, it expands the alias into its actual type, and continues with type checking. + +==== Aliases Classes ==== + +There are several classes of aliases: + + - **primitive** - An alias for a single primitive data type (int, float, string, bool, array, etc.) + - **simple** - An alias for a single class or interface + - **complex** - An alias for a union or intersection of other types + +==== Creating Aliases ==== + +There are two ways to create an alias and neither trigger autoloading of their inner types until the types are used. This is to allow "type libraries" or "type files" to be self-sufficient from their implementation and be defined without loading entire libraries. + +=== Using the use statement === + +A use statement on the top-level of the file will create an alias for the file and may be reused throughout the project once defined. + +It is created by using the ''%%use%%'' keyword, followed by the desired alias name, ''%%as alias%%'', and then the union/intersection or single type. + + +namespace MyLibrary; + +use Time as alias Second|Minute|Hour; +use Number as alias int|float; +use Stringy as alias string|Stringable; +use FancyString as alias string; + + +=== Using the type_alias function === + +A new function called ''%%type_alias%%'' will be introduced in the global scope. This function will take two arguments: + + - The alias name as a string + - The union/intersection or single type as a string. + +The use case for this function is for dynamically creating aliases during runtime, similar to ''%%class_alias%%''. + + +type_alias('\MyLibrary\Time', 'Second|Minute|Hour'); +type_alias('\MyLibrary\Number', 'int|float'); +type_alias('\MyLibrary\Stringy', 'string|Stringable'); +type_alias('\MyLibrary\FancyString', 'string'); + + +==== Using Aliases ==== + +Since an alias is essentially a class, under the hood, it can be used in the same way as a class. This allows you to define an alias in one part of a project and use it in another: + + +namespace MyProject; + +use MyLibrary\Time; +use MyLibrary\Number; +use MyLibrary\Stringy; +use MyLibrary\FancyString; + +function sleepFor(Time $time) {} + +function retryTimes(Number $times) {} + +function logMessage(Stringy|FancyString|string $message) {} // Not a fatal error + + +=== Intersections and Unions === + +A type alias may be a union or intersection of other types (including other aliases), event if they contain the same types in their alias. It will not be a fatal error as it currently is when a type is a union or intersection with itself. This allows libraries to declare type aliases that are specific to their own library and be reused in other projects that may also have similar aliases. For example, a library may define a ''%%Stringy%%'' alias that is a union of ''%%string%%'' and ''%%Stringable%%'' and another library may define a ''%%ConstantString%%'' alias that is a union of ''%%Stringable%%'' and ''%%MyString%%''. A project using both libraries would be able to use ''%%Stringy%%'' and ''%%ConstantString%%'' in its own type alias or function type. + +=== Nesting === + +Aliases may also be aliases of other aliases: + + +namespace MyLibrary; + +use Time as alias Second|Minute|Hour; +use Duration as alias Time; + + +=== Argument Lists and Return Types === + +The primary usage for aliases is in argument lists and return types: + + +use MyLibrary\Time; + +function sleep(Time $time): Time {} + +class Alarm { + public function __construct(Time $time) {} + + public function getTime(): Time {} +} + + +=== Extending and Implementing === + +For simple aliases of other classes, ''%%type_alias%%'' behaves exactly like ''%%class_alias%%'' and ''%%autoload%%'' set to ''%%false%%''. Thus, these types of aliases can be used in class extension and implementation: + + +class A {} + +type_alias('B', 'A'); + +class C extends B {} + + +However, trying to extend or implement a complex or primitive alias will result in the expected fatal error: + + +class A {} +class B {} + +type_alias('C', 'A|B'); + +class D extends C {} // Fatal error: cannot extend a complex type alias + + +=== Calling new on Aliases === + +Aliases may be used in the ''%%new%%'' keyword, but only if the alias is a simple alias of a class: + + +class A {} + +type_alias('B', 'A'); + +new B(); + + +=== Static calls on Aliases === + +Aliases may be used in static calls, but only if the alias is a simple alias of a class: + + +class A { + public static function test() {} +} + +type_alias('B', 'A'); + +B::test(); + + +===== Reflection ===== + +It will be possible to use reflection to determine the type of alias. When using ''%%ReflectionClass%%'' on an alias, it will see an object with one of the following base classes: + + * ''%%PrimitiveTypeAlias%%'' + * ''%%ComplexTypeAlias%%'' + +These classes will have the following structure: + + + +enum PrimitiveType { + case int; + case float; + case string; + case bool; + case array; + case object; + case callable; + case iterable; + case void; + case null; +} + +abstract class PrimitiveTypeAlias { + public const PrimitiveType $aliasOf; +} + +abstract class ComplexTypeAlias { + public const ReflectionUnionType|ReflectionIntersectionType $aliasOf; +} + + +For simple aliases, using ReflectionClass will return the original class name, just like with ''%%class_alias%%''. + +Developers may access the ''%%aliasOf%%'' property to ascertain the alias’s underlying type. + +===== Backward Incompatible Changes ===== + +A project using ''%%\type_alias%%'' as a global function will need to update their code. + +===== Proposed PHP Version(s) ===== + +List the proposed PHP versions that the feature will be included in. Use relative versions such as "next PHP 8.x" or "next PHP 8.x.y". + +===== RFC Impact ===== + +==== To SAPIs ==== + +N/A + +==== To Existing Extensions ==== + +N/A + +==== To Opcache ==== + +TBD + +==== New Constants ==== + +Describe any new constants so they can be accurately and comprehensively explained in the PHP documentation. + +==== php.ini Defaults ==== + +If there are any php.ini settings then list: * hardcoded default values * php.ini-development values * php.ini-production values + +===== Open Issues ===== + +Make sure there are no open issues when the vote starts! + +===== Unaffected PHP Functionality ===== + +List existing areas/features of PHP that will not be changed by the RFC. + +This helps avoid any ambiguity, shows that you have thought deeply about the RFC's impact, and helps reduces mail list noise. + +===== Future Scope ===== + +This section details areas where the feature might be improved in future, but that are not currently proposed in this RFC. + +===== Proposed Voting Choices ===== + +Include these so readers know where you are heading and can discuss the proposed voting options. + +===== Patches and Tests ===== + +Links to any external patches and tests go here. + +If there is no patch, make it clear who will create a patch, or whether a volunteer to help with implementation is needed. + +Make it clear if the patch is intended to be the final patch, or is just a prototype. + +For changes affecting the core language, you should also provide a patch for the language specification. + +===== Implementation ===== + +After the project is implemented, this section should contain - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature - a link to the language specification section (if any) + +===== References ===== + +Links to external references, discussions or RFCs + +===== Rejected Features ===== + +Keep this updated with features that were discussed on the mail lists. From 7bdb52e9d669d253cd33c89dd3fc1a880b4ddd33 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 6 Sep 2024 20:06:52 +0200 Subject: [PATCH 18/20] swap aliases --- drafts/typed-aliases.md | 36 ++++++++++++++++++++++++++---------- published/typed-aliases.ptxt | 35 +++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/drafts/typed-aliases.md b/drafts/typed-aliases.md index d030fc0..ebfd374 100644 --- a/drafts/typed-aliases.md +++ b/drafts/typed-aliases.md @@ -8,7 +8,7 @@ ## Introduction -There are many times where you may need to write out a union/intersection type in PHP. This can be cumbersome and +There are many times where you may need to write out a union/intersection type many times in PHP. This can be cumbersome and error-prone. This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, but may be used in other files as well. @@ -23,7 +23,7 @@ class Second {} class Minute {} class Hour {} -use Time as alias Second|Minute|Hour; +use Second|Minute|Hour as alias Time; // alternatively @@ -54,7 +54,7 @@ There are several classes of aliases: ### Creating Aliases -There are two ways to create an alias and neither trigger autoloading of their inner types until the types are used. +There are three ways to create an alias and neither trigger autoloading of their inner types until the types are used. This is to allow "type libraries" or "type files" to be self-sufficient from their implementation and be defined without loading entire libraries. @@ -64,15 +64,15 @@ A use statement on the top-level of the file will create an alias for the file and may be reused throughout the project once defined. It is created by using the `use` keyword, -followed by the desired alias name, `as alias`, and then the union/intersection or single type. +followed by the type to alias, `as alias`, and then desired alias name. ```php namespace MyLibrary; -use Time as alias Second|Minute|Hour; -use Number as alias int|float; -use Stringy as alias string|Stringable; -use FancyString as alias string; +use Second|Minute|Hour as alias Time; +use int|float as alias Number; +use string|Stringable as alias Stringy; +use string as alias FancyString; ``` #### Using the type_alias function @@ -91,6 +91,22 @@ type_alias('\MyLibrary\Stringy', 'string|Stringable'); type_alias('\MyLibrary\FancyString', 'string'); ``` +#### File-local aliases + +The use statement will be extended to allow using aliases in the current file, +just as you can today, only with intersections, unions, and aliases. + +```php +namespace MyLibrary; + +use Second|Minute|Hour as Time; +use int|float as Number; +use string|Stringable as Stringy; +use string as FancyString; +``` + +Local aliases lack the "as alias" syntax and only use the "as" keyword. + ### Using Aliases Since an alias is essentially a class, under the hood, it can be used in the same way as a class. @@ -132,8 +148,8 @@ Aliases may also be aliases of other aliases: ```php namespace MyLibrary; -use Time as alias Second|Minute|Hour; -use Duration as alias Time; +use Second|Minute|Hour as alias Time; +use Time as alias Duration; ``` #### Argument Lists and Return Types diff --git a/published/typed-aliases.ptxt b/published/typed-aliases.ptxt index 0890687..be7eadf 100644 --- a/published/typed-aliases.ptxt +++ b/published/typed-aliases.ptxt @@ -8,7 +8,7 @@ ===== Introduction ===== -There are many times where you may need to write out a union/intersection type in PHP. This can be cumbersome and error-prone. This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, but may be used in other files as well. +There are many times where you may need to write out a union/intersection type many times in PHP. This can be cumbersome and error-prone. This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, but may be used in other files as well. Here is a brief example: @@ -20,7 +20,7 @@ class Second {} class Minute {} class Hour {} -use Time as alias Second|Minute|Hour; +use Second|Minute|Hour as alias Time; // alternatively @@ -47,21 +47,21 @@ There are several classes of aliases: ==== Creating Aliases ==== -There are two ways to create an alias and neither trigger autoloading of their inner types until the types are used. This is to allow "type libraries" or "type files" to be self-sufficient from their implementation and be defined without loading entire libraries. +There are three ways to create an alias and neither trigger autoloading of their inner types until the types are used. This is to allow "type libraries" or "type files" to be self-sufficient from their implementation and be defined without loading entire libraries. === Using the use statement === A use statement on the top-level of the file will create an alias for the file and may be reused throughout the project once defined. -It is created by using the ''%%use%%'' keyword, followed by the desired alias name, ''%%as alias%%'', and then the union/intersection or single type. +It is created by using the ''%%use%%'' keyword, followed by the type to alias, ''%%as alias%%'', and then desired alias name. namespace MyLibrary; -use Time as alias Second|Minute|Hour; -use Number as alias int|float; -use Stringy as alias string|Stringable; -use FancyString as alias string; +use Second|Minute|Hour as alias Time; +use int|float as alias Number; +use string|Stringable as alias Stringy; +use string as alias FancyString; === Using the type_alias function === @@ -80,6 +80,21 @@ type_alias('\MyLibrary\Stringy', 'string|Stringable'); type_alias('\MyLibrary\FancyString', 'string'); +=== File-local aliases === + +The use statement will be extended to allow using aliases in the current file, just as you can today, only with intersections, unions, and aliases. + + +namespace MyLibrary; + +use Second|Minute|Hour as Time; +use int|float as Number; +use string|Stringable as Stringy; +use string as FancyString; + + +Local aliases lack the "as alias" syntax and only use the "as" keyword. + ==== Using Aliases ==== Since an alias is essentially a class, under the hood, it can be used in the same way as a class. This allows you to define an alias in one part of a project and use it in another: @@ -110,8 +125,8 @@ Aliases may also be aliases of other aliases: namespace MyLibrary; -use Time as alias Second|Minute|Hour; -use Duration as alias Time; +use Second|Minute|Hour as alias Time; +use Time as alias Duration; === Argument Lists and Return Types === From b82b883f4733fb8546025b98c28c5d79fe7f6804 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 6 Sep 2024 20:36:23 +0200 Subject: [PATCH 19/20] add special classes --- drafts/typed-aliases.md | 8 ++++++++ published/typed-aliases.ptxt | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/drafts/typed-aliases.md b/drafts/typed-aliases.md index ebfd374..e4c937d 100644 --- a/drafts/typed-aliases.md +++ b/drafts/typed-aliases.md @@ -256,6 +256,14 @@ For simple aliases, using ReflectionClass will return the original class name, j Developers may access the `aliasOf` property to ascertain the alias’s underlying type. +## Why Special Classes? + +After looking at the current type system in PHP, +it became clear that if we were to implement aliases in the existing type system, +it would be overly complex and challenging to maintain. +Using classes, however, is much simpler, easier to maintain, and debug. +It is also easier to reason about in the symbol tables as well since classes are synonymous with types in PHP. + ## Backward Incompatible Changes A project using `\type_alias` as a global function will need to update their code. diff --git a/published/typed-aliases.ptxt b/published/typed-aliases.ptxt index be7eadf..74d126d 100644 --- a/published/typed-aliases.ptxt +++ b/published/typed-aliases.ptxt @@ -231,6 +231,10 @@ For simple aliases, using ReflectionClass will return the original class name, j Developers may access the ''%%aliasOf%%'' property to ascertain the alias’s underlying type. +===== Why Special Classes? ===== + +After looking at the current type system in PHP, it became clear that if we were to implement aliases in the existing type system, it would be overly complex and challenging to maintain. Using classes, however, is much simpler, easier to maintain, and debug. It is also easier to reason about in the symbol tables as well since classes are synonymous with types in PHP. + ===== Backward Incompatible Changes ===== A project using ''%%\type_alias%%'' as a global function will need to update their code. From b229a0f85795b451bb3a58f0adf970679d63507b Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 20 Sep 2024 19:32:12 +0200 Subject: [PATCH 20/20] update typed aliases --- drafts/typed-aliases.md | 123 ++++++++++++----------------------- published/typed-aliases.ptxt | 108 +++++++++++------------------- 2 files changed, 81 insertions(+), 150 deletions(-) diff --git a/drafts/typed-aliases.md b/drafts/typed-aliases.md index e4c937d..d44e64c 100644 --- a/drafts/typed-aliases.md +++ b/drafts/typed-aliases.md @@ -8,10 +8,10 @@ ## Introduction -There are many times where you may need to write out a union/intersection type many times in PHP. This can be cumbersome and +There are many times when you may need to write out a union/intersection type many times in PHP. This can be cumbersome and error-prone. -This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, -but may be used in other files as well. +This RFC proposes a new "typed alias" +syntax that will allow for the creation of type aliases that may be used project-wide. Here is a brief example: @@ -23,11 +23,7 @@ class Second {} class Minute {} class Hour {} -use Second|Minute|Hour as alias Time; - -// alternatively - -\type_alias('Time', 'Second|Minute|Hour'); +alias Time: Second|Minute|Hour; // in another file @@ -36,9 +32,9 @@ use MyLibrary\Time; ## Proposal -This RFC proposes to implement aliases as a "special" +This RFC proposes to implement type aliases as a "special" class type (similar to final, abstract, etc.) under the hood of the same name as its alias. -Thus, the alias `Time` would be a class named `Time` in the namespace `MyLibrary`, in the example above. +Thus, the alias `Time`, in the above example, would be a class named `Time` in the namespace `MyLibrary`. This prevents collisions with other classes and defined "types" in the same namespace. When the engine sees a class with this special type, it expands the alias into its actual type, @@ -54,59 +50,16 @@ There are several classes of aliases: ### Creating Aliases -There are three ways to create an alias and neither trigger autoloading of their inner types until the types are used. -This is to allow "type libraries" or "type files" -to be self-sufficient from their implementation and be defined without loading entire libraries. - -#### Using the use statement - -A use statement on the top-level of the file will create an alias for the file -and may be reused throughout the project once defined. - -It is created by using the `use` keyword, -followed by the type to alias, `as alias`, and then desired alias name. - -```php -namespace MyLibrary; - -use Second|Minute|Hour as alias Time; -use int|float as alias Number; -use string|Stringable as alias Stringy; -use string as alias FancyString; -``` - -#### Using the type_alias function - -A new function called `type_alias` will be introduced in the global scope. This function will take two arguments: - -1. The alias name as a string -2. The union/intersection or single type as a string. - -The use case for this function is for dynamically creating aliases during runtime, similar to `class_alias`. +An alias is defined by the work `alias`, followed by the alias name, a colon, and then the type to alias: ```php -type_alias('\MyLibrary\Time', 'Second|Minute|Hour'); -type_alias('\MyLibrary\Number', 'int|float'); -type_alias('\MyLibrary\Stringy', 'string|Stringable'); -type_alias('\MyLibrary\FancyString', 'string'); +alias Time: Second|Minute|Hour; +alias Number: int|float; +alias Stringy: string|Stringable; +alias Matrix: array; +alias Client: HttpClient&JsonClient; ``` -#### File-local aliases - -The use statement will be extended to allow using aliases in the current file, -just as you can today, only with intersections, unions, and aliases. - -```php -namespace MyLibrary; - -use Second|Minute|Hour as Time; -use int|float as Number; -use string|Stringable as Stringy; -use string as FancyString; -``` - -Local aliases lack the "as alias" syntax and only use the "as" keyword. - ### Using Aliases Since an alias is essentially a class, under the hood, it can be used in the same way as a class. @@ -118,29 +71,35 @@ namespace MyProject; use MyLibrary\Time; use MyLibrary\Number; use MyLibrary\Stringy; -use MyLibrary\FancyString; function sleepFor(Time $time) {} function retryTimes(Number $times) {} -function logMessage(Stringy|FancyString|string $message) {} // Not a fatal error +function logMessage(Stringy|string $message) {} // Not a fatal error ``` #### Intersections and Unions A type alias may be a union or intersection of other types (including other aliases), -event if they contain the same types in their alias. +even if they contain the same types in their alias. It will not be a fatal error as it currently is when a type is a union or intersection with itself. This allows libraries to declare type aliases that are specific to their own library and be reused in other projects that may also have similar aliases. For example, a library may define a `Stringy` alias that is a union of `string` and `Stringable` and another library may define a `ConstantString` alias -that is a union of `Stringable` and `MyString`. +that is also a union of `Stringable` and `string`. A project using both libraries would be able to use `Stringy` and `ConstantString` in its own type alias or function type. +```php +alias Stringy: string|Stringable; +alias ConstantString: string|Stringable; + +function logMessage(Stringy|ConstantString $message) {} // Not a fatal error +``` + #### Nesting Aliases may also be aliases of other aliases: @@ -148,13 +107,13 @@ Aliases may also be aliases of other aliases: ```php namespace MyLibrary; -use Second|Minute|Hour as alias Time; -use Time as alias Duration; +alas Time: Second|Minute|Hour; +alias Duration: Time|int; ``` #### Argument Lists and Return Types -The primary usage for aliases is in argument lists and return types: +The primary usage for aliases is in argument lists, instanceof and return types: ```php use MyLibrary\Time; @@ -162,7 +121,9 @@ use MyLibrary\Time; function sleep(Time $time): Time {} class Alarm { - public function __construct(Time $time) {} + public function __construct(Time $time) { + assert($time instanceof Time); + } public function getTime(): Time {} } @@ -176,7 +137,7 @@ Thus, these types of aliases can be used in class extension and implementation: ```php class A {} -type_alias('B', 'A'); +alias B: A; class C extends B {} ``` @@ -187,33 +148,35 @@ However, trying to extend or implement a complex or primitive alias will result class A {} class B {} -type_alias('C', 'A|B'); +alias C: A|B; +alias D: int; -class D extends C {} // Fatal error: cannot extend a complex type alias +class E extends C {} // Fatal error: cannot extend a complex type alias +class F implements D {} // Fatal error: cannot implement a primitive type alias ``` #### Calling new on Aliases -Aliases may be used in the `new` keyword, but only if the alias is a simple alias of a class: +Aliases may be used in the `new` keyword, but only if the alias is a simple alias of a class, as is currently possible: ```php class A {} -type_alias('B', 'A'); +alias B: A; // same as calling class_alias('A', 'B'); new B(); ``` #### Static calls on Aliases -Aliases may be used in static calls, but only if the alias is a simple alias of a class: +Aliases may be used in static calls, but only if the alias is a simple alias of a class, as is currently possible: ```php class A { public static function test() {} } -type_alias('B', 'A'); +alias B: A; // same as calling class_alias('A', 'B'); B::test(); ``` @@ -248,13 +211,13 @@ abstract class PrimitiveTypeAlias { } abstract class ComplexTypeAlias { - public const ReflectionUnionType|ReflectionIntersectionType $aliasOf; + public const ReflectionType $aliasOf; } ``` For simple aliases, using ReflectionClass will return the original class name, just like with `class_alias`. -Developers may access the `aliasOf` property to ascertain the alias’s underlying type. +Developers may access the `aliasOf` property to find out the alias’s underlying type. ## Why Special Classes? @@ -262,16 +225,16 @@ After looking at the current type system in PHP, it became clear that if we were to implement aliases in the existing type system, it would be overly complex and challenging to maintain. Using classes, however, is much simpler, easier to maintain, and debug. -It is also easier to reason about in the symbol tables as well since classes are synonymous with types in PHP. +It is also easier to reason about in the symbol tables as well +since classes are synonymous with types in lay-terms. ## Backward Incompatible Changes -A project using `\type_alias` as a global function will need to update their code. +There should be no backward incompatible changes. ## Proposed PHP Version(s) -List the proposed PHP versions that the feature will be included in. Use -relative versions such as "next PHP 8.x" or "next PHP 8.x.y". +8.5 or later. ## RFC Impact diff --git a/published/typed-aliases.ptxt b/published/typed-aliases.ptxt index 74d126d..057afa0 100644 --- a/published/typed-aliases.ptxt +++ b/published/typed-aliases.ptxt @@ -8,7 +8,7 @@ ===== Introduction ===== -There are many times where you may need to write out a union/intersection type many times in PHP. This can be cumbersome and error-prone. This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that is per-file, but may be used in other files as well. +There are many times when you may need to write out a union/intersection type many times in PHP. This can be cumbersome and error-prone. This RFC proposes a new "typed alias" syntax that will allow for the creation of type aliases that may be used project-wide. Here is a brief example: @@ -20,11 +20,7 @@ class Second {} class Minute {} class Hour {} -use Second|Minute|Hour as alias Time; - -// alternatively - -\type_alias('Time', 'Second|Minute|Hour'); +alias Time: Second|Minute|Hour; // in another file @@ -33,7 +29,7 @@ use MyLibrary\Time; ===== Proposal ===== -This RFC proposes to implement aliases as a "special" class type (similar to final, abstract, etc.) under the hood of the same name as its alias. Thus, the alias ''%%Time%%'' would be a class named ''%%Time%%'' in the namespace ''%%MyLibrary%%'', in the example above. This prevents collisions with other classes and defined "types" in the same namespace. +This RFC proposes to implement type aliases as a "special" class type (similar to final, abstract, etc.) under the hood of the same name as its alias. Thus, the alias ''%%Time%%'', in the above example, would be a class named ''%%Time%%'' in the namespace ''%%MyLibrary%%''. This prevents collisions with other classes and defined "types" in the same namespace. When the engine sees a class with this special type, it expands the alias into its actual type, and continues with type checking. @@ -47,54 +43,16 @@ There are several classes of aliases: ==== Creating Aliases ==== -There are three ways to create an alias and neither trigger autoloading of their inner types until the types are used. This is to allow "type libraries" or "type files" to be self-sufficient from their implementation and be defined without loading entire libraries. - -=== Using the use statement === - -A use statement on the top-level of the file will create an alias for the file and may be reused throughout the project once defined. - -It is created by using the ''%%use%%'' keyword, followed by the type to alias, ''%%as alias%%'', and then desired alias name. - - -namespace MyLibrary; - -use Second|Minute|Hour as alias Time; -use int|float as alias Number; -use string|Stringable as alias Stringy; -use string as alias FancyString; - - -=== Using the type_alias function === - -A new function called ''%%type_alias%%'' will be introduced in the global scope. This function will take two arguments: - - - The alias name as a string - - The union/intersection or single type as a string. - -The use case for this function is for dynamically creating aliases during runtime, similar to ''%%class_alias%%''. +An alias is defined by the work ''%%alias%%'', followed by the alias name, a colon, and then the type to alias: -type_alias('\MyLibrary\Time', 'Second|Minute|Hour'); -type_alias('\MyLibrary\Number', 'int|float'); -type_alias('\MyLibrary\Stringy', 'string|Stringable'); -type_alias('\MyLibrary\FancyString', 'string'); +alias Time: Second|Minute|Hour; +alias Number: int|float; +alias Stringy: string|Stringable; +alias Matrix: array; +alias Client: HttpClient&JsonClient; -=== File-local aliases === - -The use statement will be extended to allow using aliases in the current file, just as you can today, only with intersections, unions, and aliases. - - -namespace MyLibrary; - -use Second|Minute|Hour as Time; -use int|float as Number; -use string|Stringable as Stringy; -use string as FancyString; - - -Local aliases lack the "as alias" syntax and only use the "as" keyword. - ==== Using Aliases ==== Since an alias is essentially a class, under the hood, it can be used in the same way as a class. This allows you to define an alias in one part of a project and use it in another: @@ -105,18 +63,24 @@ namespace MyProject; use MyLibrary\Time; use MyLibrary\Number; use MyLibrary\Stringy; -use MyLibrary\FancyString; function sleepFor(Time $time) {} function retryTimes(Number $times) {} -function logMessage(Stringy|FancyString|string $message) {} // Not a fatal error +function logMessage(Stringy|string $message) {} // Not a fatal error === Intersections and Unions === -A type alias may be a union or intersection of other types (including other aliases), event if they contain the same types in their alias. It will not be a fatal error as it currently is when a type is a union or intersection with itself. This allows libraries to declare type aliases that are specific to their own library and be reused in other projects that may also have similar aliases. For example, a library may define a ''%%Stringy%%'' alias that is a union of ''%%string%%'' and ''%%Stringable%%'' and another library may define a ''%%ConstantString%%'' alias that is a union of ''%%Stringable%%'' and ''%%MyString%%''. A project using both libraries would be able to use ''%%Stringy%%'' and ''%%ConstantString%%'' in its own type alias or function type. +A type alias may be a union or intersection of other types (including other aliases), even if they contain the same types in their alias. It will not be a fatal error as it currently is when a type is a union or intersection with itself. This allows libraries to declare type aliases that are specific to their own library and be reused in other projects that may also have similar aliases. For example, a library may define a ''%%Stringy%%'' alias that is a union of ''%%string%%'' and ''%%Stringable%%'' and another library may define a ''%%ConstantString%%'' alias that is also a union of ''%%Stringable%%'' and ''%%string%%''. A project using both libraries would be able to use ''%%Stringy%%'' and ''%%ConstantString%%'' in its own type alias or function type. + + +alias Stringy: string|Stringable; +alias ConstantString: string|Stringable; + +function logMessage(Stringy|ConstantString $message) {} // Not a fatal error + === Nesting === @@ -125,13 +89,13 @@ Aliases may also be aliases of other aliases: namespace MyLibrary; -use Second|Minute|Hour as alias Time; -use Time as alias Duration; +alas Time: Second|Minute|Hour; +alias Duration: Time|int; === Argument Lists and Return Types === -The primary usage for aliases is in argument lists and return types: +The primary usage for aliases is in argument lists, instanceof and return types: use MyLibrary\Time; @@ -139,7 +103,9 @@ use MyLibrary\Time; function sleep(Time $time): Time {} class Alarm { - public function __construct(Time $time) {} + public function __construct(Time $time) { + assert($time instanceof Time); + } public function getTime(): Time {} } @@ -152,7 +118,7 @@ For simple aliases of other classes, ''%%type_alias%%'' behaves exactly like ''% class A {} -type_alias('B', 'A'); +alias B: A; class C extends B {} @@ -163,33 +129,35 @@ However, trying to extend or implement a complex or primitive alias will result class A {} class B {} -type_alias('C', 'A|B'); +alias C: A|B; +alias D: int; -class D extends C {} // Fatal error: cannot extend a complex type alias +class E extends C {} // Fatal error: cannot extend a complex type alias +class F implements D {} // Fatal error: cannot implement a primitive type alias === Calling new on Aliases === -Aliases may be used in the ''%%new%%'' keyword, but only if the alias is a simple alias of a class: +Aliases may be used in the ''%%new%%'' keyword, but only if the alias is a simple alias of a class, as is currently possible: class A {} -type_alias('B', 'A'); +alias B: A; // same as calling class_alias('A', 'B'); new B(); === Static calls on Aliases === -Aliases may be used in static calls, but only if the alias is a simple alias of a class: +Aliases may be used in static calls, but only if the alias is a simple alias of a class, as is currently possible: class A { public static function test() {} } -type_alias('B', 'A'); +alias B: A; // same as calling class_alias('A', 'B'); B::test(); @@ -223,25 +191,25 @@ abstract class PrimitiveTypeAlias { } abstract class ComplexTypeAlias { - public const ReflectionUnionType|ReflectionIntersectionType $aliasOf; + public const ReflectionType $aliasOf; } For simple aliases, using ReflectionClass will return the original class name, just like with ''%%class_alias%%''. -Developers may access the ''%%aliasOf%%'' property to ascertain the alias’s underlying type. +Developers may access the ''%%aliasOf%%'' property to find out the alias’s underlying type. ===== Why Special Classes? ===== -After looking at the current type system in PHP, it became clear that if we were to implement aliases in the existing type system, it would be overly complex and challenging to maintain. Using classes, however, is much simpler, easier to maintain, and debug. It is also easier to reason about in the symbol tables as well since classes are synonymous with types in PHP. +After looking at the current type system in PHP, it became clear that if we were to implement aliases in the existing type system, it would be overly complex and challenging to maintain. Using classes, however, is much simpler, easier to maintain, and debug. It is also easier to reason about in the symbol tables as well since classes are synonymous with types in lay-terms. ===== Backward Incompatible Changes ===== -A project using ''%%\type_alias%%'' as a global function will need to update their code. +There should be no backward incompatible changes. ===== Proposed PHP Version(s) ===== -List the proposed PHP versions that the feature will be included in. Use relative versions such as "next PHP 8.x" or "next PHP 8.x.y". +8.5 or later. ===== RFC Impact =====