Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you access private methods or attributes from outside the type they belong to?

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?

like image 259
Kaiepi Avatar asked Sep 17 '19 22:09

Kaiepi


People also ask

Which method can be used to access private variables outside the class?

We have used the getter and setter method to access the private variables.

How do you access the private property and methods outside the class using class object in PHP?

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.

How do you access private members outside class in python?

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.


1 Answers

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.

Calling private methods

Trusting other classes

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"
# 

Using the ^find_private_method meta-method

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
}

Getting and setting private attributes

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"
#

F.A.Q.

What does { ... } do?

This stubs a package, allowing you to declare it before you actually define it.

What does 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.

Why are grammars classes by extension?

Grammars use Metamodel::GrammarHOW, which is a subclass of Metamodel::ClassHOW.

You say ^find_private_method and ^get_attribute_for_usage take an object as their first parameter, but you omit it in the example. Why?

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.

like image 94
Kaiepi Avatar answered Sep 28 '22 01:09

Kaiepi