In some rare cases where this would actually be acceptable, like in unit tests, you may want to get or set the value of a private attribute, or call a private method of a type where it shouldn't be possible. Is it really impossible? If not, how can you do it?
We have used the getter and setter method to access the private variables.
As you can see, the privateMethod is a private method and if we want to access it outside of the class like so, we would get a fatal error. To get around this, we can use PHP's in-built ReflectionMethod class which can give handlful of information about the class.
In Python, there is no existence of Private methods that cannot be accessed except inside a class. However, to define a private method prefix the member name with the double underscore “__”. Note: The __init__ method is a constructor and runs as soon as an object of a class is instantiated.
There are two ways you can access a private method of a type, and one way to get private attributes. All require meta-programming except for the first way to invoke private methods, whose explanation still involves meta-programming anyway.
As an example, we will be implementing a Hidden
class that hides a value using a private attribute, and a Password
class that uses Hidden
to store a password. Do not copy this example to your own code. This is not how you would reasonably handle passwords; this is solely for example's sake.
Metamodel::Trusting
is the meta-role that implements the behaviour needed for higher-order workings (types of types, or kinds, referred to from hereon out as HOWs) to be able to trust other types. Metamodel::ClassHOW
(classes, and by extension, grammars) is the only HOW that's builtin to Rakudo that does this role.
trusts
is a keyword that can be used from within packages to permit another package to call its private methods (this does not include private attributes). For example, a rough implementation of a password container class could look like this using trusts
:
class Password { ... }
class Hidden {
trusts Password;
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value
}
method !dump(Hidden:D: --> Str:D) {
$!value.perl
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
qc:to/END/;
{self.^name}:
$!value: {$!value!Hidden::dump}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password $insecure .= new: 'qwerty';
$insecure.say;
# OUTPUT:
# Password:
# $!value: "qwerty"
#
Metamodel::PrivateMethodContainer
is a meta-role that implements the behaviour for HOWs that should be able to contain private methods. Metamodel::MethodContainer
and Metamodel::MultiMethodContainer
are the other meta-roles that implement the behaviour for methods, but those won't be discussed here. Metamodel::ClassHOW
(classes, and by extension, grammars), Metamodel::ParametricRoleHOW
and Metamodel::ConcreteRoleHOW
(roles), and Metamodel::EnumHOW
(enums) are the HOWs builtin to Rakudo that do this role. One of Metamodel::PrivateMethodContainer
's methods is find_private_method
, which takes an object and a method name as parameters and either returns Mu
when none is found, or the Method
instance representing the method you're looking up.
The password example can be rewritten not to use the trusts
keyword by removing the line that makes Hidden
trust Password
and changing Password!dump
to this:
method !dump(Password:D: --> Str:D) {
my Method:D $dump = $!value.^find_private_method: 'dump';
qc:to/END/;
{self.^name}:
$!value: {$dump($!value)}
END
}
Metamodel::AttributeContainer
is the meta-role that implements the behaviour for types that should contain attributes. Unlike with methods, this is the only meta-role needed to handle all types of attributes. Of the HOWs builtin to Rakudo, Metamodel::ClassHOW
(classes, and by extension, grammars), Metamodel::ParametricRoleHOW
and Metamodel::ConcreteRoleHOW
(roles), Metamodel::EnumHOW
(enums), and Metamodel::DefiniteHOW
(used internally as the value self
is bound to in accessor methods for public attributes) do this role.
One of the meta-methods Metamodel::AttributeContainer
adds to a HOW is get_attribute_for_usage
, which given an object and an attribute name, throws if no attribute is found, otherwise returns the Attribute
instance representing the attribute you're looking up.
Attribute
is how attributes are stored internally by Rakudo. The two methods of Attribute
we care about here are get_value
, which takes an object that contains the Attribute
instance and returns its value, and set_value
, which takes an object that contains the Attribute
instance and a value, and sets its value.
The password example can be rewritten so Hidden
doesn't implement a dump
private method like so:
class Hidden {
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value;
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
my Attribute:D $value-attr = $!value.^get_attribute_for_usage: '$!value';
my Str:D $password = $value-attr.get_value: $!value;
qc:to/END/;
{self.^name}:
$!value: {$password.perl}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password:D $secure .= new: 'APrettyLongPhrase,DifficultToCrack';
$secure.say;
# OUTPUT:
# Password:
# $!value: "APrettyLongPhrase,DifficultToCrack"
#
{ ... }
do?This stubs a package, allowing you to declare it before you actually define it.
qc:to/END/
do?You've probably seen q:to/END/
before, which allows you to write a multiline string. Adding c
before :to
allows closures to be embedded in the string.
Grammars use Metamodel::GrammarHOW
, which is a subclass of Metamodel::ClassHOW
.
Calling a meta-method on an object passes itself as the first parameter implicitly. If we were calling them directly on the object's HOW, we would be passing the object as the first parameter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With