Constructor and Destructor in PHP

What constructors and destructors do in PHP, when each one runs, and how to use __construct and __destruct - with clear, beginner-friendly examples.

In Object-Oriented Programming (OOP) in PHP, two special methods play a key role in the lifecycle of an object built from a class you already met in the lesson on classes and objects: constructor and destructor. The constructor (__construct) is used to initialize the object right after it is created, while the destructor (__destruct) is used to clean up resources just before the object is destroyed. Understanding how they work, when they are called, and how to write them according to best practices is essential in everyday PHP programming - especially when working with databases, files, or external APIs.

In this lesson you will learn the basics, see practical PHP code examples, discover common mistakes, and learn how to avoid them - explained simply and step by step.


What are constructors and destructors in PHP?

Constructor: __construct

  • When it runs: automatically after creating an object (new Class()).
  • Purpose: setting initial object state (e.g., property values), injecting dependencies (e.g., database connections), validating input.
  • How it looks: method named __construct, optionally with parameters.
  • Visibility: can be public, protected, or private (e.g., in factory or singleton patterns) - these access modifiers get a lesson of their own later in this chapter.

Destructor: __destruct

  • When it runs: automatically when the object is destroyed, usually:
    • when you stop referencing it (variable goes out of scope),
    • at the end of the PHP script,
    • when the garbage collector runs (e.g., with cyclic references).
  • Purpose: closing connections, saving final data, deleting temporary files. It’s the “cleanup” phase.
  • Note: do not rely on destructors for critical operations (e.g., finalizing transactions). In case of fatal errors (memory limit, uncaught errors), destructors may not run.

Key facts

  • In PHP, there is only one constructor per class (no method overloading by signature). Different “versions” should be implemented via optional parameters or named constructors (static factory methods).
  • Old-style constructors (method named like the class, PHP 4) were removed in PHP 8. Use only __construct.
  • Destructors cannot accept parameters and should not throw exceptions.
  • In PHP 8, you can use constructor property promotion, which declares properties and methods straight from the parameter list and simplifies code.

PHP constructor and destructor examples

Simplest constructor

<?php
class User
{
    public string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$u = new User('Alice');
echo $u->name; // Alice

Constructor with defaults and validation

<?php
class Product
{
    public string $name;
    public float $price;

    public function __construct(string $name, float $price = 0.0)
    {
        if ($price < 0) {
            throw new InvalidArgumentException('Price cannot be negative.');
        }
        $this->name = $name;
        $this->price = $price;
    }
}

$p = new Product('Book', 29.99);

Property promotion (PHP 8+)

<?php
class Order
{
    public function __construct(
        public int $id,
        public string $customer,
        public float $total = 0.0,
    ) {}
}

$o = new Order(1, 'Company XYZ', 150.00);
echo $o->customer; // Company XYZ

Inheritance and parent::__construct()

<?php
class Connection
{
    public function __construct(
        protected string $dsn,
        protected string $user,
        protected string $pass
    ) {
        // Example: $this->pdo = new PDO($dsn, $user, $pass);
    }
}

class UserRepository extends Connection
{
    private string $table;

    public function __construct(string $table, string $dsn, string $user, string $pass)
    {
        parent::__construct($dsn, $user, $pass);
        $this->table = $table;
    }
}
  • If a subclass does not declare its own constructor, it inherits the parent constructor.
  • Always call parent::__construct() if the parent requires initialization.
  • Don’t worry if extends looks unfamiliar - inheritance in PHP has its own lesson later in this chapter.

Dependency Injection (DI) via constructor

<?php
interface Logger { public function info(string $msg): void; }

class FileLogger implements Logger
{
    public function __construct(private string $path) {}

    public function info(string $msg): void
    {
        file_put_contents($this->path, "[INFO] $msg\n", FILE_APPEND);
    }
}

class OrderService
{
    public function __construct(private Logger $logger) {}

    public function createOrder(int $id): void
    {
        $this->logger->info("Created order #$id");
    }
}

$logger = new FileLogger(__DIR__ . '/app.log');
$service = new OrderService($logger);
$service->createOrder(42);

DI makes testing easier (you can inject a mock logger). If you want to go deeper, this is one of the habits behind writing testable PHP code.

Destructor for cleanup

<?php
class TempFile
{
    private string $path;

    public function __construct(string $prefix = 'tmp_')
    {
        $this->path = tempnam(sys_get_temp_dir(), $prefix);
        file_put_contents($this->path, "Temporary data\n");
    }

    public function getPath(): string { return $this->path; }

    public function __destruct()
    {
        if (is_file($this->path)) {
            @unlink($this->path);
        }
    }
}

$file = new TempFile();
echo $file->getPath();

⚠️ Don’t rely on destructors for time-critical tasks. Use try/finally instead.

Named constructors (static factory methods)

<?php
class Price
{
    private function __construct(private int $cents) {}

    public static function fromPLN(float $pln): self
    {
        return new self((int) round($pln * 100));
    }

    public static function fromCents(int $cents): self
    {
        return new self($cents);
    }

    public function toPLN(): float { return $this->cents / 100; }
}

$p1 = Price::fromPLN(12.34);
$p2 = Price::fromCents(1234);
  • Private constructor ensures controlled instantiation.

Safe initialization and exception handling

<?php
class ApiClient
{
    private $handle;

    public function __construct(private string $endpoint)
    {
        $this->handle = $this->connect($endpoint);
    }

    public function request(string $resource): string { return 'ok'; }

    public function __destruct() { $this->close(); }

    private function connect(string $endpoint) { return fopen('php://memory', 'r+'); }
    private function close(): void { if (is_resource($this->handle)) fclose($this->handle); }
}

try {
    $api = new ApiClient('https://api.example.com');
    echo $api->request('/status');
} catch (Throwable $e) {
    // handle errors
} finally {
    // use finally for critical cleanup
}

Best Practices and Common Mistakes

Best Practices

  • Keep constructors lightweight - only set dependencies and initial values.
  • Validate inputs early.
  • Use typing and property promotion (PHP 8+).
  • Remember parent::__construct() in inheritance.
  • Use named constructors or design patterns (Factory) for alternative creation paths.
  • Place only safe cleanup in destructors - do not rely on them for critical operations.
  • Consider Dependency Injection for testability.
  • Use private/protected constructors to control object creation when needed.

Common Mistakes

  • Throwing exceptions in destructors.
  • Overloading constructors (PHP does not support it).
  • Putting heavy logic in constructors (e.g., network calls).
  • Returning values from __construct (not allowed).
  • Forgetting parent::__construct() in subclasses.
  • Using destructors for critical transactions.

Summary

  • Constructor (__construct) initializes object state and dependencies.
  • Destructor (__destruct) cleans up resources - do not use for critical tasks.
  • PHP supports only one constructor per class.
  • Use property promotion in PHP 8+.
  • Apply best practices: validation, DI, named constructors, safe destructors.

Now you know how constructors and destructors work in PHP, and how to use __construct and __destruct cleanly in your classes.

FAQ

What is the difference between a constructor and a destructor in PHP?

The constructor (__construct) runs when you create an object with new and sets it up - assigning properties and injecting dependencies. The destructor (__destruct) runs when the object is destroyed and cleans up, such as closing files or connections.

When is the destructor called in PHP?

When the object loses its last reference, when the script finishes, or when the garbage collector frees it. Because the exact timing isn't guaranteed, don't rely on a destructor for critical tasks - use try/finally instead.

Can a PHP class have more than one constructor?

No. A class has a single __construct. For alternative ways to build an object, use optional parameters or static factory methods (named constructors) like Price::fromPLN(...).

Can a PHP constructor return a value?

No. __construct cannot return a value - any return is ignored. Its only job is to initialize the newly created object.