← Tilbage til koncepter

Magic Methods

Special methods som __construct, __get, __set, __call, __toString, __invoke og andre magic methods i PHP

Kategori: Fundamentals

🎯 Key Points

  • __construct() - Called automatisk når object instantieres, bruges til initialization
  • __destruct() - Called når object destroyes eller script terminates, til cleanup
  • __get($name) - Intercept property reads for non-existent eller inaccessible properties
  • __set($name, $value) - Intercept property writes for non-existent eller inaccessible properties
  • __isset($name) - Called når isset() eller empty() bruges på inaccessible properties
  • __unset($name) - Called når unset() bruges på inaccessible properties
  • __call($name, $args) - Intercept calls til non-existent eller inaccessible methods
  • __callStatic($name, $args) - Intercept static method calls til non-existent methods
  • __toString() - Definerer hvordan object konverteres til string
  • __invoke() - Gør object callable som en function
  • __clone() - Called når object clones med clone keyword
  • __serialize() & __unserialize() - Custom serialization logic (PHP 7.4+)

💻 Kode Eksempel

<?php

// Comprehensive magic methods example
class MagicUser {
    private array $data = [];
    private array $methodCache = [];
    
    // Constructor
    public function __construct(private string $name, private int $age) {
        echo "User created: {$this->name}\n";
    }
    
    // Dynamic property getter
    public function __get(string $name): mixed {
        echo "Getting property: {$name}\n";
        return $this->data[$name] ?? null;
    }
    
    // Dynamic property setter
    public function __set(string $name, mixed $value): void {
        echo "Setting property {$name} = {$value}\n";
        $this->data[$name] = $value;
    }
    
    // Check if property exists
    public function __isset(string $name): bool {
        return isset($this->data[$name]);
    }
    
    // Unset property
    public function __unset(string $name): void {
        unset($this->data[$name]);
    }
    
    // Dynamic method calls
    public function __call(string $name, array $args): mixed {
        // Magic getter methods: getPropertyName()
        if (str_starts_with($name, 'get')) {
            $property = lcfirst(substr($name, 3));
            return $this->data[$property] ?? null;
        }
        
        // Magic setter methods: setPropertyName($value)
        if (str_starts_with($name, 'set') && count($args) === 1) {
            $property = lcfirst(substr($name, 3));
            $this->data[$property] = $args[0];
            return $this;
        }
        
        throw new BadMethodCallException("Method {$name} does not exist");
    }
    
    // Static method calls
    public static function __callStatic(string $name, array $args): mixed {
        if ($name === 'create') {
            return new self(...$args);
        }
        throw new BadMethodCallException("Static method {$name} does not exist");
    }
    
    // String conversion
    public function __toString(): string {
        return "{$this->name} (Age: {$this->age})";
    }
    
    // Make object callable
    public function __invoke(string $message): void {
        echo "{$this->name} says: {$message}\n";
    }
    
    // Clone handling
    public function __clone(): void {
        echo "Cloning user: {$this->name}\n";
        // Deep clone data array
        $this->data = [...$this->data];
    }
    
    // Destructor
    public function __destruct() {
        echo "User destroyed: {$this->name}\n";
    }
}

// Usage examples
echo "=== Creating User ===\n";
$user = new MagicUser('John', 30);

echo "\n=== Dynamic Properties ===\n";
$user->email = 'john@example.com';
echo "Email: {$user->email}\n";

echo "\n=== Dynamic Methods ===\n";
$user->setPhone('555-1234');
echo "Phone: " . $user->getPhone() . "\n";

echo "\n=== String Conversion ===\n";
echo "User: {$user}\n";

echo "\n=== Callable Object ===\n";
$user('Hello World!');

echo "\n=== Static Factory ===\n";
$user2 = MagicUser::create('Jane', 25);

echo "\n=== Cloning ===\n";
$user3 = clone $user;

echo "\n=== Script End (triggers destructors) ===\n";

💼 Hvornår bruges det?

  • ORM models (Eloquent, Doctrine) hvor database columns tilgås som object properties dynamisk
  • API clients hvor remote endpoints mappes til method calls med __call for flexible API design
  • Configuration objects med nested access via __get for reading hierarchical config data
  • Mock objects i testing hvor __call intercepter method calls for verification og stubbing
  • Value objects med __toString for convenient debugging og logging af complex objects
  • Event emitters med __invoke til callable objects der kan dispatches som event handlers

⭐ Best Practices

  • Dokumentér magic behavior med @property og @method PHPDoc tags for IDE autocomplete support
  • Use magic methods sparsomt - prefer explicit methods når behavior er core functionality
  • Throw descriptive exceptions i __call for non-existent methods frem for silent failures
  • Implement __isset og __unset sammen med __get og __set for consistent property behavior
  • Cache results i __get hvis property access er expensive (database queries, calculations)
  • Be aware of static analysis limitations - tools som PHPStan kan ikke analysere magic behavior
  • Consider performance - magic methods har slight overhead sammenlignet med direct property access
  • Use __toString defensively - never throw exceptions, return string representation even for invalid state

ℹ️ Quick Info

Kategori
Fundamentals
Sværhedsgrad
Mellem