Class type declaration
Learn to declare a class type on a constructor parameter so it only accepts that class.
Class type declaration
When you add a type declaration to a constructor parameter, PHP accepts only values of that type. If you write a class name as the type, the parameter only takes instances of that class:
class Engine {
public function start(): void {
echo "Started\n";
}
}
class Car {
public function __construct(Engine $engine) {
$this->engine = $engine;
}
}Here Engine $engine says the parameter may only be an instance of the Engine class. Pass the wrong type and PHP throws a TypeError. This pattern shows explicitly that a class needs another class, and is called dependency injection.
Dependency injection
When a class needs another class to do its job, the cleanest approach is to take that need from the outside as a constructor parameter. This is called dependency injection.
$engine = new Engine();
$car = new Car($engine);Here you first create the Engine instance, then pass it to the Car instance. The Car class does not create the Engine itself; it uses the one given from outside. This makes the code easier to test: you can pass a mock Engine during testing.
Without a type declaration you might accidentally pass an array or a string; the type declaration prevents that mistake.
Exercise Dependency injection with Logger and Service
Define two classes. First Logger: it should have a method public function log(string $message): void that prints echo "[LOG] " . $message . "\n"; in the body. Second Service: declare a property public $logger;; its constructor should take a Logger instance with the signature __construct(Logger $logger) and perform $this->logger = $logger;. Outside the classes call $logger = new Logger();, $service = new Service($logger); and $service->logger->log("Service ready");.
Show example solution
public function log(string $message): void { echo "[LOG] " . $message . "\n"; }. Inside Service: public $logger; and public function __construct(Logger $logger) { $this->logger = $logger; }. Then $logger = new Logger();, $service = new Service($logger); and $service->logger->log("Service ready");.
Example solution:
<?php
// Define the Logger and Service classes
class Logger {
public function log(string $message): void {
echo "[LOG] " . $message . "\n";
}
}
class Service {
public $logger;
public function __construct(Logger $logger) {
$this->logger = $logger;
}
}
$logger = new Logger();
$service = new Service($logger);
$service->logger->log("Service ready");
Test yourself
-
Match the concepts
- Engine $engine
- class-typed parameter
- TypeError
- wrong type error
- new Car($engine)
- passing the dependency
- dependency injection
- external dependency
-
What does the Engine $engine type declaration provide?
- The parameter can take any value
- The parameter can only be an Engine instance
- The parameter must be an array
- The parameter must be null
Show answer
The parameter can only be an Engine instance — Correct. A class-name type declaration restricts the parameter to instances of that class only.
-
Complete the type declaration
public function __construct(____ $engine) { $this->engine = $engine; }Show answer
public function __construct(Engine $engine) { $this->engine = $engine; }