rfc:namespace_scoped_declares

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
rfc:namespace_scoped_declares [2016/09/22 20:39] nikicrfc:namespace_scoped_declares [2016/09/23 21:29] nikic
Line 4: Line 4:
   * Author: Nikita Popov <nikic@php.net>   * Author: Nikita Popov <nikic@php.net>
   * Proposed PHP version: PHP 7.2   * Proposed PHP version: PHP 7.2
-  * Status: Draft+  * Status: Under Discussion 
 +  * ML thread: http://externals.io/thread/326
  
 ===== Introduction ===== ===== Introduction =====
Line 24: Line 25:
 The motivation behind this proposal is broader than just saving a single ''declare()'' in each file of a project. This kind of namespace-scoped declare may provide the means for introducing certain changes that make the language "stricter" without breaking backwards compatibility or library-interoperability (as ini directives do). The motivation behind this proposal is broader than just saving a single ''declare()'' in each file of a project. This kind of namespace-scoped declare may provide the means for introducing certain changes that make the language "stricter" without breaking backwards compatibility or library-interoperability (as ini directives do).
  
-For example, in modern PHP code it is nearly always a bug to dynamically create a property on an object that has not previously been declared. However disallowing this globally would be completely unthinkable, because a lot of software relies on it, and some use-cases legitimately benefit from the ability to do this. Adding an ini-setting for this is not possible either, because this would prevent using libraries that assume different values for this setting (which is the same reason why strict_types is not an ini setting).+For example, in modern PHP code it is nearly always a bug to dynamically create a property on an object that has not previously been declared. However disallowing this globally would be unthinkable, because a lot of software relies on it, and some use-cases legitimately benefit from the ability to do this. Adding an ini-setting to control this behavior is not possible either, because this would prevent using libraries that assume different values for this setting (which is the same reason for why strict_types is not an ini setting).
  
-Using a declare() directive for this purpose would solve the inter-operability issues and would additionally provide an "escape-hatch" for the cases the functionality is legitimately needed. However if the number of such directives increases, specifying them for every single file in a project would quickly become unwieldy. Additionally, it is likely that a library will want to use the same directives for all files. Changing the value of a directive would then require performing an update on all files. Namespace-scoped declares solve this by specifying such options in a single bootstrap file:+Using a declare() directive for this purpose would solve the inter-operability issues and would additionally provide an "escape-hatch" for the cases where the functionality is legitimately needed. However if the number of such directives increases, specifying them for every single file in a project would quickly become unwieldy. Additionally, it is likely that a library will want to use the same directives for all files. Changing the value of a directive would then require performing an update on all files. Namespace-scoped declares solve this by specifying such options in a single bootstrap file:
  
 <code php> <code php>
Line 64: Line 65:
 If ''namespace_declare'' was already called for a certain ''$namespace'', another call with the same namespace will result in an ''Error''. If ''namespace_declare'' was already called for a certain ''$namespace'', another call with the same namespace will result in an ''Error''.
  
-The declare directive defaults specified by ''namespace_declare()'' take effect for all files compiled (i.e. included) **after** the execution of the function call. Files included prior to the function call are not affect. The file that contains the call is also not affected, because the function will only be executed after the file has already been compiled.+The declare directive defaults specified by ''namespace_declare()'' take effect for all files compiled (i.e. included) **after** the execution of the function call. Files included prior to the function call are not affected. The file that contains the call is also not affected, because the function will only be executed after the file has already been compiled.
  
 ''namespace_declare'' has no impact on aliases created using ''class_alias'' and entities declared by internal code. ''namespace_declare'' has no impact on aliases created using ''class_alias'' and entities declared by internal code.
- 
-TODO: What about the encoding directive? 
  
 ==== Nesting behavior ==== ==== Nesting behavior ====
Line 75: Line 74:
  
 <code php> <code php>
-namespace_declare('A', ['strict_types' => 1, 'no_dynamic_properties' => 1]); +namespace_declare('A', ['strict_types' => 1, 'dynamic_object_properties' => 0]); 
-namespace_declare('A\B', ['no_dynamic_properties' => 0]);+namespace_declare('A\B', ['dynamic_object_properties' => 1]);
 </code> </code>
  
Line 82: Line 81:
  
 <code> <code>
-no_dynamic_properties=// Because this was specified on A\B +dynamic_object_properties=// Because this was specified on A\B 
-strict_types=1          // Because this was not specified on A\B, but specified on A +strict_types=1              // Because this was not specified on A\B, but specified on A 
-ticks=0                 // Because this was not specified on A\B or A, so the global default is used+ticks=0                     // Because this was not specified on A\B or A, so the global default is used
 ... ...
 </code> </code>
Line 91: Line 90:
  
 <code php> <code php>
-namespace_declare('A\B', ['no_dynamic_properties' => 0]); +namespace_declare('A\B', ['dynamic_object_properties' => 1]); 
-namespace_declare('A', ['strict_types' => 1, 'no_dynamic_properties' => 1]);+namespace_declare('A', ['strict_types' => 1, 'dynamic_object_properties' => 0]);
 </code> </code>
  
Line 116: Line 115:
 ==== Open Question: Handling of unknown directives ==== ==== Open Question: Handling of unknown directives ====
  
-The ''declare()'' statement will throw a compile-time warning if an unknown directive is used. This has the advantage that typos can be easily spotted. On the other hand this is bad for forward-compatibility, because specifying a directive that is only known in newer PHP versions would generate a warning in older versions. If the directive only exists to make certain checks stricter (such as  the ''dynamic_object_properties'' example) it would be preferably it would simply be (silently) ignored on older versions.+The ''declare()'' statement will throw a compile-time warning if an unknown directive is used. This has the advantage that typos can be easily spotted. On the other hand this is bad for forward-compatibility, because specifying a directive that is only known in newer PHP versions would generate a warning in older versions. If the directive only exists to make certain checks stricter (such as  the ''dynamic_object_properties'' example) it would be preferably if it would simply be (silently) ignored on older versions.
  
 If we go for throwing a warning here, it might be beneficial to add a ''supports_declare()'' function, which allows you to determine whether a certain declare directive is supported, without resorting to a PHP version check (which would likely be unreliable with regard to alternative PHP implementations). If we go for throwing a warning here, it might be beneficial to add a ''supports_declare()'' function, which allows you to determine whether a certain declare directive is supported, without resorting to a PHP version check (which would likely be unreliable with regard to alternative PHP implementations).
Line 149: Line 148:
 While this RFC proposes specific functionality (namespace-scoped declares), the Motivation section outlined a likely future application of this mechanism, which is the introduction of additional declare directives that modify language behavior to be "stricter" in some sense, without breaking compatibility or interoperability. While this RFC proposes specific functionality (namespace-scoped declares), the Motivation section outlined a likely future application of this mechanism, which is the introduction of additional declare directives that modify language behavior to be "stricter" in some sense, without breaking compatibility or interoperability.
  
-A danger I see in this approach is a proliferation of many declare directives controlling small bits of the language. In this RFC I used a ''no_dynamic_properties'' directive as an example. Recently a discussion has come up for a ''strict_comparison'' directive, which makes operators such as ''<'' type-strict. On top of this we could add a ''strict_conditionals'' directive to make conditions only accept booleans. Maybe ''strict_arg_counts'' to forbid passing too many arguments to a userland function. You get the picture. Increasingly minor issues would end up covered by their own declare directive, so that you'd have to specify dozens of different directives to enable everything.+A danger I see in this approach is a proliferation of many declare directives controlling small bits of the language. In this RFC I used a ''dynamic_object_properties'' directive as an example. Recently a discussion has come up for a ''strict_comparison'' directive, which makes operators such as ''<'' type-strict. On top of this we could add a ''strict_conditionals'' directive to make conditions only accept booleans. Maybe ''strict_arg_counts'' to forbid passing too many arguments to a userland function. You get the picture. Increasingly minor issues would end up covered by their own declare directive, so that you'd have to specify dozens of different directives to enable everything.
  
 This problem might motivate one to instead introduce The One True Strict Mode, similar to ''"use strict"'' in JavaScript. A single ''strict=1'' directive that enables a certain, carefully chosen set of "strict" functionality. If there were just a single directive you have to specify at the top of each file, we could probably also live without the functionality proposed by this RFC. This problem might motivate one to instead introduce The One True Strict Mode, similar to ''"use strict"'' in JavaScript. A single ''strict=1'' directive that enables a certain, carefully chosen set of "strict" functionality. If there were just a single directive you have to specify at the top of each file, we could probably also live without the functionality proposed by this RFC.
Line 165: Line 164:
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-None.+The global function name ''namespace_declare'' will no longer be usable.
  
-===== Proposed Voting Choices =====+===== Vote =====
  
 As this is a language change, a 2/3 majority is required. As this is a language change, a 2/3 majority is required.
Line 174: Line 173:
  
 There is no implementation yet. There is no implementation yet.
 +
 +
 +===== TODOs =====
 +
 +  * Resolve open question "Handling of unknown directives"
 +  * Resolve open question "namespace_declare after namespace already used"
 +  * Consider adding some introspection functionality, such as ''get_namespace_declares()''
 +  * Investigate whether this mechanism can support the ''encoding'' directive, which is a lot more magic than the rest.
 +  * Actually implement this thing.
rfc/namespace_scoped_declares.txt · Last modified: 2022/01/25 18:22 by ilutov