All posts
Published in PHP

PHP Magic Methods Explained

Profile image of Atakan Demircioğlu
By Atakan Demircioğlu
Fullstack Developer

PHP Magic methods explained. What are the PHP Magic methods?

PHP Magic Methods Explained image 1

PHP Magic Methods Explained

Here are my notes about PHP Magic methods.

What are PHP Magic Methods?

  • special methods that are called automatically when certain conditions are met
  • start with a double underscore ( __ )
  • predefined
  • neither can be created nor removed
  • automatically called
  • All magic methods, with the exception of __construct(), __destruct(), and __clone(), must be declared as public, otherwise an E_WARNING is emitted.

__construct Magic Method

  • automatically every time the object of a particular class is created
  • useful to initialization what the object may need before going further

__destruct Magic Method

  • called when the object is destroyed

__call Magic Method

  • triggered when we call a method that doesn’t define yet

__callStatic Magic Method

  • similar to the __call() method but this is for static context.

__get Magic Method

  • called when an inaccessible variable or non-existing variable is used

__set Magic Method

  • called when an inaccessible variable or non-existing variable is written

__isset Magic Method

  • triggered by calling isset() or empty() on inaccessible (protected or private) or non-existing properties.

__unset Magic Method

  • invoked when unset() is used on inaccessible (protected or private) or non-existing properties.

__toString Magic Method

  • called when we need to convert the object into a string

__invoke Magic Method

  • called when the object is being called as a function

__clone Magic Method

  • triggered where cloning objects is done by using the clone keyword

__debugInfo Magic Method

  • triggered when the var_dump function is called

__sleep Magic Method

  • serialize() function triggers
  • should return an array holding all the object’s properties that should be included in the serialized version of that object.
  • For resource types, PHP fails at serializing it

__wakeup Magic Method

  • need to initialize the value whenever we get a new fresh instance from the unserialize function, and that’s what we’re doing inside the __wakeup method.

__serialize() and __unserialize()

  • serialize() checks if the class has a function with the __serialize() magic method. If yes, the function is executed first before any serialization operation.
  • If there are both __serialize and _sleep in the same object, just serialize will be called.

Note: For a better understanding of what is happening in __sleep and __wakeup magic methods, you can read this article.

Real Life Example

(__get, __set, __isset, __unset)

<?php

class PSession
{
    /**
     * @var array
     */
    private array $options = [];
    /**
     * @var array|string[]
     */
    private array $phpSessionValidOptions = [
        'save_path',
        'name',
        'save_handler',
        'auto_start',
        'gc_probability',
        'gc_divisor',
        'gc_maxlifetime',
        'serialize_handler',
        'cookie_lifetime',
        'cookie_path',
        'cookie_domain',
        'cookie_secure',
        'cookie_httponly',
        'cookie_samesite',
        'use_strict_mode',
        'use_cookies',
        'use_only_cookies',
        'referer_check',
        'cache_limiter',
        'cache_expire',
        'use_trans_sid',
        'trans_sid_tags',
        'trans_sid_hosts',
        'sid_length',
        'sid_bits_per_character',
        'upload_progress.enabled',
        'upload_progress.cleanup',
        'upload_progress.prefix',
        'upload_progress.name',
        'upload_progress.freq',
        'upload_progress.min-freq',
        'lazy_write'
    ];

    /**
     * @param array $options
     * @throws InvalidOption
     */
    public function __construct(array $options = [])
    {
        // if includes non valid php session options
        foreach ($options as $key => $value) {
            if (!in_array($key, $this->phpSessionValidOptions)) {
                throw new InvalidOption($key);
            }
        }
        $this->options = $options;
    }

    /**
     * @return bool
     */
    public function start(): bool
    {
        return session_start($this->options);
    }

    /**
     * @return bool
     */
    public function destroy(): bool
    {
        return session_destroy();
    }

    /**
     *
     */
    public function clear(): void
    {
        session_unset();
    }

    /**
     * @return bool
     */
    public function regenerateId(): bool
    {
        return session_regenerate_id();
    }

    /**
     * @return string
     */
    public function getId(): string
    {
        return session_id();
    }

    /**
     * @param string $id
     */
    public function setId(string $id): void
    {
        session_id($id);
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return session_name();
    }

    /**
     * @param string $name
     */
    public function setName(string $name): void
    {
        session_name($name);
    }

    /**
     * @return array
     */
    public function all(): array
    {
        return $_SESSION ?? [];
    }

    /**
     * @return bool
     */
    public function isStarted(): bool
    {
        return $this->getStatus() === PHP_SESSION_ACTIVE;
    }

    /**
     * @return int
     */
    public function getStatus(): int
    {
        return session_status();
    }

    /**
     * @return bool
     */
    public function isNotStarted(): bool
    {
        return $this->getStatus() === PHP_SESSION_NONE;
    }

    /**
     * @return bool
     */
    public function isDestroyed(): bool
    {
        return $this->getStatus() === PHP_SESSION_DISABLED;
    }

    /**
     * @param string $key
     * @return mixed|null
     */
    public function __get(string $key)
    {
        return $this->get($key, null);
    }

    /**
     * @param string $key
     * @param $value
     */
    public function __set(string $key, $value): void
    {
        $this->set($key, $value);
    }

    /**
     * @param string $key
     * @param null $defaultValue
     * @return mixed|null
     */
    public function get(string $key, $defaultValue = null)
    {
        $keys = explode('.', $key);
        $session = $_SESSION;
        foreach ($keys as $key) {
            // allow both object and array access
            if (is_array($session) && isset($session[$key])) {
                $session = $session[$key];
            } elseif (is_object($session) && isset($session->$key)) {
                $session = $session->$key;
            } else {
                return $defaultValue;
            }
        }

        return $session ?? $defaultValue;
    }

    /**
     * @param string $key
     * @param null $value
     */
    public function set(string $key, $value = null): void
    {
        // both object and array access
        $keys = explode('.', $key);
        $session = &$_SESSION;
        foreach ($keys as $key) {
            if (is_array($session) && !isset($session[$key])) {
                $session[$key] = [];
            } elseif (is_object($session) && !isset($session->$key)) {
                $session->$key = [];
            }
            
            if (is_array($session)) {
                $session = &$session[$key];
            } elseif (is_object($session)) {
                $session = &$session->$key;
            }
        }

        $session = $value;
    }

    /**
     * @param string $key
     * @return bool
     */
    public function __isset(string $key): bool
    {
        return $this->has($key);
    }

    /**
     * @param string $key
     * @return bool
     */
    public function has(string $key): bool
    {
        return isset($_SESSION[$key]);
    }

    /**
     * @param string $key
     */
    public function __unset(string $key): void
    {
        $this->remove($key);
    }

    /**
     * @param string $key
     */
    public function remove(string $key): void
    {
        unset($_SESSION[$key]);
    }
}

Useful video for a better understanding

References

Recommended Articles;

How To Secure PHP Sessions?
My notes about how to secure PHP Sessions, what is session hijacking, and so on.atakde.medium.com

PHP Loops — Which is faster?
Foreach vs for in PHP, which is the fastest loop in PHP?atakde.medium.com

JIT Compiler in PHP
Here are my notes about the JIT compiler in PHP.atakde.medium.com