How to emulate __destruct() in a static class?
Asked Answered
T

4

8

I've coded a simple configuration class for my own framework.

There are simple functions like get(), set() or loadFile(). But all functions and variables are static.

And now I want to implement an autosave mechanism. I had the idea to create an instance (in my init() function) whose __destruct() will call the static destruct() function:

<?php

class Config
{
  static private $autoSave;
  static public function get() {} /* set(), save(), load(), etc. */

  static public function init($autoSave)
  {
    self::$autoSave = $autoSave;
    new Config();
  }
  static public function destruct()
  {
    if (self::$autoSave)
      self::save();
  }

  public function __destruct()
  {
    Config::destruct();
  }
}

?>

Are there any better solutions or is my design pattern completely wrong in this case?

Tilford answered 8/8, 2011 at 15:24 Comment(0)
P
10

Are there any better solutions or is my design pattern completely wrong in this case?

Destructors are called on objects only, not for static classes.

Instead you could convert your class from static to regular so you can create an instance of it. Then it will have the destructor. Additionally it makes your code easier to re-use and test.

Additionally you're able to implement magic methods for __get and __set or ArrayAccess which often is useful for easy data storage and access as for a configuration.

Alternatively, you can add a destructor object to a static class member to achieve what you're looking for:

class ConfigDestructor
{
  public function __destruct()
  {
    Config::destruct();
  }
}

class Config
{
  static private $destructorInstance;
  static private $autoSave;
  static public function get() {} /* set(), save(), load(), etc. */

  static public function init($autoSave)
  {
    if (null === self::$destructorInstance) 
        self::$destructorInstance = new ConfigDestructor();

    self::$autoSave = $autoSave;
  }
  static public function destruct()
  {
    if (self::$autoSave)
      self::save();
  }
}

Just FYI: You wrote you want to add an auto-save functionality. There is a common gap to fall over for both __destruct() and register_shutdown_function:

Note: Working directory of the script can change inside the shutdown function under some web servers, e.g. Apache.

You should specify an absolute path to access the file you want to save into. See as well: PHP file creation/write within destructor.

Palindrome answered 8/8, 2011 at 15:41 Comment(3)
I already know that function but I thought there other ways. And I've built a workaround for the path, too. I simply save a file handle by loading. BTW at first my class hasn't any static functions, I've converted it to the code above.Tilford
@user603003: I just added a way to do it with a ConfigDestructor object if you really want to stay static. But I highly recommend you don't static class functions that generally, but make the configuration work as an object.Palindrome
So I use my first code which contained 100% object-oriented functions.Tilford
A
8

Inside your init method, add a call to register_shutdown_function:

register_shutdown_function(array('Config', 'destruct'));
Asleep answered 8/8, 2011 at 15:31 Comment(0)
P
3

Have you looked at register_shutdown_function? You could add your method to the shutdown part of the script.

It could also be worth it to look at the Singleton pattern.

Photostat answered 8/8, 2011 at 15:32 Comment(3)
Yes, but I didn't want to reserve the function for the config class.Tilford
You may have several shutdown functions registered, if that's your worry. Registering one does not unregister the previously registered ones, if that's what you meant by "reserve".Murrumbidgee
Well if one of those shutdown functions calls exit() the others aren't called any further - __destruct() is immune against that behavior.Palindrome
C
1

You can create an instance of this static class on autoregister.

$instance = array();
spl_autoload_register(function ($class)
{
    ...
    global $instance;
    if ($isStatic) $instance[] = new $class();
    ...
});

This is working fine for me.

... and for those who don't like readable code (it is untested):

class staticInstances()
{
    private static $list = array();

    public static function add($class)
    {
        self::$list[] = new $class();
    }

    function __distruct()
    {
        foreach (self::$list as $class)
            unset(self::$list);
    }
}
$staticInstances = new staticInstances();

spl_autoload_register(function ($class)
{
    ...
    if ($isStatic) staticInstances::add($class);
    ...
});
Cyrstalcyrus answered 12/10, 2013 at 19:7 Comment(2)
Wow, an answer even 1 year after I asked the question. Your code might work fine, but it's using global variables - a thing which a programmer should avoid.Tilford
then build a class with a static function addclass() and instanciate this class. but this is an overkill in my opinion. it is my only global variable. remember k.i.s.s. as often as possible. this doesnt mean to be lazy.Cyrstalcyrus

© 2022 - 2024 — McMap. All rights reserved.