I’m still working on the next article in my series looking at PHP on servers, so in the mean time, check out this simple way to emulate Ruby’s nice way of handling separate getter, setter and state query methods in PHP:

class Example
{
	private $canCache = false;
	private $cacheXml = null;

	function canCache($newState = null)
	{
		// check if we are querying or changing state
                if ($newState === null)
 		{
 			// we are querying
 			return $this->canCache;
 		}

  	 	// if we get here, we are a traditional getter/setter method
 		if ($newState)
 		{
 			$this->canCache = true;
 			$this->cacheXml = $this->_toXml();
 			return true;
 		}
 		else
 		{
 			$this->canCache = false;
 			unset($this->cacheXml);
 			return false;
 		}
 	}
}

$exObj = new Example();
$exObj->canCache(true);
if ($exObj->canCache())
{
 	// ... do something here
}

It isn’t as elegant as Ruby, but it does the job, and it means that your classes don’t have to be full of seperate canDoSomething() and isSomethingAllowed() type methods. I think it makes the code that uses the object a little easier to read, and a little more intuitive. YMMV.

13 Comments

  1. Dave says:
    December 10th, 2007 at 1:32 pm

    Except that you are applying a semantic meaning to null data which overrides any previous meaning that null might have had.

    For example consider using this method to return or set an optional string

  2. Mark Twain says:
    December 10th, 2007 at 1:51 pm

    With this apporach it is impossible to set Example::$canCache to null which might be preferable in this situation, but in a more general case it might not. Most of all it seems very inflexible with the if-statements .

    It should also be noted that attr_accessor in Ruby actually creates two methods (def can_do_something; @can_do_something; end and def can_do_something=(value); @can_do_something; end;) analogous to canDoSomething() and isSomethingAllowed() in PHP.

  3. Mark Twain says:
    December 10th, 2007 at 1:53 pm

    Correction of my previous comment:

    can_do_something=(value); @can_do_something; end;

    should be

    can_do_something=(value); @can_do_something = value; end;

  4. Pierre says:
    December 10th, 2007 at 3:26 pm

    I prefer KISS, and that’s not simple. People who see calls to canCache() with false, null or true or no arguments several times in one file on several circumstances would be confused if they don’t know what that method does exactly.

  5. ctx2002 says:
    December 10th, 2007 at 9:42 pm

    this code has many side effect when you passing different value into canCache($newState = null) function.

    i have no ruby experience , but why you want to do many things in one method seems violate an important programming principal. “an function only do one thing and do it well.”

  6. developercast.com » Stuart Herbert’s Blog: Quick Tip: Get, Set and Query in One Method says:
    December 12th, 2007 at 9:17 pm

    [...] his blog, Stuart Herbert has a quick tip showing how to mimic a feature of Ruby on Rails: I’m still working on the next article in [...]

  7. Kirrus says:
    December 14th, 2007 at 10:41 am

    Hello,
    I’ve only worked on php at a low-level (made a website, altered some php code for another), so I’m a bit of a Newbie when it comes to php..

    I hope you don’t mind me asking, but I see this sort of expression a lot nowadays, and was wondering if you could tell me what it means?

    $this->canCache = false;

  8. Kirrus says:
    December 17th, 2007 at 4:27 pm

    As in, what does the “->” do in the statement?
    (Just realised I hadn’t made clear what I was asking :S)

  9. M L says:
    February 21st, 2008 at 4:04 pm

    The null restriction is enough to discourage this method.

    Why not use the member overload methods __get and __set? With reflection, it could be made even more robust by looking up a private get/set method.

  10. Stu says:
    February 21st, 2008 at 5:02 pm

    @M L: __get and __set are great as a general catch-all, but they quickly become unmaintainable if you need different behaviour for each of the attributes that you are hiding.

  11. M L says:
    February 22nd, 2008 at 12:39 am

    Here is what I was getting at:

    class HidingExample
    {
    private $_SomeProperty;

    private function getSomeProperty()
    {
    //custom code…
    return $this->_SomeProperty;
    }

    private function setSomeProperty($value)
    {
    //custom code…
    $this->_SomeProperty = $value;
    }

    public function __get($name)
    {
    $methodName = “get$name”;

    if (method_exists($this, $methodName))
    return $this->$methodName();

    //do catch all stuff here
    }

    public function __set($name, $value)
    {
    $methodName = “set$name”;

    if (method_exists($this, $methodName))
    return $this->$methodName($value);

    //do catch all stuff here
    }
    }

  12. Stu says:
    February 22nd, 2008 at 7:30 am

    @M L: what’s the performance cost of doing it that way? How do you support the tristate syntax I put forward in my original example?

    Best regards,
    Stu

  13. M L says:
    February 22nd, 2008 at 4:15 pm

    It benchmarked at about 3 times longer than your method. Not a nice trade-off for readability. But traditional getX and setX methods were faster than your single method and don’t have the non-null requirement for the property.

    Could you clarify the difference between query and get? It seems to me that your code acts like a getCanCache method when you don’t pass any parameters (or pass null) and acts like a setCanCache when you do.