5

I have a class for interacting with a memcache server. I have different functions for inserting, deleting and retrieving data. Originally each function made a call to memcache_connect(), however that was unnecessary, e.g.:

mc->insert()  
mc->get()  
mc->delete() 

would make three memcache connections. I worked around this by creating a construct for the class:

function __construct() {
    $this->mem = memcache_connect( ... );
}

and then using $this->mem wherever the resource was needed, so each of the three functions use the same memcache_connect resource.

This is alright, however if I call the class inside other classes, e.g.:

class abc
{
    function __construct() {
        $this->mc = new cache_class;
    }
}    
class def
{
    function __construct() {
        $this->mc = new cache_class;
    }
}

then it is still making two memcache_connect calls, when it only needs one.

I can do this with globals but I would prefer not to use them if I don't have to.

Example globals implementation:

$resource = memcache_connect( ... );

class cache_class
{
    function insert() {
        global $resource;
        memcache_set( $resource , ... );
    }
    function get() {
        global $resource;
        return memcache_get( $resource , ... );
    }

}

Then no matter how many times the class is called there will only be one call to memcache_connect.

Is there a way to do this or should I just use globals?

Nikola K.
  • 7,093
  • 13
  • 31
  • 39
carbin
  • 2,907
  • 2
  • 28
  • 36

4 Answers4

9

I would code another class using singleton pattern for getting the only instance of memcache. Like this -

class MemCache 
{ 
  private static $instance = false;   
  private function __construct() {}

  public static function getInstance()
  { 
    if(self::$instance === false)
    { 
      self::$instance = memcache_connect(); 
    } 

    return self::$instance; 
  } 
}

and usage -

$mc = MemCache::getInstance();
memcache_get($mc, ...)
...
Martin Jonáš
  • 2,309
  • 15
  • 12
  • It's a reasonable solution but let's face it, it's a global. :) – cletus Feb 15 '09 at 11:43
  • This is what I'd do. You can't test it, but let's be honest, who tests this kind of thing anyway? – Philip Morton Feb 15 '09 at 11:45
  • The question is how testable stays the rest of the whole code if one small part isn't testable. Or how would you implement a simulated MemCache object for other tests? – okoman Feb 15 '09 at 11:51
  • 2
    @cletus: It's using a global pattern, but it's not using the global scope. Programs run in an environment; at that level of organization, everything's a global, even if you've got all your real data inside class properties. You want to avoid having *variables* in global, but interfaces to external data sources have to live somewhere, and you want Singleton for those anyway. – dirtside May 11 '09 at 04:28
6

Pass in the MC instance:

class abc
{
    function __construct($mc) {
        $this->mc = $mc;
    }
}    
class def
{
    function __construct($mc) {
        $this->mc = $mc;
    }
}

$mc = new cache_class;
$abc = new abc($mc);

etc.

Greg
  • 10,350
  • 1
  • 26
  • 35
2

I think you're looking for static properties here.

class mc {
    private static $instance;

    public static function getInstance() {
        if (self::$instance== null) {
            self::$instance= new self;
        }
        return self::$instance;
    }

    private function __construct() {
        $this->mem = memcache_connect(...);
    }
}

This implements a basic singleton pattern. Instead of constructing the object call mc::getInstance(). Have a look at singletons.

Ross
  • 46,186
  • 39
  • 120
  • 173
  • Funny how people down vote after having read no more than the first sentence of an answer. +1 again. – Tomalak Feb 15 '09 at 13:06
1

You should use dependency injection. The singleton pattern and static constructs are considered bad practice because they essentially are globals (and for good reason -- they cement you to using whatever class you instantiate as opposed to some other).

Here is something like what you should do in order to have easy maintenance.

class MemCache {
    protected $memcache;

    public function __construct(){
        $this->memcache = memcache_connect();
    }
}

class Client {
    protected $MemCache;

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

    public function getMemCache(){
        return $this->MemCache;
    }
}

$MemCache = new MemCache();
$Client = new Client($MemCache);
$MemCache1 = $Client->getMemCache();

// $MemCache and $MemCache1 are the same object. 
// memcache_connect() has not been called more than once.
Ryan Williams
  • 959
  • 7
  • 15