A constructor is one of those PHP features you start using almost automatically once your classes become useful. Instead of creating an object and then remembering to set five different properties, you can make the object start life in a valid state.
The destructor sits at the other end of the object’s lifecycle. PHP calls it when the object is being destroyed. It can be useful for cleanup, although it should not become a last-minute dumping ground for important application logic.
PHP provides the __construct() and __destruct() magic methods for these two jobs.
PHP constructor and destructor quick answer
A PHP constructor is the __construct() method of a class. PHP calls it automatically when you create an object with new. It is commonly used to initialize properties and dependencies.
A destructor is the __destruct() method. PHP calls it when an object is destroyed, such as when its last reference disappears or during script shutdown. A destructor takes no arguments.
<?php
class Product
{
public function __construct()
{
echo 'Product created';
}
public function __destruct()
{
echo 'Product destroyed';
}
}
$product = new Product();
Creating $product automatically calls __construct(). The destructor is called later when PHP destroys the object.
PHP constructor with arguments
Constructors become more useful when they accept values required by the object. The values are passed immediately after the class name when the object is created.
<?php
class Product
{
private string $name;
private float $price;
public function __construct(string $name, float $price)
{
$this->name = $name;
$this->price = $price;
}
public function getDetails(): string
{
return $this->name . ' - $' . number_format($this->price, 2);
}
}
$product = new Product('Wireless Mouse', 29.95);
echo $product->getDetails();
Here, the constructor requires a product name and price. An object cannot be created without supplying them. That is useful because the class does not have to deal with a half-initialized product later.
Constructor parameters can use PHP type declarations, default values and named arguments like normal PHP method parameters.
Constructor property promotion in PHP
For classes that mainly copy constructor arguments into properties, PHP 8 introduced constructor property promotion. It removes quite a lot of repetitive code.
The previous Product class can be shortened to this:
<?php
class Product
{
public function __construct(
private string $name,
private float $price
) {
}
public function getDetails(): string
{
return $this->name . ' - $' . number_format($this->price, 2);
}
}
$product = new Product('Wireless Mouse', 29.95);
echo $product->getDetails();
The $name and $price parameters are also declared as object properties and PHP assigns their values automatically. There is no need to declare the properties separately and repeat assignments such as $this->name = $name.
Property promotion is especially convenient for small value objects, configuration objects and classes that receive several dependencies through their constructor.
Calling a parent constructor
With PHP inheritance, when a child class defines its own constructor, PHP does not call the parent constructor automatically. If the parent constructor must run, call it explicitly with parent::__construct().
<?php
class User
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
}
class AdminUser extends User
{
private array $permissions;
public function __construct(string $name, array $permissions)
{
parent::__construct($name);
$this->permissions = $permissions;
}
public function getName(): string
{
return $this->name;
}
}
$admin = new AdminUser(
'John',
['create-user', 'delete-user']
);
echo $admin->getName();
The child constructor handles its own $permissions property and delegates the common $name initialization to the parent class.
If the child class does not define a constructor, it inherits the parent constructor normally.
Can a PHP constructor return a value?
No. A constructor should initialize the object, not return another value.
The result of new ClassName() is always the newly created object. Do not use a return statement to try to return data from __construct().
If object creation can fail or needs more complex setup, a static factory method is often clearer.
<?php
class ApiClient
{
private function __construct(
private string $baseUrl
) {
}
public static function create(string $baseUrl): self
{
if ($baseUrl === '') {
throw new InvalidArgumentException(
'Base URL cannot be empty.'
);
}
return new self($baseUrl);
}
}
$client = ApiClient::create('https://api.example.com');
This pattern is not required for ordinary classes. It is useful when object creation needs validation or several setup steps before returning a usable instance.
How PHP destructors work
A destructor is declared with __destruct(). It takes no parameters.
<?php
class TemporaryFile
{
public function __construct(
private string $path
) {
file_put_contents($this->path, 'Temporary data');
}
public function __destruct()
{
if (is_file($this->path)) {
unlink($this->path);
}
}
}
$file = new TemporaryFile(
sys_get_temp_dir() . '/php-example.txt'
);
In this example, the destructor removes a temporary file when PHP destroys the object.
This demonstrates the idea well, but important application behavior should not depend only on a destructor. Destructor timing can be affected by object references and script shutdown. Explicit cleanup is usually easier to reason about when timing matters.
When is a destructor called?
According to PHP’s constructor and destructor documentation, a destructor can run before the end of the script if the object is destroyed earlier.
<?php
class Connection
{
public function __construct()
{
echo "Connection opened\n";
}
public function __destruct()
{
echo "Connection closed\n";
}
}
$connection = new Connection();
unset($connection);
echo "Script continues\n";
Once unset() removes the last reference to the object, PHP can destroy it and call __destruct().
If other references to the same object still exist, removing one variable does not necessarily destroy the object.
<?php
class Demo
{
public function __destruct()
{
echo "Object destroyed\n";
}
}
$first = new Demo();
$second = $first;
unset($first);
echo "One reference still exists\n";
unset($second);
The destructor runs only after the remaining reference is removed.
This distinction is useful when debugging object lifecycle issues. unset($variable) removes a variable. It does not guarantee immediate object destruction if something else still references that object.
Calling a parent destructor
Destructor inheritance follows a rule similar to constructors. If a child class defines its own destructor, the parent destructor is not called automatically.
Call parent::__destruct() when the parent class also has cleanup work to perform.
<?php
class BaseLogger
{
public function __destruct()
{
echo "Base logger cleanup\n";
}
}
class FileLogger extends BaseLogger
{
public function __destruct()
{
echo "File logger cleanup\n";
parent::__destruct();
}
}
$logger = new FileLogger();
Forgetting the parent call can leave part of the intended cleanup logic unused. This matters most when a base class owns a resource that the child class does not manage directly.
Do not use old-style PHP constructors
Older PHP code sometimes used a method with the same name as the class as its constructor.
<?php
class Product
{
public function Product()
{
echo 'Old-style constructor';
}
}
That syntax is obsolete. PHP 8 and later do not treat Product() as a constructor.
Use __construct() in modern PHP:
<?php
class Product
{
public function __construct()
{
echo 'Modern constructor';
}
}
This is particularly important when maintaining an old PHP application. A class-name method may look like a constructor at first glance, but on current PHP versions it is just an ordinary method unless something calls it explicitly.
Constructor and destructor best practices
Keep constructors focused on getting the object into a valid state. Passing required dependencies and initializing properties are good constructor responsibilities.
Avoid putting large amounts of work in a constructor. Database queries, remote API calls, file processing, and other expensive operations can make object creation harder to test and harder to control.
Use destructors for lightweight cleanup where automatic cleanup is genuinely useful. Do not rely on them for critical work such as saving important business data, committing transactions, sending required notifications, or recording something that absolutely must happen.
When cleanup must happen at a predictable point, an explicit method is usually clearer:
<?php
class ReportFile
{
private bool $closed = false;
public function close(): void
{
if ($this->closed) {
return;
}
// Perform predictable cleanup here.
$this->closed = true;
}
public function __destruct()
{
$this->close();
}
}
This gives application code a clear close() method while keeping the destructor as a fallback.
Common constructor and destructor mistakes
Most problems with constructors and destructors are not syntax errors. They come from assumptions about when methods run and what they should be responsible for.
Forgetting to call the parent constructor
If both the parent and child define __construct(), the child must call parent::__construct() when the parent initialization is required.
<?php
class Account
{
protected string $id;
public function __construct(string $id)
{
$this->id = $id;
}
}
class PremiumAccount extends Account
{
private int $level;
public function __construct(string $id, int $level)
{
parent::__construct($id);
$this->level = $level;
}
}
Doing too much work in the constructor
A constructor that performs several database queries, API requests, or file operations makes object creation expensive and harder to test.
Prefer initializing the object in the constructor and moving larger operations into normal methods.
Expecting unset() to always call the destructor immediately
unset() removes a variable. If another reference to the same object still exists, the object remains alive and its destructor does not run yet.
Depending on a destructor for critical work
Destructors are useful for cleanup, but they are a poor place for work that must definitely happen at a precise point in the request.
If something is important enough that failure would affect application data, call it explicitly instead of relying only on __destruct().
Constructor vs destructor in PHP
| Constructor | Destructor |
|---|---|
__construct() |
__destruct() |
| Runs when an object is created | Runs when an object is destroyed |
| Can accept parameters | Cannot accept parameters |
| Commonly initializes properties and dependencies | Commonly performs lightweight cleanup |
| Can use constructor property promotion | Has no equivalent property-promotion feature |
| A child constructor must explicitly call the parent constructor when needed | A child destructor must explicitly call the parent destructor when needed |
Frequently asked questions
Can a PHP class have more than one constructor?
No. PHP does not support constructor overloading by defining several __construct() methods with different parameter lists.
You can use optional parameters, named arguments, or static factory methods when you need different ways to create an object.
Is __construct() required in every PHP class?
No. Define a constructor only when the object needs initialization when it is created.
A class without __construct() can still be instantiated normally.
Is __destruct() required?
No. Most PHP classes do not need a destructor.
Add one only when the class owns cleanup work that makes sense at object destruction time.
Can I manually call __construct() or __destruct()?
They are methods and can technically be called like methods when visibility permits, but doing so usually gives the wrong object-lifecycle semantics.
Create objects with new and expose normal methods for explicit initialization or cleanup when you need those operations independently.
What happens if the constructor throws an exception?
If __construct() throws an exception, object creation does not complete successfully. The caller should handle the exception where appropriate.
<?php
class Percentage
{
public function __construct(
private float $value
) {
if ($value < 0 || $value > 100) {
throw new InvalidArgumentException(
'Percentage must be between 0 and 100.'
);
}
}
}
try {
$percentage = new Percentage(150);
} catch (InvalidArgumentException $exception) {
echo $exception->getMessage();
}
Constructor validation is useful when an object should never exist in an invalid state.
Conclusion
Use __construct() to make sure an object starts with the data and dependencies it needs. For simple classes, constructor property promotion keeps this code compact without hiding what is happening.
Use __destruct() more cautiously. It is useful for lightweight cleanup, but explicit cleanup methods are better when timing or reliability matters.
The key detail to remember is that constructors control object initialization, while destructors respond to object destruction. Keeping both responsibilities small and predictable makes PHP classes easier to understand and maintain.