PHP from the ground up

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.

Part 10 of 13

Advanced17 min readBack to Courses

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.

Output
Click "Run" to execute your code

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 pages property and a method that reports whether the book is long.
  • Make the balance private and expose only deposit(), withdraw() and a formatted balance().
  • 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.

Output
Click "Run" to execute your code

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?