Roland Franssen
Roland Franssen

Reputation: 1108

use class getters or class properties? (PHP)

What's considered best practice writing OOP classes when it comes to using a property internally.

Consider the following class;

<?php
Class Foo
{
    /**
     * @var null|string
     */
    protected $_foo;

    /**
     * @return null|string
     */
    public function getFoo()
    {
        return $this->_foo;
    }

    protected function _doSomething()
    {
        $foo    = $this->_foo;
        $result = null;
        // ...
        return $result;
    }
}

As you see im using the property _foo in _doSomething() although a sub-class could override getFoo() returning a computed value not stored back into _foo; that's a flaw.

What should i do?

  1. mark getter as final, use property internally (no extra function calls, enforce end-developer to use _foo as a property since it's protected)
  2. use getFoo() internally, mark _foo as private (extra function calls)

Both options are waterproof however im seriously concerned about all the extra function calls so i tend to use option 1 however option 2 would be more "true OOP" imho.

Also read http://fabien.potencier.org/article/47/pragmatism-over-theory-protected-vs-private which also suggests option 2 :/

Another related question; If a property has a setter should that property be private, enforcing end-developers to use it in sub-classes or should it be an unwritten rule the programmer is responsible to set a valid property value?

Upvotes: 7

Views: 1574

Answers (1)

GordonM
GordonM

Reputation: 31730

The second approach is, as you say, the more correct way according to OOP. You're also right that there is more cost in terms of CPU cycles in calling a method than in accessing a property as a variable. However, this will in most cases fall into the category of a micro-optimization. It won't have a noticeable effect on performance except if the value in question is being used heavily (such as in the innermost part of a loop). Best practice tends to favour the correct over the most performant unless the performance is genuinely suffering as a result of it.

For simple variables the use of a getter internally is not immediately obvious, but the technique comes into its own if you're dealing with a property that gets populated from an external data source such as a database. Using the getter allows you to fetch the data from the DB in a lazy way, ie on demand and not before it's needed. For example:

class Foo
{
    // All non-relevent code omitted
    protected $data = NULL;

    public class getData ()
    {
        // Initialize the data property
        $this -> data = array ();
        // Populate the data property with a DB query
        $query = $this -> db -> prepare ('SELECT * FROM footable;');
        if ($query -> execute ())
        {
            $this -> data = $query -> fetchAll ();
        }
        return ($this -> data);
    }

    public function doSomethingWithData ()
    {
        $this -> getData ()
        foreach ($this -> data as $row)
        {
            // Do processing here
        }
    }
}

Now with this approach, every time you call doSomethingWithData the result is a call to getData, which in turn does a database query. This is wasteful. Now consider the following similar class:

class Bar
{
    // All non-relevent code omitted
    protected $data = NULL;

    public class getData ()
    {
        // Only run the enclosed if the data property isn't initialized
        if (is_null ($this -> data))
        {
            // Initialize the data property
            $this -> data = array ();
            // Populate the data property with a DB query
            $query = $this -> db -> prepare ('SELECT * FROM footable;');
            if ($query -> execute ())
            {
                $this -> data = $query -> fetchAll ();
            }
        }
        return ($this -> data);
    }

    public function doSomethingWithData ()
    {
        foreach ($this -> getData () as $row)
        {
            // Do processing
        }
    }
}

In this version, you can call doSomethingWithData (and indeed getData) as often as you like, you will never trigger more than one database lookup. Furthermore, if getData and doSomethingWithData are never called, no database lookup is ever done. This will result in a big performance win, as database lookups are expensive and should be avoided where possible.

It does lead to some problems if you're working in a class that can update the database, but it's not hard to work around. If a class makes updates to its state, then your setters can simply be coded so that they null their associated state on success. That way, the data will be refreshed from the database the next time a getter is called.

Upvotes: 5

Related Questions