Public properties and functions are accessible everywhere. While it is agreed by everyone that public functions should be absolutely limited to only what is needed, debate still rages on as to when, if ever, it is advisable to make a class’ properties public.

There are three major disciplines:

  • Nothing Public — also known as the ”Getters and Setters” proponents.
  • Everything Public — also known as ”Slutty Types” proponents.
  • Real Property Encapsulation — also known as the ”Isolationists”.

Nothing Public

For the better part of the last twenty years, the dominant position among OOP practitioners has been to never make class properties public. Their arguments almost always revolve around the potential for abuse by developers who do not understand how to create and use a well-encapsulated class Advanced Topic.

Invariably, their arguments almost always boil down to the hypothesis that if one allows uncontrolled, outside-the-class manipulation of a property, that encapsulation will be invariably broken, unscrupulous and/or inexperienced developers will commit the most dastardly logic flaws possible, hell will freeze over, and the oceans will turn to blood. Many books, professors and others assert that public class variables are akin to global variables and should be avoided at all costs (”Pro Tip: Why You Should Not Use Global Variables”).

Their solution? Use pairs of functions, called getters and setters, for every private property that needs to be read or modified by other parts of the application.

Public Nothing Advantages

Using getters and setters

  • theoretically aids encapsulation by preventing incompetent programmers from inappropriately accessing and modifying core class properties (”This claim is debunked below.”),
  • adds the ability to validate a property before it is saved,
  • mitigates the need to modify a project’s codebase, if the validation of properties becomes necessary in the future,
  • enables the hiding of implementation details. E.g. getName() may one day return $this->name and the next day return “{$this->firstName} {$this->lastName}”.

Public Nothing Disadvantages and Rebuttal ==

  • It can be said that the ”’Nothing Public”’ proponents are extremely confused: Most getters and setters are virtually the same thing as public properties, except with far more code. Ex: $cars->setTankSize(5) is identical to $cars->tankSize = 5.
  • Getters and setters are almost always reactionary: control and validation are using only added after bugs have been discovered.
  • Well-designed code simply should not need validation code in a setter. Validate it before you set it, and make sure all the setting is done in the class itself.
  • You should ”’not”’ be in the habit of changing implementation details. ”This is one of the most prevalent causes of software bugs.” If you must, use a façade or proxy design pattern. Your old, unmodified code should still work flawlessly, and eventually you’ll be able to refactor away the old code far more easily than if you changed a getter and setter invisibly.
  • If you cannot trust every developer of the system to not screw up the internals of an application, then you have larger problems that cannot be solved by getters and setters anyway.

Example of Nothing Public

class AmericanCar
{
    private $fuelRemaining = 0;
    private $milesDriven = 0;
    private $tankSize = null;

    public function setFuelRemaining($fuelRemaining)
    {
        /* validate */
        if (!is_numeric($fuelRemaining)) { throw new Exception("Invalid remaining fuel amount"); }
        if ($fuelRemaining > $this->tankSize) { throw new Exception("Fuel remaining is more than tank size!"); }

        $this->fuelRemaining = $fuelRemaining;
    }

    public function getFuelRemaining()
    {
        return $this->fuelRemaining;
    }

    public function getMilesDriven()
    {
        return $this->milesDriven;
    }

    public function setMilesDrive($milesDriven)
    {
        $this->milesDriven = $milesDriven;
    }

    public function getTankSize()
    {
        return $this->tankSize;
    }

    public function setTankSize($tankSize)
    {
        $this->tankSize = $tankSize;
    }

    public function calculateMileage()
    {
        $gasUsed = $this->tankSize - $this->fuelRemaining;

        return $this->milesDriven / $gasUsed;
    }

    public function drive($miles)
    {
        $this->fuelRemaining -= Engine::getFuelConsumed();
        $this->milesDriven += $miles;
    }
}

$car = new AmericanCar();
/* Refuel car */
$car->setTankSize(10.5);

// If we refueld with 10.8 gallons, setFuelRemaining would throw an error.
$car->setFuelRemaining(10.5);
$car->drive(10);

$mpg = $car->calculateMileage();

Gosh! That was a lot to type! Well, the fellows at Zend heard everyone’s bickering, but instead of explaining that this is probably not the best system, they instead created a cheat, the ”magic methods””__get()” and ”__set()”, which are the own particular breed of evil.

Everything Public

Public Everything Advantages

  • There’s a lot less to type than Public Nothing (no getters/setters).
  • Enhances encapsulation, as you can keep truly private properties (e.g. $milesDriven) private while exposing public properties.
  • Less chance someone will get confused and make a truly private property public.

Public Everything Disadvantages

However, with Public Everything, you can ”’never”’ safely trust properties. This is where they get the name ”Slutty Types”’.

Taken from The Inquisitive Coder — Davy Brion’s Blog:

Slutty Types are [classes] which:

  • give you access to their privates without too many difficulties
  • don’t really care about your intentions, or if they do, aren’t very clear on that
  • occasionally seem like a good short-term fix
  • can be used in a variety of ways, with different outcomes and none of them are guaranteed
  • can not to be trusted
  • really need to be tested
  • will burn you sooner or later if you’re not careful
  • become even more of a mess during the aging process

Example of Everything Public

class AmericanCar
{
    private $milesDriven = 0;

    public $fuelRemaining = 0;
    public $tankSize = null;

    public function calculateMileage()
    {
        if (!is_numeric($this->tankSize)) { throw new Exception("Invalid tank size"); }
        if (!is_numeric($this->fuelRemaining)) { throw new Exception("Invalid remaining fuel amount"); }
        if ($fuelRemaining > $this->tankSize) { throw new Exception("Fuel remaining is more than tank size!"); }

        $gasUsed = $this->tankSize - $this->fuelRemaining;

        return $this->milesDriven / $gasUsed;
    }

    public function drive($miles)
    {
        $this->fuelRemaining -= Engine::getFuelConsumed();
        $this->milesDriven += $miles;
    }
}

$car = new AmericanCar();
/* Refuel car */
$car->tankSize = 10.5;

$car->fuelRemaining = 10.2;
$car->drive(10);

$mpg = $car->calculateMileage();

See how much more concise that is? But what if

$car->fuelRemaining = 20

? Nothing, until we got to

$car->calculateMileage()

, which is all fine and dandy, until you add another function in between, say

$car->calculateMaxDistance()

.