A class defines a type with state and behavior. An object is one instance of that class with its own property values.
Use a class when data and rules belong together. A few unrelated utility functions do not become better merely because they are placed in a class.
A constructor establishes initial state. Visibility expresses which operations are public and which details remain internal to the class.
<?php
declare(strict_types=1);
final class CourseProgress
{
public function __construct(
public readonly string $course,
private int $completed = 0
) {
}
public function completeLesson(): void
{
$this->completed++;
}
public function summary(): string
{
return "{$this->course}: {$this->completed} complete";
}
}
new calls the constructor and returns an object. The -> operator accesses instance methods and public properties. Each object keeps separate instance state.
<?php
$php = new CourseProgress('PHP');
$sql = new CourseProgress('SQL');
$php->completeLesson();
$php->completeLesson();
$sql->completeLesson();
echo $php->summary() . PHP_EOL;
echo $sql->summary();
PHP: 2 complete
SQL: 1 complete
Private state can change only through class methods, which provides one place to enforce rules. A getter for every property is not meaningful encapsulation if callers can still create invalid combinations.
Prefer behavior names such as completeLesson() or changeEmail() over generic setCompleted() when a rule belongs to the operation.
| Situation | Likely choice |
|---|---|
| Stateless calculation | Function |
| One data record with no behavior | Array or small value object |
| State with invariants and operations | Class |
| Several interchangeable implementations | Interface plus classes |
Two objects created from the same class have separate identity even when their properties currently match. The === operator checks whether two variables reference the same object; == compares properties according to PHP object comparison rules. Domain equality is often clearer as a named method that compares the fields the business considers significant.
A constructor should reject an impossible starting state. Every public method should preserve the same invariant afterward. Readonly properties are useful for values that must not be reassigned after initialization, but a readonly property that refers to an object does not make that nested object deeply immutable.
0 of 2 checked
Try this next
0 of 2 completed
Explore 500+ free tutorials across 20+ languages and frameworks.