ZetCode

PHP Properties Tutorial

last modified May 18, 2025

In this article we cover PHP properties.

Properties in PHP are variables that belong to a class, encapsulating data and allowing objects to store relevant information. Each property is associated with a specific visibility level, determining how accessible it is within the program. Properties can also be static or non-static, influencing whether they belong to a class or an instance.

Types of PHP Properties:

Properties are essential for object-oriented programming in PHP, allowing classes to maintain state and behavior. They can be declared with various visibility levels, and PHP 7.4 introduced typed properties for better type safety. PHP 8.0 added constructor property promotion, reducing boilerplate code when initializing properties in constructors.

Basic property declaration

Properties are declared within a class using one of the visibility keywords: public, protected, or private. Public properties can be accessed from anywhere, protected only within the class and its descendants, and private only within the declaring class.

basic_properties.php
<?php

declare(strict_types=1);

class User {
    public $name;
    protected $email;
    private $password;

    public function __construct($name, $email, $password) {
        $this->name = $name;
        $this->email = $email;
        $this->password = $password;
    }

    public function getEmail() {
        return $this->email;
    }
}

$user = new User('John Doe', 'john@example.com', 'secret123');

echo "Name: {$user->name}\n";        // Works - public
// echo "Email: {$user->email}\n";    // Error - protected
// echo "Pass: {$user->password}\n";  // Error - private
echo "Email: {$user->getEmail()}\n"; // Works - accessed via method

This example shows property declaration with different visibility levels. Public properties can be accessed directly, while protected and private properties need methods to access them. The constructor initializes all properties when creating a new object.

λ php basic_properties.php
Name: John Doe
Email: john@example.com

Property type declarations

PHP 7.4 introduced typed properties, allowing you to specify the type of a property. This helps catch type-related errors early and makes code more maintainable. Supported types include all PHP types and custom classes.

typed_properties.php
<?php

declare(strict_types=1);

class Product {

    public int $id;
    public string $name;
    public float $price;
    public bool $inStock;
    public ?DateTime $createdAt; // Nullable

    public function __construct(int $id, string $name, float $price) {
        $this->id = $id;
        $this->name = $name;
        $this->price = $price;
        $this->inStock = true;
        $this->createdAt = new DateTime();
    }

    public function setPrice(float $newPrice): void {
        if ($newPrice <= 0) {
            throw new InvalidArgumentException("Price must be positive");
        }
        $this->price = $newPrice;
    }
}

$product = new Product(1, 'Laptop', 999.99);
$product->setPrice(899.99);

echo "Product: {$product->name}, Price: {$product->price}\n";
echo "Created at: {$product->createdAt->format('Y-m-d')}\n";

// This would cause a TypeError
// $product->price = "free";

Typed properties ensure values match the declared type. The example shows various type declarations including nullable types (with ? prefix). Type checks happen both during assignment and when accessing properties.

λ php typed_properties.php
Product: Laptop, Price: 899.99
Created at: 2025-05-18

Static properties

Static properties belong to the class rather than any object instance. They are shared across all instances of the class and can be accessed without creating an object. Use the static keyword to declare them.

static_properties.php
<?php

declare(strict_types=1);

class Counter {

    public static int $count = 0;
    public int $instanceCount = 0;

    public function __construct() {
        self::$count++;
        $this->instanceCount++;
    }

    public static function getCount(): int {
        return self::$count;
    }
}

// Access static property without creating an instance
echo "Initial count: " . Counter::$count . "\n";

$c1 = new Counter();
$c2 = new Counter();
$c3 = new Counter();

echo "Static count: " . Counter::getCount() . "\n";
echo "Instance 1 count: {$c1->instanceCount}\n";
echo "Instance 2 count: {$c2->instanceCount}\n";
echo "Instance 3 count: {$c3->instanceCount}\n";

// Late Static Binding
class ParentClass {
    protected static string $name = 'Parent';

    public static function getName(): string {
        return static::$name; // 'static' instead of 'self'
    }
}

class ChildClass extends ParentClass {
    protected static string $name = 'Child';
}

echo ParentClass::getName() . "\n"; // Outputs 'Parent'
echo ChildClass::getName() . "\n";  // Outputs 'Child'

Static properties maintain their value across all instances. The example demonstrates how static $count increments for each new object while $instanceCount remains specific to each instance. Late static binding allows child classes to override static properties.

self::$count++;

The self:: keyword is used to access static members of the class from within the class itself. It allows you to refer to static properties and methods without needing to create an instance of the class.

$this->instanceCount++;

The $this keyword is used to refer to the current instance of the class. It allows you to access instance properties and methods from within the class. In this case, it increments the instance-specific property $instanceCount for each new object created.

λ php static_properties.php
Initial count: 0
Static count: 3
Instance 1 count: 1
Instance 2 count: 1
Instance 3 count: 1
Parent
Child

Property promotion in constructors

PHP 8.0 introduced constructor property promotion, allowing properties to be declared directly in the constructor parameters. This reduces boilerplate code when properties are only used to store constructor parameters.

property_promotion.php
<?php

declare(strict_types=1);

class Customer {

    public function __construct(
        public string $name,
        protected string $email,
        private string $phone,
        public readonly DateTimeImmutable $createdAt = new DateTimeImmutable()
    ) {
        // No need for $this->name = $name; etc.
    }

    public function getContactInfo(): string {
        return "Email: {$this->email}, Phone: {$this->phone}";
    }
}

$customer = new Customer('Alice Smith', 'alice@example.com', '555-1234');

echo "Customer: {$customer->name}\n";
echo "Created: {$customer->createdAt->format('Y-m-d')}\n";
echo $customer->getContactInfo() . "\n";

// readonly properties can't be modified
// $customer->createdAt = new DateTimeImmutable(); // Error

Property promotion combines parameter declaration with property declaration. The example shows promoted properties with different visibilities and a readonly property with a default value. Readonly properties can only be set once and cannot be modified afterward.

λ php property_promotion.php
Customer: Alice Smith
Created: 2025-05-18
Email: alice@example.com, Phone: 555-1234

Magic properties with __get and __set

PHP allows dynamic property access through magic methods __get and __set. These methods are called when accessing non-existent or inaccessible properties, enabling flexible property handling.

magic_properties.php
<?php

declare(strict_types=1);

class DynamicConfig {
    private array $data = [];
    private array $allowed = ['timeout', 'debug', 'log_level'];

    public function __get(string $name): mixed {
        if (in_array($name, $this->allowed)) {
            return $this->data[$name] ?? null;
        }
        throw new OutOfBoundsException("Property $name doesn't exist");
    }

    public function __set(string $name, mixed $value): void {
        if (in_array($name, $this->allowed)) {
            $this->data[$name] = $value;
        } else {
            throw new OutOfBoundsException("Cannot set $name");
        }
    }

    public function __isset(string $name): bool {
        return isset($this->data[$name]);
    }

    public function __unset(string $name): void {
        unset($this->data[$name]);
    }
}

$config = new DynamicConfig();
$config->timeout = 30;
$config->debug = true;

echo "Timeout: {$config->timeout}\n";
echo "Debug: " . ($config->debug ? 'ON' : 'OFF') . "\n";

var_dump(isset($config->timeout)); // true
var_dump(isset($config->log_level)); // false

// This would throw an exception
// $config->invalid = 'value';

Magic methods provide control over property access. The example implements a whitelist of allowed properties. __get retrieves values, __set stores them, __isset checks existence, and __unset removes properties, all with validation.

λ php magic_properties.php
Timeout: 30
Debug: ON
bool(true)
bool(false)

Readonly properties

PHP 8.1 introduced readonly properties that can only be initialized once and then remain immutable. They're useful for creating value objects and DTOs where properties shouldn't change after construction.

readonly_properties.php
<?php

declare(strict_types=1);

class Transaction {

    public readonly string $id;
    public readonly float $amount;
    public readonly DateTimeImmutable $createdAt;

    public function __construct(float $amount) {
        $this->id = uniqid('txn_');
        $this->amount = $amount;
        $this->createdAt = new DateTimeImmutable();
    }

    // Can't have a setter that modifies readonly properties
    // public function setAmount(float $amount): void {
    //     $this->amount = $amount; // Error
    // }
}

// PHP 8.2 allows readonly classes
readonly class Point {
    public function __construct(
        public float $x,
        public float $y
    ) {}
}

$txn = new Transaction(99.99);
echo "Transaction: {$txn->id}, Amount: {$txn->amount}\n";
echo "Created: {$txn->createdAt->format('Y-m-d H:i:s')}\n";

$point = new Point(3.5, 4.2);
echo "Point: ({$point->x}, {$point->y})\n";

// These would cause errors:
// $txn->amount = 100;
// $point->x = 5;

Readonly properties provide immutability guarantees. The example shows individual readonly properties and a readonly class (PHP 8.2+). Once set, these properties cannot be modified, making objects more predictable and thread-safe.

λ php readonly_properties.php
Transaction: txn_6647f5a3e3a63, Amount: 99.99
Created: 2025-05-18 00:00:00
Point: (3.5, 4.2)

Property initialization and default values

Properties can be initialized with default values when declared. PHP 7.4+ allows typed properties to have default values that match their type.

property_initialization.php
<?php

declare(strict_types=1);

class Settings {

    // Basic initialization
    public string $theme = 'light';
    public int $itemsPerPage = 10;

    // Typed properties with defaults
    public bool $notificationsEnabled = true;
    public ?string $apiKey = null;

    // Complex defaults with constants
    public const DEFAULT_LOCALE = 'en_US';
    public string $locale = self::DEFAULT_LOCALE;

    // Uninitialized typed properties
    public DateTime $lastUpdated; // Must be initialized before access

    // Static property initialization
    public static int $cacheLifetime = 3600;

    public function __construct() {
        $this->lastUpdated = new DateTime();
    }

    public function updateLastModified(): void {
        $this->lastUpdated = new DateTime();
    }
}

$settings = new Settings();
echo "Theme: {$settings->theme}\n";
echo "Items per page: {$settings->itemsPerPage}\n";
echo "Last updated: {$settings->lastUpdated->format('Y-m-d')}\n";

// Modify properties
$settings->theme = 'dark';
$settings->itemsPerPage = 25;
$settings->updateLastModified();

echo "Modified theme: {$settings->theme}\n";
echo "Modified last updated: {$settings->lastUpdated->format('H:i:s')}\n";

This example demonstrates various property initialization techniques. Typed properties without defaults must be initialized before access. Constants can provide default values. Static properties are initialized once when the class is loaded.

λ php property_initialization.php
Theme: light
Items per page: 10
Last updated: 2025-05-21
Modified theme: dark
Modified last updated: 08:42:32

PHP properties are fundamental to object-oriented programming in PHP. Key points to remember about properties:

In this article we covered the basics of PHP properties, including their declaration, visibility, static properties, typed properties, and constructor property promotion. We also discussed magic properties, readonly properties, and property initialization techniques.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all PHP tutorials.