Skip to content

Functions and classes in PHP

A function gives a piece of work a name. A class bundles data and the work done with it. This text shows both in working code, and the point at which you switch from one to the other.

  • ForBeginners, the technically curious
  • LevelBasic to intermediate
  • Reading timeabout 8 minutes
  • Updated16 October 2026

What you will learn

  • How a function is structured and what type declarations do
  • What named arguments and default values save you
  • How a class holds data and behaviour together
  • How to tell that a function should become a class

A function gives work a name

As soon as the same sequence with the same business meaning appears in two or three places, it belongs in a function. The gain is not the saved line, but that a change now happens in only one place. The gain is not the saved line, but that a change now happens in only one place.

PHPtested with PHP 8.3
declare(strict_types=1); function bruttoPreis(int $nettoCent, float $satz = 0.19): int { return (int) round($nettoCent * (1 + $satz)); } echo bruttoPreis(1000); // 1190 echo bruttoPreis(1000, 0.07); // 1070 echo bruttoPreis(nettoCent: 1000); // named argument
The type declarations on the left and after the colon are promises. If strict_types is active in the calling file, PHP rejects a string for an int parameter instead of converting it. Without the declaration in the calling file, "1000" silently becomes 1000.

Note the amount in whole cents. This is no coincidence: floating-point numbers are unsuitable for money, and a function signature is the best place to make this decision visible.

A class holds together what belongs together

When several functions keep passing around the same values, those values and the functions belong together. That is exactly what a class is.

PHPtested with PHP 8.3
declare(strict_types=1);

final class Auftragsposition
{
    public function __construct(
        private readonly string $artikel,
        private readonly int $menge,
        private readonly int $einzelpreisCent,
    ) {
        if ($menge < 1) {
            throw new InvalidArgumentException('Menge muss mindestens 1 sein.');
        }
    }

    public function gesamtCent(): int
    {
        return $this->menge * $this->einzelpreisCent;
    }

    public function beschreibung(): string
    {
        return sprintf('%dx %s', $this->menge, $this->artikel);
    }
}

$pos = new Auftragsposition('Dichtung', 3, 450);
echo $pos->beschreibung();   // 3x Dichtung
echo $pos->gesamtCent();     // 1350
The short syntax in the constructor declares properties directly. readonly means: immutable after creation.

Three things happen here that a loose function cannot do. The check in the constructor ensures that no position with a quantity of 0 can exist — no later caller has to check that any more. The data cannot be changed from outside. And the name says what it is about.

When a function becomes a class

Three signs that point towards a class; usually one is enough to seriously consider it:

  • The same three or four values travel through several functions.
  • A function needs a state that persists between calls.
  • There are invalid combinations of the values that nobody must be allowed to create.

The reverse case occurs just as often: a class with a single method and no properties is really a function. That is not a mistake, just unnecessary effort when reading.

An example from practice

What type declarations really do in operation

PHPtested with PHP 8.3
declare(strict_types=1);

function tageBis(DateTimeImmutable $ziel): int
{
    $heute = new DateTimeImmutable('today');
    return (int) $heute->diff($ziel)->format('%r%a');
}

// Correct:
echo tageBis(new DateTimeImmutable('2027-01-01'));

// Wrong — noticed IMMEDIATELY, not only in the result:
// echo tageBis('2027-01-01');
// TypeError: tageBis(): Argument #1 ($ziel) must be of type
// DateTimeImmutable, string given
Without a type declaration the function would have accepted the string and only failed when calling diff() — with a message that points to the wrong place.

That is the real benefit: the error appears where it was made, not three levels further down. In a grown application that is the difference between five minutes and half a day.

Common misconceptions

A class pays off from the moment invalid states would be possible. That is often the case as early as the second development step.

The difference is not measurable in practice. What they cost is typing; what they save is debugging.

It means that this object does not change. For a new value you create a new object — that is intended and makes side effects impossible.

Further sources

About the author

Fabian Lücker is managing partner of PHP-Schmiede KG in Rheine and has worked in software development since 2007. He is responsible for development and design, and writes here about decisions that come up in real projects.