PHP / CHAPTER 08

Classes and invariant-preserving operations

Intermediate

Encapsulate state behind meaningful, validated methods.

By the end of this chapter

  • Establish valid constructor state
  • Expose queries separately from commands
  • Reject changes before mutation

The idea

A class can protect its representation with private properties and expose operations that make sense for its domain. A constructor establishes the initial invariant, such as a nonnegative remaining capacity. Public methods must preserve the same rule after every successful or rejected operation. Providing an unrestricted setter can undermine the constructor’s work by letting callers install invalid state later.

Choose failure behavior before changing fields. A take method can return false for a negative or oversized request and leave the object unchanged; a query reports the remaining amount without modifying it. Typed properties and parameters help express accepted kinds of values, but type declarations alone do not express every range or relationship. Validate those semantic constraints explicitly and test both returned outcomes and resulting state.

PHP example
<?php
class Stock {private int $count;public function __construct(int $n){if($n<0)throw new InvalidArgumentException();$this->count=$n;}public function take(int $n):bool{if($n<0||$n>$this->count)return false;$this->count-=$n;return true;}public function count():int{return $this->count;}}
$s=new Stock(4);var_export($s->take(5));echo "\n",$s->count(),"\n";
Result
false
4

The failed request is rejected before subtraction, so the query still reports four.

Try it yourself

Implement a reading goal that caps accepted progress and rejects negative additions without mutation.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Which API best protects a nonnegative capacity?