In PHP, what does declaring a method or property static change, and why can a static method not use $this?
answer
- belongs to the class, not an object
- reached with ClassName:: or self::
- no instance, so no $this
- non-static called statically: Error since 8.0
- -> on a static property: notice
basics
~20 sA static method or property belongs to the class, not to an object: it is reached with ClassName:: or self:: and holds one value per class. A static method runs without an instance, so $this does not exist inside it.
solid answer
~40 sDeclaring a member `static` attaches it to the **class**, not to instances. A static property is one slot shared by every caller in the request, read and written with `Counter::$total` or `self::$total`, never with `$obj->total`. A static method is called with `Counter::reset()` and runs with no object bound, so `$this` is undefined inside it; touching it throws `Error: Using $this when not in object context`. The reverse direction is also strict: since PHP 8.0, calling a non-static method as `Foo::bar()` from outside the class throws `Error` instead of a deprecation. You can still call a static method on an instance (`$obj->reset()`), which quietly ignores the object. Static state is hidden global state for the rest of the request, so keep it for pure helpers, named constructors and deliberate per-class caches.
code
php · 23 lines<?php
declare(strict_types=1);
final class Counter
{
private static int $total = 0;
public static function hit(): int
{
return ++self::$total;
}
public function describe(): string
{
return 'hits so far: ' . self::$total;
}
}
Counter::hit();
Counter::hit();
$c = new Counter();
echo $c->describe(), PHP_EOL; // hits so far: 2
echo $c->hit(), PHP_EOL; // 3 (a static method called through an object)go deeper
Recall that static members belong to the class, are reached with ::, and that a static method has no $this.
Explain the Error messages for both mismatches in PHP 8 and why $obj->staticProp does not read the static slot.
Show judgement about static state as hidden global state: per-request under FPM, process-wide in CLI workers and test runs.
Weigh static helpers and named constructors against injected services in a codebase's conventions, and set rules for when static state is acceptable.
## What `static` means on a class member In PHP a class can hold two kinds of members: - **Instance members** belong to one object. Every `new Invoice()` gets its own copy of each instance property, and an instance method runs with `$this` bound to the object it was called on. - **Static members** belong to the **class**. There is one copy of a static property for the class, whatever number of objects exist (including zero), and a static method runs with no object at all. The keyword `static` is reused elsewhere in PHP (static local variables, static closures, and `static::` for late static binding). On a method or property declaration it means only this: *attach the member to the class*. ## How you reach static members Static members are addressed with the **scope resolution operator** `::`, not the object operator `->`: | Access | From outside | From inside the class | |---|---|---| | static property | `Counter::$total` | `self::$total` or `static::$total` | | static method | `Counter::reset()` | `self::reset()` or `static::reset()` | | instance property | `$c->name` | `$this->name` | | instance method | `$c->run()` | `$this->run()` | The class can also come from a string variable: `$cls = 'Counter'; $cls::reset();` works for methods, properties and constants, but the variable cannot hold the keywords `self`, `parent` or `static`. Two mismatches behave differently: 1. **A static method called through an object** (`$counter->reset()` or `$counter::reset()`) is allowed. PHP simply resolves the class of the object and calls the static method; no instance is passed in. 2. **A static property read through `->`** (`$counter->total`) is *not* the static property. PHP emits `Notice: Accessing static property Counter::$total as non static` and then treats it as an undefined instance property, so you get a warning and `null`. ## Why `$this` is missing in a static method `$this` is the object a method was invoked on. A static method is invoked on a class, so there is no object to bind. Referring to `$this` inside one throws `Error` with the message `Using $this when not in object context` when that line runs. A static method that needs object state must receive the object as a parameter, or the method should not be static. The opposite mistake, calling an **instance** method statically from outside the class (`Invoice::total()` where `total()` is not declared `static`), throws `Error: Non-static method Invoice::total() cannot be called statically`. Before PHP 8.0 this was only an `E_DEPRECATED` notice and the method ran with `$this` undefined, which is why old code sometimes "worked". Inside an instance method, `parent::total()` or `self::total()` on a non-static method is different: it is an ordinary instance call that keeps the current `$this`. ## How long static state lives A static property keeps its value until the current script ends. Under PHP-FPM or any web SAPI that means **one request**: the next request starts with the declared default again, because PHP does not keep user objects or variables between requests. In a CLI script, including a worker that loops over jobs, the value lives as long as the process does. That is useful for a deliberate per-class cache and dangerous for anything that should be per-job or per-test. ## When to use static members Static members are reasonable for: - **Named constructors** such as `Money::fromCents(int $cents): static` that build and return an instance. - **Pure helpers** that need no state and belong with a class for discoverability. - **Deliberate per-class caches or registries**, with a clear reset path for tests and workers. They are a poor fit for anything you might need to substitute: a static call such as `Mailer::send()` is hard-wired to one class, cannot be injected or replaced with a test double, and hides a dependency the constructor signature does not show. That is why modern PHP code prefers injected services and keeps static members small. ## Summary - `static` members belong to the class and are reached with `::`. - There is no `$this` in a static method, and using it throws `Error`. - Calling a non-static method statically from outside throws `Error` since PHP 8.0. - A static property lasts for the request under FPM and for the whole process in the CLI.
- In PHP, can a static method be called through an object, and what happens to the object?Yes. `$obj->helper()` and `$obj::helper()` both call a static method: PHP looks up the object's class and invokes the method with no `$this` bound. The object is used only to find the class, and the call runs without a notice.
- In PHP, why are static method calls such as Mailer::send() hard to test?The call names a concrete class at the call site, so nothing can swap in a test double, and any static state it keeps survives from one test to the next inside the same process. An injected instance behind an interface can be replaced per test and has no shared hidden state.
- Under PHP-FPM, does a value written to a static property in one request appear in the next?No. PHP tears down user state at the end of each request, so the next request on the same FPM worker starts with the property's declared default. Only a long-running CLI process keeps static values across units of work.
saying these in an interview costs you the question
- Says $this in a static method refers to the class
- Claims static properties persist between FPM requests
- Thinks calling an instance method statically only raises a deprecation in PHP 8
- Reads a static property with $obj->prop and expects its value
- Uses static methods for every service, making them impossible to replace in tests