0

I'm coming from a .Net background and I'm trying to wrap my brain around a programming pattern that I'm used to but in PHP.

I've got a class with an associative array property. I'd like to "elevate" some, but not all, of the associative array keys to class-level properties. In C# I'd normally do something like this:

//C# Code
class MyClass{
    private Dictionary<string, string> attributes = new Dictionary<string,string>();

    //Get/Set the ID from the private store
    public string ID{
        get { return (attributes.ContainsKey("ID") ? attributes["ID"] : "n/a"); }
        set { attributes.Add("ID", value); }
    }
}

This allows my object to control default values for missing properties. In PHP I couldn't find any way to do this directly. My first workaround was to just use functions:

//PHP Code
class MyClass{
  private $attributes = array();

  //Get the ID    
  public function getID(){
    return (array_key_exists('ID', $this->attributes) ? $this->attributes['ID'] : 'n/a');
  }
  //Set the ID
  public function setID($value){
    $this->attributes['ID'] = $value;
  }
}

This works although the calling syntax is slightly different and I've got two methods per property. Also, the code that is consuming these objects is currently inspecting object variables so functions wouldn't be found.

Then I started going down the magic method paths of __set and __get on the object itself and just switch case on the $name that's passed in and setting/getting my local variables. Unfortunately these methods don't get invoked if you modify the underlying array directly.

So my question is, is it possible in PHP to have a class-level property/variable that doesn't get calculated until it gets used?

5
  • 2
    There is no. And I'm not sure I understand why __set/__get don't suit you. Commented Oct 29, 2013 at 21:59
  • It's easy enough, just use a private member with a default value of null, and a public getter that checks if the value of the member is null. If it is, calculate its value and store it in the member before returning it, otherwise, just return it. Pretty standard lazy evaluation pattern. PHP doesn't have the concept of properties though so you'll have to use standard methods for your getter and setter Commented Oct 29, 2013 at 22:23
  • @zerkms __get and __set are pretty nasty. They make code obscure and difficult to follow compared to having explicit getters and setters. Besides, performance is pretty poor compared against explicit getters/setters. Commented Oct 29, 2013 at 22:30
  • Thanks guys. Perf isn't too much of an issue. The getters and setters require that you to never modify the arrays directly (if I understand them correctly). I'm fine with a different pattern, I'm just trying to match what I know to what I can do. Commented Oct 29, 2013 at 22:50
  • @GordonM: I didn't ask about the best practices and performance point of view - OP mentioned that it didn't fit some other requirements which I has asked about. Commented Oct 29, 2013 at 23:12

2 Answers 2

2

PHP doesn't have properties as C# programmers would understand the concept, so you'll have to use methods as the getter and setter, but the principle is exactly the same.

class MyClass {
    private $attributes = array ();

    public function getSomeAttribute () {
        if (!array_key_exists ('SomeAttribute', $this -> attributes)) {
            // Do whatever you need to calculate the value of SomeAttribute here
            $this -> attributes ['SomeAttribute'] = 42;
        }
        return $this -> attributes ['SomeAttribute'];
    }
    // or if you just want a predictable result returned when the value isn't set yet
    public function getSomeAttribute () {
        return array_key_exists ('SomeAttribute', $this -> attributes)?
            $this -> attributes ['SomeAttribute']:
            'n/a';
    }

    public function setSomeAttribute ($value) {
        $this -> attributes ['SomeAttribute'] = $value;
    }
}

You essentially got the basic ideas right with your implementation, but it does mean a lot of "boilerplate" code. In theory you can avoid a lot of that with __get and __set, but I'd strongly advise against those as they can lead to epic amounts of confusion and nasty logical tangles like the "what happens if the value is set within the class instead of from outside?" issue that you've run into.

Sign up to request clarification or add additional context in comments.

3 Comments

PHP does have properties, as $attribute is a property of the object MyClass
@Shamil I mean properties in the sense the OP uses in C# that are essentially invisible to the outside user of the class.
"what happens if the value is set within the class instead of from outside?" --- nothing. It would be the same
0

In PHP You can do something like this:

<?php
class MyClassWithoutParams{
    function test(){
      return $this->greeting . " " . $this->subject;
    }
}

$class = new MyClassWithoutParams();
$class->greeting = "Hello";
$class->subject = "world";

echo $class->greeting . " " . $class->subject . "<br />"; //Hello World
echo $class->test(). "<br />"; //Hello World
?>

You don't need to define any property, getter or setter to use properties. Of course it is better to do so, because it makes the code easier to read and allows autocomplete-functions of eclipse (or whatever IDE) to understand what you are looking for - but if you are just looking for the laziest way to implement an entity, that would work as well.

A more common approach would be to use an associative array instead of a class - but on arrays you cant define methods.

<?php
$array = new array();

$array["greeting"] = "Hello";
$array["subject"] = "World";

echo $array["greeting"] . " " . $array["subject"]; //Output: Hello World
?>

2 Comments

But for both of these routes you're relying on the consumer of the object to handle missing values accordingly instead of the object itself, right?
Yes. if you want to handle that inside the class, you need to have getter/setter pairs (or a function that resolves your (internal) array to attributes, when using a param based generic getter/setter). In both cases you cannot achieve it with "the same" access call (prefixed with get or set would be required.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.