user3361120
user3361120

Reputation: 1

Class restrictions: function visibility

I want function explicitSetX( $value){} to be public in a specific task otherwise it should be private. (functionality of private)

(Im not interested in how to write generic setters, this question is about Visibility / Accesibility)

class My_Object{

public function genericArrSetter ( $property, $value ){

    $this->$property = $value;
}
}

class Obj extends My_Object{

private $x;
private $x1;
private $x2;
private $x3;
private $x4;
private $x5;
private $x6;
private $x7;
private $x8;

public function explicitSetX( $value){
    $this->XX = $value; 
}

}

/*
* Below functions run from outside
* I would like to force this behaviour since now
* its possible for others to use myStart. (way of setting)
*/

function myStart (){
// set all data in Obj via generic setter
$obj = new Obj();
$obj-> genericArrSetter("x","value for x");
}

function OtherStart (){
// set all data in Obj via explicit setter
$obj = new Obj();
$obj-> explicitSetX ("value for x");
}

Upvotes: 0

Views: 112

Answers (3)

Bart
Bart

Reputation: 17361

The only feasible way of doing that is by using interfaces and type hinting.

Declare two interfaces:

interface ExplicitSetter {
    public function setExplicitX($value);
}

interface GenericSetter {
    public function setGeneric($x, $value);
}

Your class implements both.

class MyObject implements ExplicitSetter, GenericSetter {
    private $x;

    public function setExplicitX($value) {
        $this->x = $value;
    }

    public function setGeneric($x, $value) {
        $this->x = $value;
    }

    public function __toString() {
        return $this->x;
    }
}

Now you can use type hinting to only expose the interface you are interested in. Trying to use methods not declared by the interface will result in a error.

function useExplicitSetter(ExplicitSetter $setter) {
    $setter->setExplicitX('Hello explicit');
}

function useGenericSetter(GenericSetter $setter) {
    $setter->setGeneric('x', 'Hello generic');
}

Usage:

$obj = new MyObject();

useExplicitSetter($obj);
echo $obj; // Hello explicit

useGenericSetter($obj);
echo $obj; // Hello generic

http://sandbox.onlinephpfunctions.com/code/a7a7a4c4566b529762d4eea755c11a8a9a8734d4

Upvotes: 0

user133408
user133408

Reputation:

This is hakish and not recommended but you could do something like that:

class My_Object{

    // Final will disallow ovverriding
    public final function genericArrSetter ( $property, $value ){

        // Get caller function name
        $callers = debug_backtrace();

        // If it's not myStart do something more or less nasty
        if($callers[1]['function'] !== 'myStart')
        {
            throw new Exception('This is not allowed');
        }
        $this->$property = $value;
    }
}

Example:

function myStart (){
// set all data in Obj via generic setter
$obj = new Obj();
$obj-> genericArrSetter("x","value for x");
}

function OtherStart (){
// set all data in Obj via explicit setter
$obj = new Obj();
$obj-> explicitSetX ("value for x");
// Below will throw exception
$obj-> genericArrSetter("x","value for x");
}

For more robust solutions for caller name see this: How to get name of calling function/method in PHP?

DISCLAIMER: Not tested not recommended

Upvotes: 0

ThreeLLMsInATrenchcoat
ThreeLLMsInATrenchcoat

Reputation: 519

Why declare your attributes private if you want to access them publicly? In any case, you can use the magic method __set() to write data to private/protected variables

public function __set($name, $value){
    $this->$name = $value;
}

Alternatively you could pass the values into the constructor function:

public function __construct($A, $B, $C){
    $this->A = $A;
    $this->B = $B;
    $this->C = $C;
}

You could use an associative array as the argument instead with a foreach loop:

public function __construct(array $args){
    foreach ($args as $key=>$value){
        $this->$key = $value;
    }
}

The constructor's signature should probably be __construct(array $args=null) so you can use new Obj($args) and new Obj() but you will have to check in the body if $args is not empty.

Upvotes: 1

Related Questions