(NB: This idea is NOT about namespace visibility for classes, interfaces or
traits (already discussed here: https://wiki.php.net/rfc/namespace-visibility).
It's about adding a new visibility qualifier, call it "internal", to
make properties
private inside a namespace. The purpose is to make composition less
fragile by not
exposing more than necessary.)
(NB: This feature exists in Swift, described here:
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)
We have public, protected and private. Since PHP has no module system, we
have no qualifier to mark a class property as "private for this module".
One solution to this could be to add a new qualifier "internal", to make
properties public within the current namespace.
Use-cases:
- Split data and behaviour into different files and classes
- Safely skip getters and setters without risk of properties being abused
by client code
Tiny example:
namespace App\User;
class User {
internal $username;
internal $password;
}
namespace App\User\Services;
class ProcessUser {
// Child namespace has access to internal properties,
// but App\ has not.
}
Possible?
Olle
(NB: This idea is NOT about namespace visibility for classes, interfaces or
traits (already discussed here: https://wiki.php.net/rfc/namespace-visibility).
It's about adding a new visibility qualifier, call it "internal", to
make properties
private inside a namespace. The purpose is to make composition less
fragile by not
exposing more than necessary.)(NB: This feature exists in Swift, described here:
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)We have public, protected and private. Since PHP has no module system, we
have no qualifier to mark a class property as "private for this module".
One solution to this could be to add a new qualifier "internal", to make
properties public within the current namespace.Use-cases:
- Split data and behaviour into different files and classes
- Safely skip getters and setters without risk of properties being abused
by client code
+1
This might be able to address a use-case I have wanted for a while, which is to have traits with properties (and methods) that are not accessible with the class that uses the trait.
So if a trait is in a different namespace than the using class, the using class would not be able to access these "internal" properties.
Or at least I think that could work?
-Mike
Tiny example:
namespace App\User; class User { internal $username; internal $password; } namespace App\User\Services; class ProcessUser { // Child namespace has access to internal properties, // but App\ has not. }
Possible?
Olle
On Sep 17, 2020, at 8:28 AM, Olle Härstedt olleharstedt@gmail.com
wrote:(NB: This idea is NOT about namespace visibility for classes, interfaces
or
traits (already discussed here:
https://wiki.php.net/rfc/namespace-visibility).
It's about adding a new visibility qualifier, call it "internal", to
make properties
private inside a namespace. The purpose is to make composition less
fragile by not
exposing more than necessary.)(NB: This feature exists in Swift, described here:
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)We have public, protected and private. Since PHP has no module system, we
have no qualifier to mark a class property as "private for this module".
One solution to this could be to add a new qualifier "internal", to make
properties public within the current namespace.Use-cases:
- Split data and behaviour into different files and classes
- Safely skip getters and setters without risk of properties being abused
by client code+1
This might be able to address a use-case I have wanted for a while, which
is to have traits with properties (and methods) that are not accessible
with the class that uses the trait.So if a trait is in a different namespace than the using class, the using
class would not be able to access these "internal" properties.Or at least I think that could work?
-Mike
True, the properties/methods of the trait could be made internal, while
exposing the trait itself (and at least one method, to make it usable).
The question is if PHP tracks the current namespace it's in on each line.
It needs to be known at all times for such an access level to work.
Olle
On Sep 17, 2020, at 8:28 AM, Olle Härstedt olleharstedt@gmail.com
wrote:(NB: This idea is NOT about namespace visibility for classes,
interfaces or
traits (already discussed here:
https://wiki.php.net/rfc/namespace-visibility).
It's about adding a new visibility qualifier, call it "internal", to
make properties
private inside a namespace. The purpose is to make composition less
fragile by not
exposing more than necessary.)(NB: This feature exists in Swift, described here:
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)We have public, protected and private. Since PHP has no module system,
we
have no qualifier to mark a class property as "private for this module".
One solution to this could be to add a new qualifier "internal", to make
properties public within the current namespace.Use-cases:
- Split data and behaviour into different files and classes
- Safely skip getters and setters without risk of properties being
abused
by client code+1
This might be able to address a use-case I have wanted for a while, which
is to have traits with properties (and methods) that are not accessible
with the class that uses the trait.So if a trait is in a different namespace than the using class, the using
class would not be able to access these "internal" properties.Or at least I think that could work?
-Mike
True, the properties/methods of the trait could be made internal, while
exposing the trait itself (and at least one method, to make it usable).The question is if PHP tracks the current namespace it's in on each line.
It needs to be known at all times for such an access level to work.Olle
Just discovered that this feature already exists in Psalm, @psalm-internal.
They are streets ahead. :)
Olle
We have public, protected and private. Since PHP has no module system, we
have no qualifier to mark a class property as "private for this module".
One solution to this could be to add a new qualifier "internal", to make
properties public within the current namespace.
As well as the implementation details of doing this at run-time (which
is where PHP does all its access checks right now) the mapping of
"namespace" to "module" is not necessarily the most useful one.
For instance, if you define an "internal" property in a class called
Acme\Frobulator\Handlers\TextFile\Format\CSV, it would be accessible in
other classes in namespace Acme\Frobulator\Handlers\TextFile\Format\ and
possibly also sub-namespaces like
Acme\Frobulator\Handlers\TextFile\Format\Exceptions, but not in other
parts of Acme\Frobulator\Handlers\TextFile or Acme\Frobulator as a whole.
If Acme\Frobulator was the root of the Composer package, the only way to
make something internal to the package as a whole would be to flatten
everything into that root namespace, like
Acme\Frobulator\Handler_TextFile_Format_CSV
That leads me to think that if we do want "module"-level features - be
it internal classes, internal properties, or module-level declare()
settings - we need a way for the user to define that separately from the
fully-qualified namespace.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
This sort of thing would be useful. Drupal and Symfony both mark methods in
their libraries that aren't supposed to be used externally, but people do
anyway and then they get mad at the framework developers when they decide
to rearrange what are supposed to be internal methods.
I wrote a userland assertion to enforce just this sort of thing - enforcing
Java's notion of protected on methods otherwise marked as public. It worked
by looking up the caller in the debug_backtrace and checking to see if the
namespaces matched. To keep the impact of this check minimal I used
assert()
for the check so that it wouldn't bog down production (where,
presumably, no new code would be written).
Now, the above worked, but you could hack in by lying about your namespace
in your file declaration. Since, at the end of the day, PHP's notion of
namespace is entirely a string replace with no other enforcement there's no
way I can see of changing this short of fundamentally changing how PHP
handles namespaces. But I wonder if that should be bothered with since
someone would have to go considerably out of their way to make a call to a
method they weren't supposed to, and they only have themselves to blame if
this sort of solution becomes unstable.
Next thing to consider - we have the problem of having already used the
protected keyword in PHP, and honestly I prefer PHP's interpretation of the
word. However, PHP doesn't have class scope at this time, so one
possibility is to allow class scope to set a default scope of the unscoped
methods and members of the class. So...
public class A {
function foo () {} // Callable from anywhere.
}
protected class B {
function foo () {} // Callable only from current namespace, or any child
namespace.
public function moo() {} // Callable anywhere. In effect the method
specific scope overrides class scope.
}
private class C {
function foo () {} // Callable only from the same namespace, no children
can access. This includes a extending child function from another namespace
protected function moo () {} // Callable from any child class regardless
of that child's namespace.
}
That could work. Feel free to poke holes in this - I'm sure I've missed
something.
On Sat, Sep 19, 2020 at 7:43 AM Rowan Tommins rowan.collins@gmail.com
wrote:
We have public, protected and private. Since PHP has no module system, we
have no qualifier to mark a class property as "private for this module".
One solution to this could be to add a new qualifier "internal", to make
properties public within the current namespace.As well as the implementation details of doing this at run-time (which
is where PHP does all its access checks right now) the mapping of
"namespace" to "module" is not necessarily the most useful one.For instance, if you define an "internal" property in a class called
Acme\Frobulator\Handlers\TextFile\Format\CSV, it would be accessible in
other classes in namespace Acme\Frobulator\Handlers\TextFile\Format\ and
possibly also sub-namespaces like
Acme\Frobulator\Handlers\TextFile\Format\Exceptions, but not in other
parts of Acme\Frobulator\Handlers\TextFile or Acme\Frobulator as a whole.If Acme\Frobulator was the root of the Composer package, the only way to
make something internal to the package as a whole would be to flatten
everything into that root namespace, like
Acme\Frobulator\Handler_TextFile_Format_CSVThat leads me to think that if we do want "module"-level features - be
it internal classes, internal properties, or module-level declare()
settings - we need a way for the user to define that separately from the
fully-qualified namespace.Regards,
--
Rowan Tommins (né Collins)
[IMSoP]--
To unsubscribe, visit: https://www.php.net/unsub.php
2020-09-21 16:13 GMT, Michael Morris tendoaki@gmail.com:
This sort of thing would be useful. Drupal and Symfony both mark methods in
their libraries that aren't supposed to be used externally, but people do
anyway and then they get mad at the framework developers when they decide
to rearrange what are supposed to be internal methods.I wrote a userland assertion to enforce just this sort of thing - enforcing
Java's notion of protected on methods otherwise marked as public. It worked
by looking up the caller in the debug_backtrace and checking to see if the
namespaces matched. To keep the impact of this check minimal I used
assert()
for the check so that it wouldn't bog down production (where,
presumably, no new code would be written).Now, the above worked, but you could hack in by lying about your namespace
in your file declaration. Since, at the end of the day, PHP's notion of
namespace is entirely a string replace with no other enforcement there's no
way I can see of changing this short of fundamentally changing how PHP
handles namespaces. But I wonder if that should be bothered with since
someone would have to go considerably out of their way to make a call to a
method they weren't supposed to, and they only have themselves to blame if
this sort of solution becomes unstable.Next thing to consider - we have the problem of having already used the
protected keyword in PHP, and honestly I prefer PHP's interpretation of the
word. However, PHP doesn't have class scope at this time, so one
possibility is to allow class scope to set a default scope of the unscoped
methods and members of the class. So...public class A {
function foo () {} // Callable from anywhere.
}protected class B {
function foo () {} // Callable only from current namespace, or any child
namespace.
public function moo() {} // Callable anywhere. In effect the method
specific scope overrides class scope.
}private class C {
function foo () {} // Callable only from the same namespace, no children
can access. This includes a extending child function from another namespace
protected function moo () {} // Callable from any child class regardless
of that child's namespace.
}That could work. Feel free to poke holes in this - I'm sure I've missed
something.
This syntax is already used for the PR about namespace encapsulation.
Also, I think visibility always should be explicit.
Olle
Next thing to consider - we have the problem of having already used the
protected keyword in PHP, and honestly I prefer PHP's interpretation of the
word.
I think it's actually Java that's the outlier here: PHP's meaning of
"protected" corresponds directly to C++, and to name a few I came upon
in a quick search it's also used that way by Kotlin [1], Scala [2], C#
[3] and Oxygene [4] - all of which use other ways to signal package
visibility:
- Kotlin has a fourth visibility, "internal", meaning "public within
this module" - Scala has an interesting syntax where you can modify the visibility
with the name of a package, as in "private[SomePackage]" - C# adds three extra visibility modes: "internal", "internal
protected", and "private protected" - Oxygene (a language I know next to nothing about) apparently has no
fewer than TEN modes, including both "assembly or protected" and
"assembly and protected"
All of which I think shows two things: firstly, that there are lots of
different ways to express this concept; and secondly, that there are a
lot of subtle combinations that might be useful.
The C# list is particularly confusing, presumably because it's evolved
without a coherent plan. If I'm understanding correctly, C#'s "private
protected" corresponds to Oxygene's much clearer "assembly and
protected", while "protected internal" corresponds to "assembly or
protected". Considering those combinations up front might be sensible,
even if they're not implemented at once.
If we're considering using namespaces rather than a new package concept
for this, we potentially have a third dimension: is it private to this
exact namespace, or any "child" namespace starting with the same
prefix? Maybe something like Scala's modifier syntax might be more
useful, for instance:
namespace Acme\Frobulator\Helpers\File;
class FileWidget {
private[namespace] $foo; // accessible specifically in the
"Acme\Frobulator\Helpers\File" namespace
private[Acme\Frobulator\Helpers\File*] $bar; // accessible in this
namespace, and all sub-namespaces
private[Acme\Frobulator*] $baz; // accessible in "Acme\Frobulator"
and all its sub-namespaces
}
[1] https://kotlinlang.org/docs/reference/visibility-modifiers.html
[2] https://www.tutorialspoint.com/scala/scala_access_modifiers.htm
[3]
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers
[4]
https://docs.elementscompiler.com/Oxygene/Members/VisibilityLevels/structs/access-modifiers
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
2020-09-21 21:50 GMT, Rowan Tommins rowan.collins@gmail.com:
Next thing to consider - we have the problem of having already used the
protected keyword in PHP, and honestly I prefer PHP's interpretation of
the
word.I think it's actually Java that's the outlier here: PHP's meaning of
"protected" corresponds directly to C++, and to name a few I came upon
in a quick search it's also used that way by Kotlin [1], Scala [2], C#
[3] and Oxygene [4] - all of which use other ways to signal package
visibility:
- Kotlin has a fourth visibility, "internal", meaning "public within
this module"- Scala has an interesting syntax where you can modify the visibility
with the name of a package, as in "private[SomePackage]"- C# adds three extra visibility modes: "internal", "internal
protected", and "private protected"- Oxygene (a language I know next to nothing about) apparently has no
fewer than TEN modes, including both "assembly or protected" and
"assembly and protected"All of which I think shows two things: firstly, that there are lots of
different ways to express this concept; and secondly, that there are a
lot of subtle combinations that might be useful.The C# list is particularly confusing, presumably because it's evolved
without a coherent plan. If I'm understanding correctly, C#'s "private
protected" corresponds to Oxygene's much clearer "assembly and
protected", while "protected internal" corresponds to "assembly or
protected". Considering those combinations up front might be sensible,
even if they're not implemented at once.If we're considering using namespaces rather than a new package concept
for this, we potentially have a third dimension: is it private to this
exact namespace, or any "child" namespace starting with the same
prefix? Maybe something like Scala's modifier syntax might be more
useful, for instance:namespace Acme\Frobulator\Helpers\File;
class FileWidget {
private[namespace] $foo; // accessible specifically in the
"Acme\Frobulator\Helpers\File" namespace
private[Acme\Frobulator\Helpers\File*] $bar; // accessible in this
namespace, and all sub-namespaces
private[Acme\Frobulator*] $baz; // accessible in "Acme\Frobulator"
and all its sub-namespaces
}[1] https://kotlinlang.org/docs/reference/visibility-modifiers.html
[2] https://www.tutorialspoint.com/scala/scala_access_modifiers.htm
[3]
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers
[4]
https://docs.elementscompiler.com/Oxygene/Members/VisibilityLevels/structs/access-modifiersRegards,
--
Rowan Tommins (né Collins)
[IMSoP]--
To unsubscribe, visit: https://www.php.net/unsub.php
All good ideas, but I still don't know if PHP is aware of current
namespace at all time? Anyone has this knowledge? It's a precondition
to check namespace access.
Olle
This should answer your question:
https://github.com/php/php-src/pull/947#issuecomment-224912697
2020-09-21 21:50 GMT, Rowan Tommins rowan.collins@gmail.com:
Next thing to consider - we have the problem of having already used the
protected keyword in PHP, and honestly I prefer PHP's interpretation of
the
word.I think it's actually Java that's the outlier here: PHP's meaning of
"protected" corresponds directly to C++, and to name a few I came upon
in a quick search it's also used that way by Kotlin [1], Scala [2], C#
[3] and Oxygene [4] - all of which use other ways to signal package
visibility:
- Kotlin has a fourth visibility, "internal", meaning "public within
this module"- Scala has an interesting syntax where you can modify the visibility
with the name of a package, as in "private[SomePackage]"- C# adds three extra visibility modes: "internal", "internal
protected", and "private protected"- Oxygene (a language I know next to nothing about) apparently has no
fewer than TEN modes, including both "assembly or protected" and
"assembly and protected"All of which I think shows two things: firstly, that there are lots of
different ways to express this concept; and secondly, that there are a
lot of subtle combinations that might be useful.The C# list is particularly confusing, presumably because it's evolved
without a coherent plan. If I'm understanding correctly, C#'s "private
protected" corresponds to Oxygene's much clearer "assembly and
protected", while "protected internal" corresponds to "assembly or
protected". Considering those combinations up front might be sensible,
even if they're not implemented at once.If we're considering using namespaces rather than a new package concept
for this, we potentially have a third dimension: is it private to this
exact namespace, or any "child" namespace starting with the same
prefix? Maybe something like Scala's modifier syntax might be more
useful, for instance:namespace Acme\Frobulator\Helpers\File;
class FileWidget {
private[namespace] $foo; // accessible specifically in the
"Acme\Frobulator\Helpers\File" namespace
private[Acme\Frobulator\Helpers\File*] $bar; // accessible in this
namespace, and all sub-namespaces
private[Acme\Frobulator*] $baz; // accessible in "Acme\Frobulator"
and all its sub-namespaces
}[1] https://kotlinlang.org/docs/reference/visibility-modifiers.html
[2] https://www.tutorialspoint.com/scala/scala_access_modifiers.htm
[3]
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers
[4]
https://docs.elementscompiler.com/Oxygene/Members/VisibilityLevels/structs/access-modifiersRegards,
--
Rowan Tommins (né Collins)
[IMSoP]--
To unsubscribe, visit: https://www.php.net/unsub.php
All good ideas, but I still don't know if PHP is aware of current
namespace at all time? Anyone has this knowledge? It's a precondition
to check namespace access.Olle
--
To unsubscribe, visit: https://www.php.net/unsub.php
--
Guilherme Blanco
SVP Technology at Statflo Inc.
Mobile: +1 647 232 5599
2020-09-22 14:51 GMT, guilhermeblanco@gmail.com guilhermeblanco@gmail.com:
This should answer your question:
https://github.com/php/php-src/pull/947#issuecomment-224912697
Yes, indeed. "The reason comes due to how namespaces are implemented
in the language, where they only exist at compile time." Pity.
On Tue, Sep 22, 2020 at 7:38 AM Olle Härstedt olleharstedt@gmail.com
wrote:2020-09-21 21:50 GMT, Rowan Tommins rowan.collins@gmail.com:
Next thing to consider - we have the problem of having already used
the
protected keyword in PHP, and honestly I prefer PHP's interpretation
of
the
word.I think it's actually Java that's the outlier here: PHP's meaning of
"protected" corresponds directly to C++, and to name a few I came upon
in a quick search it's also used that way by Kotlin [1], Scala [2], C#
[3] and Oxygene [4] - all of which use other ways to signal package
visibility:
- Kotlin has a fourth visibility, "internal", meaning "public within
this module"- Scala has an interesting syntax where you can modify the visibility
with the name of a package, as in "private[SomePackage]"- C# adds three extra visibility modes: "internal", "internal
protected", and "private protected"- Oxygene (a language I know next to nothing about) apparently has no
fewer than TEN modes, including both "assembly or protected" and
"assembly and protected"All of which I think shows two things: firstly, that there are lots of
different ways to express this concept; and secondly, that there are a
lot of subtle combinations that might be useful.The C# list is particularly confusing, presumably because it's evolved
without a coherent plan. If I'm understanding correctly, C#'s "private
protected" corresponds to Oxygene's much clearer "assembly and
protected", while "protected internal" corresponds to "assembly or
protected". Considering those combinations up front might be sensible,
even if they're not implemented at once.If we're considering using namespaces rather than a new package concept
for this, we potentially have a third dimension: is it private to this
exact namespace, or any "child" namespace starting with the same
prefix? Maybe something like Scala's modifier syntax might be more
useful, for instance:namespace Acme\Frobulator\Helpers\File;
class FileWidget {
private[namespace] $foo; // accessible specifically in the
"Acme\Frobulator\Helpers\File" namespace
private[Acme\Frobulator\Helpers\File*] $bar; // accessible in
this
namespace, and all sub-namespaces
private[Acme\Frobulator*] $baz; // accessible in
"Acme\Frobulator"
and all its sub-namespaces
}[1] https://kotlinlang.org/docs/reference/visibility-modifiers.html
[2] https://www.tutorialspoint.com/scala/scala_access_modifiers.htm
[3]
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers
[4]
https://docs.elementscompiler.com/Oxygene/Members/VisibilityLevels/structs/access-modifiersRegards,
--
Rowan Tommins (né Collins)
[IMSoP]--
To unsubscribe, visit: https://www.php.net/unsub.php
All good ideas, but I still don't know if PHP is aware of current
namespace at all time? Anyone has this knowledge? It's a precondition
to check namespace access.Olle
--
To unsubscribe, visit: https://www.php.net/unsub.php
--
Guilherme Blanco
SVP Technology at Statflo Inc.
Mobile: +1 647 232 5599