Classes: your own types
Bundling data with the functions that work on it: properties, a constructor, methods, $this, and the visibility keywords that keep the inside inside.
A class, a record that can act
An associative array is a fine record until it needs behaviour — then the behaviour lives beside it as loose functions, and every new copy of the record has to be built by hand. A class puts the data and the work together: properties hold the values, methods are functions that read them, and new makes an instance.
Inside a method, $this is the instance the call arrived on — $this->title reads that object's property. The arrow is the same one used to call functions on values elsewhere; what changes is the target: properties and methods of this object rather than array keys.
Not every record needs a class. A row that is read, printed and thrown away is fine as an array; a class earns its place when the data has rules — a balance that cannot go negative, a title that cannot be empty — or when the same operations are repeated at every call site. The class is a place to put those rules once.
An object is not a nicer array
The two are not interchangeable, and the difference is not only syntax. An array is open: any code can add a key, read one that is not there, or replace a value with a different type. An object closes that surface. Its properties are declared, its methods are the supported way in, and a call to a method that does not exist is an error rather than a key that quietly returns null.
That closure is the point. When a class keeps its balance private and exposes deposit() and withdraw(), every change to the balance goes through the same two checks. The rules live in one place, and a caller cannot forget them by assigning the property directly. An array with the same fields would let every caller write its own version of the rule.
The constructor, and visibility
__construct is the one method whose name is reserved: PHP calls it at new, and it is where an instance is given its starting values. An object that arrives fully formed is an object that cannot be used half-initialised, which is the quiet promise a constructor makes.
public means anyone may read it, protected that subclasses may, private that only this class may — the next chapter is about inheritance, and the visibility keywords are the walls it moves between. Typed properties (public int $pages;) turn a wrong assignment into an error at the line that made it, the same promise function types make.
Methods, and the string form
A method is called with the arrow: $book->summary(). It may use the object's own properties and other methods freely through $this, and it may return anything a function can — including another object.
One special method is worth meeting early: __toString, called automatically whenever the object is used where a string is expected — echo $book;. Give a class a __toString and it becomes printable; a class without one answers echo with an error, which is the honest outcome.
A class with data, behaviour and a printable form
<?php
declare(strict_types=1);
class Book
{
public string $title;
public int $pages;
public bool $read = false;
public function __construct(string $title, int $pages)
{
$this->title = $title;
$this->pages = $pages;
}
public function isLong(): bool
{
return $this->pages > 300;
}
public function summary(): string
{
return $this->title . ' (' . $this->pages . ' pages)';
}
public function __toString(): string
{
return $this->summary();
}
}
$book = new Book('Dune', 412);
echo $book->summary() . PHP_EOL;
echo ($book->isLong() ? 'A long one.' : 'A short one.') . PHP_EOL;
$book->read = true;
echo $book->title . ' read: ' . ($book->read ? 'yes' : 'no') . PHP_EOL;
// __toString is why this line needs no method call.
echo $book . PHP_EOL;A thermostat with a memory
Run it and watch the state change through method calls. Then add a `heat()` method that raises the target by one degree, and a private property that records how many times it was called.
Modern PHP can fold the property declarations into the constructor — public function __construct(private string $title) — which declares, assigns and scopes in one line. The long form above shows what that shorthand is doing; both are ordinary PHP.
Handles, state and visibility
new creates an object and gives back a handle. $second = $first; copies the handle, so both variables point at the same instance and a method called through either changes the one set of properties. clone $first is the operation that makes a second object, and by default it copies the properties shallowly — an object-valued property is still shared.
A typed property with no default starts uninitialised, and reading it before assignment is an error rather than a null. That is why the constructor does the assigning: an instance that exists has been through it once. Visibility is checked from the code doing the reading: private is this class only, protected adds subclasses, and public is everyone.
Three class mistakes are common: writing $title inside a method when $this->title was meant, reaching for a private property from outside the class, and leaving a typed property uninitialised until a later method happens to set it. Use the arrow for properties, keep the constructor responsible for a valid object, and let the error tell you which line crossed a wall.
Try these next
- Add a
pagesproperty and a method that reports whether the book is long. - Make the balance private and expose only
deposit(),withdraw()and a formattedbalance(). - Create two objects from the same class, change one, and print both to show they do not share state.
A savings account
Run the account, then add a `transfer()` method that moves money between two accounts and refuses when the source is short. Print both balances afterwards.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What is $this inside a method?
