Jeppe Strøm
Jeppe Strøm

Reputation: 543

get set properties in php

I'm from the C# environment and I'm starting to learn PHP in school. I'm used to set my properties in C# like this.

public int ID { get; set; }

What's the equivalent to this in php?

Thanks.

Upvotes: 32

Views: 75881

Answers (10)

andreas
andreas

Reputation: 8004

As of PHP 8.4 in 2025, we are finally able to use so-called property hooks (https://wiki.php.net/rfc/property-hooks).

Your equivalent to the C# code looks as follows:

public int $ID {
    get {
        return $this->ID;
    }
    set(int $value) {
        $this->ID = $value;
    }
}

It is possible to use the arrow notation as follows:

public int $ID {
    get => $this->ID;
    set => $value
}

You can leave out the getter or the setter if you like, it will then be replaced by PHP's default read/write behavior. Obviously, this simple example is the same as:

public int $ID;

It is also possible to create virtual properties. They are not allowed to have a setter:

public int $ID {
    get => 12345;
}

Note: Always sure that the types returned by the getter and received by the setter match the type of the property.

Upvotes: 0

George John
George John

Reputation: 2767

class MyClass
{
    private $name = null;

    public function __construct($name = null)
    {
        $this->name = $name;
    }

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

    public function __get($name)
    {
        if (property_exists($this, $name)) {
            return $this->$name;
        }
        return null;
    }
}

Upvotes: 0

Roger
Roger

Reputation: 101

I like to use this pattern:

class foo 
{
    //just add p as prefix to be different than method name.
    protected $pData;
    
    public funtion __construct() {} 
    public funtion __destruct() {}
    public funtion __clone() {}

    public function Data($value == "") 
    {
        if ($value != "") {
            $this->pData = $value;
        }
        return $this->pData;
    }
}
$myVar = new foo();
//for SET
$myVar->Data("A Value");
//for GET
$item = $myVar->Data();

Upvotes: 0

vagustolga
vagustolga

Reputation: 39

this is PHP ; you don't need get set

class MyClass {
  public $ID;
}

$object = new MyClass();
$object->ID = 'foo';
echo $object->ID;

will work

Upvotes: -5

rmblstrp
rmblstrp

Reputation: 374

I know I am a bit late to the party on this question, but I had the same question/thought myself. As a C# developer who does PHP, when the job requires, I want to have a simple way to create properties just I would be able to in C#.

I whipped up a first draft this afternoon which allows you to create the backing fields and specify their accessors or have pure accessors with no backing field. I will update my answer as the code evolves and provide a link when I get it to the state where it can be imported as a composer package.

For simplicity, I created the functionality as a PHP trait so you can drop it in to any class you want instead of having to extend a base class. Eventually I hope to extend this functionality to discern between external public calls to the properties and protected/private calls.

Here is the code for the trait itself:

trait PropertyAccessorTrait
{
    private static $__propertyAccessors = [];

    /* @property string $__propertyPrefix */

    public function __get($name)
    {
        $this->__populatePropertyAcessors($name);

        return $this->__performGet($name);
    }

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

        $this->__performSet($name, $value);
    }

    public function __isset($name)
    {
        // TODO: Implement __isset() method.
    }

    public function __unset($name)
    {
        // TODO: Implement __unset() method.
    }

    protected function __getBackingFieldName($name)
    {
        if (property_exists(self::class, '__propertyPrefix')) {
            $prefix = $this->__propertyPrefix;
        } else {
            $prefix = '';
        }

        return $prefix . $name;
    }

    protected function __canget($name)
    {
        $accessors = $this->__getPropertyAccessors($name);

        return $accessors !== null && isset($accessors['get']);
    }

    protected function __canset($name)
    {
        $accessors = $this->__getPropertyAccessors($name);

        return $accessors !== null && isset($accessors['set']);
    }

    protected function __performGet($name)
    {
        if (!$this->__canget($name)) {
            throw new \Exception('Getter not allowed for property: ' . $name);
        }

        $accessors = $this->__getPropertyAccessors($name)['get'];

        /* @var \ReflectionMethod $method */
        $method = $accessors['method'];

        if (!empty($method)) {
            return $method->invoke($this);
        }

        return $this->{$this->__getBackingFieldName($name)};
    }

    protected function __performSet($name, $value)
    {
        if (!$this->__canset($name)) {
            throw new \Exception('Setter not allowed for property: ' . $name);
        }

        $accessors = $this->__getPropertyAccessors($name)['set'];

        /* @var \ReflectionMethod $method */
        $method = $accessors['method'];

        if (!empty($method)) {
            return $method->invoke($this, $value);
        }

        $this->{$this->__getBackingFieldName($name)} = $value;
    }

    protected function __getPropertyAccessors($name)
    {
        return isset(self::$__propertyAccessors[$name])
            ? self::$__propertyAccessors[$name]
            : null
            ;
    }

    protected function __getAccessorsFromDocBlock($docblock)
    {
        $accessors = [];

        if (!empty(trim($docblock))) {
            $doclines = null;

            if (!empty($docblock)) {
                $doclines = explode("\n", $docblock);
            }

            if (!empty($doclines)) {
                foreach ($doclines as $line) {
                    if (preg_match('/@(get|set)\\s+(public|private|protected)/', $line, $matches)) {
                        $accessors[$matches[1]]['visibility'] = $matches[2];
                    }
                }
            }
        }

        return $accessors;
    }

    protected function __populatePropertyAcessors($name)
    {
        if ($this->__getPropertyAccessors($name) !== null) return;

        try {
            $property = new \ReflectionProperty(self::class, $this->__getBackingFieldName($name));
        } catch (\ReflectionException $ex) {
            $property = null;
        }

        $accessors = [];

        if ($property != null) {
            $accessors = $this->__getAccessorsFromDocBlock($property->getDocComment());
        }

        try {
            $methodName = 'get' . ucfirst($name);
            $method = new \ReflectionMethod(self::class, $methodName);
            $method->setAccessible(true);
            $accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment()));
        } catch (\ReflectionException $ex) {
            $method = null;
        }


        if ($method !== null || isset($accessors['get'])) {
            $accessors['get']['method'] = $method;
        }

        try {
            $methodName = 'set' . ucfirst($name);
            $method = new \ReflectionMethod(self::class, $methodName);
            $method->setAccessible(true);
            $accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment()));
        } catch (\ReflectionException $ex) {
            $method = null;
        }

        if ($method !== null || isset($accessors['set'])) {
            $accessors['set']['method'] = $method;
        }

        self::$__propertyAccessors[$name] = $accessors;
    }
}

Here is a quick unit test I created using the Codeception format:

<?php

class PropertyAssesorTraitTestClass
{
    use PropertyAccessorTrait;

    private $__propertyPrefix = '_';

    /**
     * @get public
     * @set public
     */
    private $_integer = 1;

    /**
     * @get public
     */
    private $_getonly = 100;

    /**
     * @set public
     */
    private $_setonly;

    private $_customDoubler;

    private function getCustomDoubler()
    {
        return $this->_customDoubler * 2;
    }

    private function setCustomDoubler($value)
    {
        $this->_customDoubler = $value * 2;
    }

    public $publicField = 1234;

    /**
     * @return int
     * @get public
     */
    private function getPureAccessor()
    {
        return $this->publicField;
    }

    /**
     * @param $value
     * @set public
     */
    private function setPureAccessor($value)
    {
        $this->publicField = $value;
    }

    private $_purePrivate = 256;
}

$I = new UnitTester($scenario);
$I->wantTo('Ensure properties are accessed correctly');

$instance = new PropertyAssesorTraitTestClass();
$I->assertSame(1, $instance->integer);

$instance->integer = 2;
$I->assertSame(2, $instance->integer);

$instance->integer = $instance->integer + 1;
$I->assertSame(3, $instance->integer);

$instance->integer++;
$I->assertSame(4, $instance->integer);

$I->assertSame(100, $instance->getonly);
$I->expectException('Exception', function () use ($instance) { $instance->getonly = 50; });

$instance->setonly = 50;
$I->expectException('Exception', function () use ($instance) { $a = $instance->setonly; });

$instance->customDoubler = 100;
$I->assertSame(400, $instance->customDoubler);

$I->assertSame(1234, $instance->publicField);
$instance->pureAccessor = 1000;
$I->assertSame(1000, $instance->publicField);
$instance->publicField = 1234;
$I->assertSame(1234, $instance->publicField);
$I->assertSame(1234, $instance->pureAccessor);

$I->expectException('Exception', function () use ($instance) { return $instance->purePrivate; });

Upvotes: 1

user2757283
user2757283

Reputation:

private $ID;

public function getsetID($value = NULL)
{
    if ($value === NULL) {
        return $this->ID;
    } else {
        $this->ID = $value;
    }
}

Upvotes: 1

Jaspreet Chahal
Jaspreet Chahal

Reputation: 2750

Mchi is right, but there is another way of doing it by using single function

    private $ID;

public function ID( $value = "" )

{

    if( empty( $value ) )

        return $this->ID;

    else

        $this->ID = $value;

}

But yeah this approach is pretty much inline with what you do in c#. but this is only an alternative

Or try using php's __set and __get in your class more info here

http://php.net/manual/en/language.oop5.overloading.php

Upvotes: 6

Piotr Czyż
Piotr Czyż

Reputation: 2710

Thanks for your answers everyone. It helped me to create something like this:

In my parent class:

public function __get($name){

    if (ObjectHelper::existsMethod($this,$name)){
        return $this->$name();
    }

    return null;
}

public function __set($name, $value){

    if (ObjectHelper::existsMethod($this,$name))
        $this->$name($value);
}

ObjectHelper::existsMethod is a method which just check if given protected method exists.

private $_propertyName = null;

protected function PropertyName($value = ""){

    if (empty($value)) // getter
    {
        if ($this-> _propertyName != null)
            return $this->_propertyName;
    }
    else // setter
    {
        $this-> _propertyName = $value;
    }

    return null;
}

So I can use something like this in any class:

$class = new Class();
$class->PropertyName = "test";
echo $class->PropertyName;

I was inspired by C# :)

What do you think about this, guys?

Here is my ObjectHelper if someone would like to use it:

namespace Helpers;

use ReflectionMethod;

class ObjectHelper {

public static function existsMethod($obj, $methodName){

    $methods = self::getMethods($obj);

    $neededObject = array_filter(
        $methods,
        function ($e) use($methodName) {
            return $e->Name == $methodName;
         }
    );

    if (is_array($neededObject))
        return true;

    return false;
}

public static function getMethods($obj){

    $var = new \ReflectionClass($obj);

    return $var->getMethods(ReflectionMethod::IS_PROTECTED);
}
}

Upvotes: 9

user3397560
user3397560

Reputation: 51

Another exampled using Variable function name

class MyClass {

  private $ID;
  protected $ID2;

  private function setID($ID) {
    $this->ID = $ID;
  }
  private function getID() {
    return $this->ID;
  }
  private function setID2($ID2) {
    $this->ID2 = $ID2;
  }

  private function getID2() {
    return $this->ID2;
  }
  public function __set($name,$value) {
    $functionname='set'.$name;
    return $this->$functionname($value);
  }
  public function __get($name) {
    $functionname='get'.$name;
    return $this->$functionname();
  }

}


$object = new MyClass();
$object->ID = 'foo'; //setID('foo') will be called
$object->ID2 = 'bar'; //setID2('bar') will be called

Upvotes: 5

Mchl
Mchl

Reputation: 62377

There is none, although there are some proposals for implementing that in future versions. For now you unfortunately need to declare all getters and setters by hand.

private $ID;

public function setID($ID) {
  $this->ID = $ID;
}

public function getID() {
  return $this->ID;
}

for some magic (PHP likes magic), you can look up __set and __get magic methods.

Example

class MyClass {

  private $ID;

  private function setID($ID) {
    $this->ID = $ID;
  }

  private function getID() {
    return $this->ID;
  }


  public function __set($name,$value) {
    switch($name) { //this is kind of silly example, bt shows the idea
      case 'ID': 
        return $this->setID($value);
    }
  }

  public function __get($name) {
    switch($name) {
      case 'ID': 
        return $this->getID();
    }
  }

}


$object = new MyClass();
$object->ID = 'foo'; //setID('foo') will be called

Upvotes: 34

Related Questions