Lesson 76 of 100
Classes15 min5 checksPHP 8.5

Static Methods and Class Constants in PHP 8.4

Learn when static methods and class constants make good stateless helpers, and why persistent mutable static properties are risky.

01 · THE IDEAREAD → TRY → LEARN

Why static members exist

Most class members belong to an object. A normal method can read $this, so its result may depend on that object's properties. A static member belongs to the class itself. You can reach it with the scope resolution operator :: without creating an object first.

That makes static methods a good fit for small helpers that only need their arguments, such as converting units, formatting a label, or classifying a number. Class constants fit alongside them. They give a name to a fixed value that the class owns, so you avoid scattering magic numbers through your code.

A first static method and constant

A static method is declared with the static keyword. Because no object is involved, $this does not exist inside it. Inside the class, use self::NAME to read a constant. From outside, write ClassName::NAME.

Example
<?php
class Temperature
{
    public const FREEZING_C = 0;

    public static function toFahrenheit(float $celsius): float
    {
        return $celsius * 9 / 5 + 32;
    }
}

echo Temperature::toFahrenheit(100), "\n";
echo Temperature::toFahrenheit(Temperature::FREEZING_C), "\n";
Output
212
32

The method does not remember anything between calls. Given the same input, it returns the same output. That predictability is what you want from a helper.

Static methods as callables

Static methods work well with functions that accept callbacks. The first-class callable syntax Label::wrap(...) turns a static method into a closure without writing a string or an array callable by hand.

Example
<?php
final class Label
{
    public static function wrap(string $text): string
    {
        return '[' . $text . ']';
    }
}

$wrapped = array_map(Label::wrap(...), ['a', 'b']);
echo json_encode($wrapped, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
["[a]","[b]"]

Because wrap() depends only on its argument, you can reuse it anywhere and test it with a handful of inputs.

The hazard: persistent mutable statics

A static property is shared by the whole program for the life of the request. If a static method changes it, every later call can behave differently. The result depends on hidden history.

Example
<?php
class Counter
{
    public static int $calls = 0;

    public static function next(): int
    {
        self::$calls++;
        return self::$calls;
    }
}

echo Counter::next(), "\n";
echo Counter::next(), "\n";
Output
1
2

The same call Counter::next() gave two different answers. This is effectively a global variable with a class name. Tests that call it can pass or fail depending on what ran earlier. Code in unrelated places can also change the value without you noticing.

A safer design passes state in and returns new state out. A method such as next(int $current): int would be stateless, and the caller owns the value. Reserve static properties for rare cases, such as caches that cannot change observable results.

Rules of thumb

  • Use a static method when the work needs only its parameters and constants.
  • Use an instance method when behavior depends on object state.
  • Use self::CONSTANT for values the class owns, and give each one a meaningful name.
  • Do not call $this in a static method. It is not available.
  • Calling a non-static method statically throws an Error in PHP 8.
  • Keep static methods deterministic. Avoid reading clocks, randomness or hidden static properties.
  • Remember that self:: refers to the class where the code was written, while static:: is for late static binding. That is a separate topic you will meet with inheritance.

Worked plan for the exercise

You will build a small ScoreGrader class. It holds three threshold constants and one static method, grade(int $score): string. Then you will write a plain function, gradeScores(array $scores): array, that maps each score to a letter in the original order.

  1. Check the highest threshold first: a score at or above A_MIN is A.
  2. Check B_MIN, then C_MIN, in descending order.
  3. Return F when no threshold matches.
  4. Pay attention to the boundaries. A score of exactly 90 must be an A, so use >= rather than >.
  5. Loop over the input and call ScoreGrader::grade() for each value. An empty array should give an empty array.

No static property is needed, so the helper stays stateless and every call is independent.

Recap

Static methods and class constants let a class offer helpers without an object. They shine when they are stateless, deterministic and named well. Avoid persistent mutable static properties, because they create hidden shared state that makes code harder to reason about and test.

Before you start

Functions · Arrays · Types

You’ll practice
  • Write stateless static helpers; avoid persistent mutable statics
  • Implement and test a deterministic PHP 8.4 function
Your task

Make gradeScores() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.