Through out our application we have something very similar to this:
$cache = App_Cache::getInstance()->newObject(300);
$sig = App_Cache::getCacheName(sha1($sql));
$res = $cache->load($sig);
if ($res === false) {
$res = $db->fetchAll($sql);
$cache->save($res, $sig);
}
The problem at the moment is that we end up creating a new object of Zend_Cache each time and for each request this can end up getting called 300+ times.
class App_Cache {
protected static $_instance = null;
public static $enabled = true;
protected $frontend = null;
protected $backend = null;
protected $lifetime = null;
public function __construct() { }
public static function getInstance() {
if (is_null(self::$_instance))
self::$_instance = new self();
return self::$_instance;
}
public function newObject($lifetime = 0) {
return Zend_Cache::factory('Core','Memcached',$this->getFrontend($lifetime),$this->getBackend());
}
public static function getCacheName($suffix) {
$suffix = str_replace(array("-","'","#",":"), "_",$suffix);
return "x{$suffix}";
}
In Magento they seem to create it once in the __construct, where as Concrete5 create a static property.
My question is whats the best solution?
I think your getInstance() method should be returning your instance of Zend_Cache rather than App_Cache. Try something like this:
class App_Cache
{
protected static $_instance = null;
protected static $_cacheInstance = null;
public static $enabled = true;
protected $frontend = null;
protected $backend = null;
protected $lifetime = null;
public function __construct() { }
public static function getInstance() {
if (is_null(self::$_instance))
self::$_instance = new self();
return self::$_instance;
}
public function newObject($lifetime = 0) {
if (is_null(self::$_cacheInstance))
self::$_cacheInstance = Zend_Cache::factory('Core','Memcached',$this->getFrontend($lifetime),$this->getBackend());
return self::$_cacheInstance;
}
public static function getCacheName($suffix) {
$suffix = str_replace(array("-","'","#",":"), "_",$suffix);
return "x{$suffix}";
}
}
Notice that I changed the newObject() method to be static and added the parameter for it into getInstance(). In this way, you can call getInstance() throughout your code and it will only create the Zend_Cache instance once and then save it in the App_Cache object's $_instance variable.
Ok, changed the code to hold a static instance of the Zend_Cache object and return it if requested. This will only create the instance once. I think the method name should be changed to getCache() or something like that so it's clearer what it is doing.
Related
I use hook mechanism in Codeigniter. The kind of hook is post_controller_constructor.
There is one private object inside class hook`s:
private $settings = array();
This object is filled after executing hook.
How I can get access to $settings from libraries CI and controllers?
Class:
<? class LCode_module
{
private $CI;
private $_default_lang = "en";
private $_sufixLangDefault = "_EN";
private $allowedLanguages = array();
private $_countryCurrent;
private $countries = array();
private $languages = array();
private $settings = array();
public function __construct()
{
$this->CI =& get_instance();
/* Load lists */
$this->CI->load->library('listdata');
$this->countries['country'] = $this->CI->listdata->country;
$this->countries['country_code'] = $this->CI->listdata->country_code;
$this->countries['country_lang'] = $this->CI->listdata->country_lang;
$this->languages = $this->CI->listdata->languages_sys;
}
public function route()
{
//Here I put data to $settings
}
}
Method route is init method in hook
At the end of constructor:
/* Add object of class to GI instance */
$this->CI->LCode_module = new stdClass;
$this->CI->LCode_module->settings = &$this->settings;
After I try to get data in controller:
$CI =& get_instance();
$c = $CI->LCode_module;
var_dump($c); // NULL
Use this as your class, I created a static option which you can use whenever you need:
<?php
class LCode_module
{
private $CI;
private $_default_lang = "en";
private $_sufixLangDefault = "_EN";
private $allowedLanguages = array();
private $_countryCurrent;
private $countries = array();
private $languages = array();
private $settings = array();
private static $instance;
private static $static_settings;
public function __construct()
{
$this->CI =& get_instance();
/* Load lists */
$this->CI->load->library('listdata');
$this->countries['country'] = $this->CI->listdata->country;
$this->countries['country_code'] = $this->CI->listdata->country_code;
$this->countries['country_lang'] = $this->CI->listdata->country_lang;
$this->languages = $this->CI->listdata->languages_sys;
self::$instance = &$this;
self::$static_settings = &$this->settings;
}
public function route()
{
//Here I put data to $settings
}
public static function getInstance(){
if (is_null(self::$instance)) { self::$instance = new self(); }
return self::$instance;
}
public static function settings($key = NULL){
$instance = self::getInstance();
if(is_null($key)) return $instance::$static_settings;
return (array_key_exists($key, $instance::$static_settings) ? $instance::$static_settings[$key] : null);
}
}
Then you just call
LCode_module::settings()
when you need to retrieve the settings
This does imply parallel singleton use, which isn't exactly best practice, but it should do the trick for now since hooks only get loaded once. I'm sure CI has a way of performing this, but I'm drawing a blank on it right now.
in this below class i want to use class like with static methods and for use class methods without create new object from parent.
for example:
<?php
class Permission
{
protected $permission = false;
protected $id = 0;
public static function __construct()
{
return new static;
}
public function user( $id )
{
$this->id = $id;
}
public function check()
{
$this->permission = true;
}
public function item( $item )
{
return $item;
}
}
$bar = Permission::user(100)->item("HELLO");
print_r($bar);
this code not working and have problem. how to resolve this class problem?
That will not work because user method is not static, try changing this two methods, and this is good way of generating objects
public function __construct($id)
{
$this->id = $id;
}
public static function user( $id )
{
return new static($id);
}
I'd suggest you a singleton pattern, like this
class Permission
{
static protected $permission = false;
static protected $id = 0;
private static $_instance = null;
private function __construct () { }
public static function getInstance()
{
if (self::$_instance === null) {
self::$_instance = new self;
}
return self::$_instance;
}
public static function user( $userId )
{
self::$id = $userId;
return self::$_instance;
}
public static function check()
{
self::$permission = true;
return self::$_instance;
}
public static function item( $item )
{
return $item;
}
}
$bar = Permission::getInstance()->user(100)->item("HELLO");
print_r($bar);
You can chain methods in 'dynamic' classes by returning $this at the end of method (remember, you have a static).
class A {
public function someMethod()
{
// some code
return $this
}
public function otherMethod()
{
// some code
return $this
}
$a = new A();
$a->someMethod()->otherMethod();
}
I have an extended class with an overriden method doSomething().
For some reason the inherited class' method never runs only the base one.
class cDemoClass {
public static function getInstance() {
static $instance = null;
if ($instance === null)
$instance = new cDemoClass();
return $instance;
}
private function __construct() {
}
protected function doSomething() {
echo 'do something';
}
public function call_me() {
$this->doSomething();
}
}
class cDemoClassEx extends cDemoClass {
protected function doSomething() {
echo 'do something differently';
}
}
$baseclass = cDemoClass::getInstance();
$baseclass->call_me();
echo '<br/>';
$extendedclass = cDemoClassEx::getInstance();
$extendedclass->call_me();
result:
do something
do something
The second one should be "do something differently" at least that's what I'm expecting.
Can anyone tell me what I'm doing wrong? Thanks
In this case, you need using late static binding (5.3+). Change in parent method getInstance line :
$instance = new cDemoClass();
to
$instance = new static();
You will get:
do something
do something differently
Read more about this feature here: http://www.php.net/manual/en/language.oop5.late-static-bindings.php
Because cDemoClassEx::getInstance(); is still returning new cDemoClass();. You have to also overwrite the getInstance() method:
class cDemoClass {
public static function getInstance() {
static $instance = null;
if ($instance === null)
$instance = new cDemoClass();
return $instance;
}
private function __construct() {
}
protected function doSomething() {
echo 'do something';
}
public function call_me() {
$this->doSomething();
}
}
class cDemoClassEx extends cDemoClass {
public static function getInstance() {
static $instance = null;
if ($instance === null)
$instance = new cDemoClassEx();
return $instance;
}
private function __construct() {
}
protected function doSomething() {
echo 'do something differently';
}
}
$baseclass = cDemoClass::getInstance();
$baseclass->call_me();
echo '<br/>';
$extendedclass = cDemoClassEx::getInstance();
$extendedclass->call_me();
You have to override with the cDemoClassEx::getInstance() and change this line
$instance = new cDemoClass();
into
$instance = new cDemoClassEx();
You will also need to declare the cDemoClass::__construct() as protected or simply override it in cDemoClassEx.
We have a class that holds a public array called $saved that contains lots of data required to share between methods (example below)...
class Common {
public $saved = array();
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUserID() {
return $this->saved['user_data']['id'];
}
}
There are literally thousands of lines of code that work like this.
The problem is that new instance of classes that extend Common are being made within some methods so when they access $saved it does not hold the same data.
The solution is to make $saved a static variable, however I can't change all of the references to $this->saved so I want to try and keep the code identical but make it act static.
Here is my attempt to make $this->saved calls static...
class PropertyTest {
private $data = array();
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}
class Common {
public $saved;
private static $_instance;
public function __construct() {
$this->saved = self::getInstance();
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new PropertyTest();
self::$_instance->foo = array();
}
return self::$_instance->foo;
}
}
This doesn't quite work when setting a variable it doesn't seem to stay static (test case below)...
class Template extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data'] = array('name' => 'bob');
$user = new User();
}
}
class User extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data']['name'] .= " rocks!";
$this->saved['user_data']['id'] = array(400, 10, 20);
}
}
$tpl = new Template();
print_r($tpl->saved['user_data']);
$this->saved is empty when User gets initialized and doesn't seem to be the same variable, the final print_r only shows an array of name => bob.
Any ideas?
First of all, I have to say that, IMO, it is not that good to use an instance's property as a class's property ($saved is not declared as static but its value is shared with all instance).
Here is a working version http://codepad.org/8hj1MOCT, and here is the commented code. Basically, the trick is located in using both ArrayAccess interface and the singleton pattern.
class Accumulator implements ArrayAccess {
private $container = array();
private static $instance = null;
private function __construct() {
}
public function getInstance() {
if( self::$instance === null ) {
self::$instance = new self();
}
return self::$instance;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
class Common {
public $saved = null;
public function __construct() {
// initialize the "saved" object's property with the singleton
// that variable can be used with the array syntax thanks to the ArrayAccess interface
// so you won't have to modify your actual code
// but also, since it's an object, this local "$this->saved" is a reference to the singleton object
// so any change made to "$this->saved" is in reality made into the Accumulator::$instance variable
$this->saved = Accumulator::getInstance();
}
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUser() {
return $this->saved['user_data'];
}
}
class Template extends Common {
// you can redeclare the variable or not. Since the property is inherited, IMO you should not redeclare it, but it works in both cases
// public $saved = null;
public function __construct() {
// maybe we can move this initialization in a method in the parent class and call that method here
$this->saved = Accumulator::getInstance();
}
}
I think there are a number of issues with this implementation that could well come back to bite you. However, in your current implementation your contructing a new instance (albeit through a static call) every time.
Instead use getInstance() as your singleton hook, and make your __construct private, as you'll only be accessing it from with the context of the Common class.
Like so:
class Common {
public $saved;
private static $_instance;
private function __construct() {
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
... any other modifications you want to make ....
}
return self::$_instance;
}
}
And don't ever run parent::_construct(), instead always use the getInstance() method.
You might also want to ditch the idea of extending this singleton class. This is really a bad antipattern and could cost you a number of issues in the long run. Instead just maintain a Common class that other classes can read / write to. As its a singleton you don't need to worry about injection.
I seem to have solved the problem, by making $this->saved a reference to a static variable it works...
class Common {
private static $savedData = array();
public $saved;
public function __construct() {
$this->saved =& self::$savedData;
}
}
I'm a AS3 coder and i do a bit of php and i am having a hard time doing a static class that can cache variables.
Here's what i have so far :
<?php
class Cache {
private static $obj;
public static function getInstance() {
if (is_null(self::$obj)){
$obj = new stdClass();
}
if (!self::$instance instanceof self) {
self::$instance = new self;
}
return self::$instance;
}
public static function set($key,$value){
self::$obj->$key = $value;
}
public static function get($key){
return self::$obj->$key;
}
}
?>
And i use the following to set my variable into an object of my static class :
<?php
include 'cache.php';
$cache = new Cache();
$cache->set("foo", "bar");
?>
And this is retrieve the variable
<?php
include 'cache.php';
$cache = new Cache();
$echo = $cache->get("foo");
echo $echo //doesn't echo anything
?>
What am i doing wrong ? Thank you
I've adapted #prodigitalson's code above to get something rudimentary that works (and has much room for improvement):
class VarCache {
protected static $instance;
protected static $data = array();
protected function __construct() {}
public static function getInstance() {
if(!self::$instance) {
self:$instance = new self();
}
return self::$instance;
}
public function get($key) {
self::getInstance();
return isset(self::$data[$key]) ? self::$data[$key] : null;
}
public function set($key, $value) {
self::getInstance();
self::$data[$key] = $value;
}
}
Usage
VarCache::set('foo', 'bar');
echo VarCache::get('foo');
// 'bar'
You'll want this class to be available everywhere you need it, and if you want it to persist between requests, I'd consider using Memcached or something similar, which will give you everything you need.
You could alternatively use some SPL functions, like ArrayObject, if you wanted to be clever :)
Try this:
class VarCache {
protected $instance;
protected $data = array();
protected __construct() {}
public static function getInstance()
{
if(!self::$instance) {
self:$instance = new self();
}
return self::$instance;
}
public function __get($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function __set($key, $value) {
$this->data[$key] = $value;
}
}
// usage
VarCache::getInstance()->theKey = 'somevalue';
echo VarCache::getInstance()->theKey;