Hi list!
Don't know if this already exists, but maybe we could consider
brainstorming or gather alternatives for different types of
encapsulation when using composition? Currently, encapsulation like
private and protected is only possible when using inheritance - when
using composition, that is, class A is using (aggregating) class B,
your only alternative is public, so full visibility (or
getters/setters, which also is full visibility, since you can't limit
who's using those methods).
There are four alternatives at least:
- Friend classes, like in C++: Class B is annotated to be a friend of
A, so class A can see the content of class B but no other class can. - Package private, like in Java: Class A and B are defined in the same
package, so they can see each other's fields that are defined as
"internal" - Namespace private: Class A and B are defined in the same namespace,
so they can see each other's properties that are defined as "internal" - Attribute for namespace private: Like above, but not possible right
now since it requires that namespace is added to the PHP runtime
Functionality like this should not be handled by static analysis,
because library authors need to be able to trust it. Psalm supports
already @internal and @psalm-internal, but of course not everyone is
using Psalm/Phpstan/other analyzer.
The problem with package private is that PHP does not have packages.
The problem with namespace private is that it cannot handle namespace
siblings. Another problem is that it can be "hacked" by client code if
the client code decides to use another vendor's namespace (a weird
thing to do, though).
The run-time hit should not be bigger, since we're already checking
each object property for private. Correct me if I'm wrong.
The risk/benefit/effort should be considered for all alternatives. I'd
argue that something is better than nothing, so far as it does not
block future work. :) Maybe adding namespaces to runtime could be a
first step?
Olle
- Friend classes, like in C++: Class B is annotated to be a friend of
A, so class A can see the content of class B but no other class can.
https://wiki.php.net/rfc/friend-classes has been declined.
- Namespace private: Class A and B are defined in the same namespace,
so they can see each other's properties that are defined as "internal"
https://wiki.php.net/rfc/namespace-visibility appears to be inactive.
--
Christoph M. Becker
2021-08-26 16:20 GMT+02:00, Christoph M. Becker cmbecker69@gmx.de:
- Friend classes, like in C++: Class B is annotated to be a friend of
A, so class A can see the content of class B but no other class can.https://wiki.php.net/rfc/friend-classes has been declined.
- Namespace private: Class A and B are defined in the same namespace,
so they can see each other's properties that are defined as "internal"https://wiki.php.net/rfc/namespace-visibility appears to be inactive.
--
Christoph M. Becker
Thanks! I'll check the belonging discussions on externals.io
Olle
Don't know if this already exists, but maybe we could consider
brainstorming or gather alternatives for different types of
encapsulation when using composition?
Although not quite equivalent to your suggestions, these threads on
making delegation to an encapsulated object might be interesting to you:
* https://externals.io/message/103353
If we had something like that, then perhaps there could be a
"delegatable" visibility, which was the equivalent for "protected", but
when delegating rather than inheriting, e.g.
class Foo {
private int $a = 1;
delegatable int $b = 2;
}
class Bar {
private delegate Foo $foo;
public function __construct(Foo $foo) {
$this->foo = $foo;
echo $this->a; // Error: can't access private property of a
different class
echo $this->b; // allowed: access is to a "delegatable"
property, in a "delegated" context
}
}
--
Rowan Tommins
[IMSoP]
2021-08-26 20:30 GMT+02:00, Rowan Tommins rowan.collins@gmail.com:
Don't know if this already exists, but maybe we could consider
brainstorming or gather alternatives for different types of
encapsulation when using composition?Although not quite equivalent to your suggestions, these threads on
making delegation to an encapsulated object might be interesting to you:
Right, there are indeed a bunch of attempts and suggestions, some
downvoted, others abandoned.
I'm thinking adding namespaces to runtime might be a logical first
step. That would allow attributes to add logic like internal
properties for a certain namespace. Makes me wonder why that never
happened for this PR: https://github.com/php/php-src/pull/947
Asking anyone on the list, would it be possible to make an opcode out
of "namespace Foo\Bar" and letting it set a flag in the runtime system
when run? Then make the flag accessible with a constant like
NAMESPACE_RUNTIME or whatever.
Ooooh, there's an interesting case with "goto" and namespace
visibility here:
https://github.com/php/php-src/pull/947#issuecomment-419821198
That would mean every zval has to carry in which namespace it was
defined inside. I wonder how that would affect the memory footprint of
PHP.
If we had something like that, then perhaps there could be a
"delegatable" visibility, which was the equivalent for "protected", but
when delegating rather than inheriting, e.g.class Foo {
private int $a = 1;
delegatable int $b = 2;
}class Bar {
private delegate Foo $foo;
public function __construct(Foo $foo) {
$this->foo = $foo;
echo $this->a; // Error: can't access private property of a
different class
echo $this->b; // allowed: access is to a "delegatable"
property, in a "delegated" context
}
}
The Foo class has to decide who to give access to, otherwise it's the
same as public access.
Olle
2021-08-26 22:20 GMT+02:00, Olle Härstedt olleharstedt@gmail.com:
2021-08-26 20:30 GMT+02:00, Rowan Tommins rowan.collins@gmail.com:
Don't know if this already exists, but maybe we could consider
brainstorming or gather alternatives for different types of
encapsulation when using composition?Although not quite equivalent to your suggestions, these threads on
making delegation to an encapsulated object might be interesting to you:Right, there are indeed a bunch of attempts and suggestions, some
downvoted, others abandoned.I'm thinking adding namespaces to runtime might be a logical first
step. That would allow attributes to add logic like internal
properties for a certain namespace. Makes me wonder why that never
happened for this PR: https://github.com/php/php-src/pull/947Asking anyone on the list, would it be possible to make an opcode out
of "namespace Foo\Bar" and letting it set a flag in the runtime system
when run? Then make the flag accessible with a constant like
NAMESPACE_RUNTIME or whatever.Ooooh, there's an interesting case with "goto" and namespace
visibility here:
https://github.com/php/php-src/pull/947#issuecomment-419821198That would mean every zval has to carry in which namespace it was
defined inside. I wonder how that would affect the memory footprint of
PHP.If we had something like that, then perhaps there could be a
"delegatable" visibility, which was the equivalent for "protected", but
when delegating rather than inheriting, e.g.class Foo {
private int $a = 1;
delegatable int $b = 2;
}class Bar {
private delegate Foo $foo;
public function __construct(Foo $foo) {
$this->foo = $foo;
echo $this->a; // Error: can't access private property of a
different class
echo $this->b; // allowed: access is to a "delegatable"
property, in a "delegated" context
}
}The Foo class has to decide who to give access to, otherwise it's the
same as public access.Olle
Whops, Nikic already answered this in 2016:
The goto here will jump from code in namespace A to code in namespace B without "directly" crossing the boundary. For this reason just inserting opcodes when a namespace starts or ends will quite cut it.
https://github.com/php/php-src/pull/947#issuecomment-224934615
So, deprecate goto when...?
Olle
The Foo class has to decide who to give access to, otherwise it's the
same as public access.
It does decide who has access: any class that declares it as "delegated". In exactly the same way, "protected" gives access to any class that declares it as a "parent", and namespace visibility would give access to any class that declares itself in the same namespace.
None of those actually limit access to a named list of classes, but all of them document an intended use, and catch mistakes where that intent isn't followed.
Regards,
--
Rowan Tommins
[IMSoP]
On Fri, Aug 27, 2021 at 12:53 AM Rowan Tommins rowan.collins@gmail.com
wrote:
On 26 August 2021 21:20:35 BST, "Olle Härstedt" olleharstedt@gmail.com
wrote:The Foo class has to decide who to give access to, otherwise it's the
same as public access.It does decide who has access: any class that declares it as "delegated".
In exactly the same way, "protected" gives access to any class that
declares it as a "parent", and namespace visibility would give access to
any class that declares itself in the same namespace.None of those actually limit access to a named list of classes, but all of
them document an intended use, and catch mistakes where that intent isn't
followed.
Well I wouldn't say they're quite the same. You can definitely limit the
usages more with the "private" keyword. Or with declaring the class "final"
for "protected" visibility, not allowing a "parent".
I think something along these lines was asked. To have a way to decide the
limitation by the original library, not by the one that decides to use it.
Now... I also think that we might not necessarily need this.
So far it looks like @internal works well enough.
If a library is marking a class or method with @internal, it means it is
not part of the public API and they might change it in the future.
Static tool analysis like phpstan and psalm produce errors for using it.
Also editors will warn you if you are using it.
For all purposes you should treat it as private.
If we really want the PHP runtime to produce a warning/error, even if this
check makes sense mostly statically, we can think about a similar metadata
with attributes with something like
#[PackageInternal(namespacePrefix: 'Acme\Foo')]
that can be applied to methods, classes and even properties and would
trigger those warnings/errors when used from another namespace.
This leaves the decision point on the library authors, not on the users of
it.
Regards,
Alex
Regards,
--
Rowan Tommins
[IMSoP]--
To unsubscribe, visit: https://www.php.net/unsub.php
Don't know if this already exists, but maybe we could consider
brainstorming or gather alternatives for different types of
encapsulation when using composition? Currently, encapsulation like
private and protected is only possible when using inheritance - when
using composition, that is, class A is using (aggregating) class B,
your only alternative is public, so full visibility (or
getters/setters, which also is full visibility, since you can't limit
who's using those methods).
When you say "composition" I think of traits, but it does not appear that is what you mean?
I have often wanted to be able to create a trait-private property so that I could write a trait
and be confident that the classes that use it don't start using its internal properties and tie my hands in terms of being able to upgrade the trait to better internal approaches as I evolve the trait.
Would trait-private address any part of what you need? Or would other trait-specific visibility address what you need?
And if not, what about traits makes them not-applicable here? Maybe we could also address that?
-Mike