This question already has answers here:
Magic __get getter for static properties in PHP
(6 answers)
Closed 9 years ago.
I'm trying to convert array to object. I want to use magic methods - __get and __set with static properties.
My code:
class UserData {
private static $id, $name, $login;
public function __get($var)
{
return self::$var;
}
public function __set($var, $val)
{
self::{$var} = $val;
}
}
And setting:
foreach($userArray as $key => $val)
{
DaneBilingowe::${$key} = $val;
}
Error:
Fatal error: Cannot access private property UserData::$id
Is it possible to use magic method with static property?
In short, no.
__get() and __set() are instance methods. They are essentially the functions that make up the stdClass(), which is an instance.
If you must set static content in this manner you can give the class a stdClass parameter and a singleton structure that would allow you to magically set and get data.
For example:
class UserData {
protected static $_instance;
protected $_data = array();
public static function get_instance() {
static $initialized = FALSE;
if ( ! $initialized) {
self::$_instance = new UserData;
$initialized = TRUE;
}
return self::$_instance;
}
public function __get($var) {
$self = self::get_instance();
return isset($self->_data[$var]) ? $self->_data[$var] : NULL;
}
public function __set($var, $val) {
$self = self::get_instance();
$self->_data[$var] = $val;
}
}
Then you could go:
$UserData =& UserData::get_instance();
$UserData->var = 'val';
echo $UserData->var; // prints 'val'
I don't recommend using Singletons in PHP, however, because they are pointless. You can read some reasons why in the post Best practice on PHP singleton classes.
Either use a static class or an instance class.
Magic getters and setters are shortcuts. You can implement the same behavior with normal setters and getters. The example below provides the same functionality, but the intent is a lot more clear:
class UserData {
protected $id, $name, $login;
public static function set_name($name) {
self::$name = $name;
}
public static function set_login($login) {
self::$login = $login;
}
public static function get_id() {
return self::$id;
}
public static function get_name() {
return self::$name;
}
public static function get_login() {
return self::login;
}
}
Notice how in the above code $id is not writable. It is only readable. $name and $login are readable and writable. It is easier and less buggy to control reading and writing using normal setters and getters. Magic methods are just that, magic, and usually magic is not concrete and is less understandable in code.
The final point I want to make is, why would UserData be static? Unless you only have 1 user in the entirety of your code it doesn't make sense to have it static. Perhaps I am not getting the whole picture, but something with an id and name should be instantiated so that you can have multiple instances. Otherwise, why have the id because the class itself is unique.
If you really want to use magic methods on static properties, you can but you will need an instance. Though it does not look reasonable, being a programmer itself is not reasonable at all :)
Also user defined classes and objects are not dynamic in php.
You can not add variables to them that easily... So you can use the pattern below:
class UserData {
private static $id, $name, $login, $arr = [];
public function __get($var){
return (array_key_exists(self::$arr, $var)? self::$arr[$var]:null;
}
public function __set($var, $val){
self::$arr[$var] = $val;
}
}
And setting: Well what is DaneBilingowe? I do not now here... But:
$inst = new UserData();
foreach($userArray as $key => $val){
$inst->$key = $val;
}
will work.
But beware, It will work only on class (static) memory.
Also since there is no appropriate filtering for setting names, weird things can happen.
(That means you should add them)
Related
I wrote the following example class:
class Test {
public $baseSymbol;
public $counterSymbol;
public function __construct($baseSymbol,$counterSymbol) {
$this->baseSymbol = $baseSymbol;
$this->counterSymbol = $counterSymbol;
}
public static $var = new Test("CV", "SWR");
}
As you may noticed, I want that the attribute $var of the class Test become a Object of type Test. I did the same thing easily in Java, as a public static variable, but in PHP it's not working...Is there any alternative for what I'm trying to do?
Because you cant set complex types within the definition of the class variables, the only way I can perceive doing this in PHP is to use the magic of procedural code ( Joke ). Now It's important to note that within the file the class exists in you can certainly do something like this.
class Test {
public $baseSymbol;
public $counterSymbol;
protected static $var;
public function __construct($baseSymbol,$counterSymbol) {
$this->baseSymbol = $baseSymbol;
$this->counterSymbol = $counterSymbol;
}
public static setVar(Test $var ){
self::$var = $var;
}
}
Test::setVar( new Test() );
This is pretty standard fare, but as mentioned above by placing the setting code within the class, when the file is loaded the setting is done immediately and pre-loads the class instance before anything else can be done.
This is simply a consequence of not being pre-compiled. When the class is loaded there is no guarantee that a complex object that is required is present, and because of that PHP plays it safe and restricts this ability.
I did change the variable to be protected to keep it encapsulated, this of course is optional. I also added type casting which will insure that only an object or descendant object of Test is used as the input.
Probably not the most favorable solution but one that will work. One last thing you could do just to make sure if you really want to keep it from being changed is to change the setter like this.
public static setVar(){
if( !self::$var ){
self::$var = new Test();
}
}
This way you just call the method with Test::setVar() and once it's got a value it always returns not false, so it will not be changed latter.
One last note, if you truly want a copy of the class itself, this is the Test class with a static instance of itself ( like a singleton ) then do this instead of using the name
public static setVar(){
if( !self::$var ){
self::$var = new self;
}
}
Now to wrap that into a singleton, you can do the class like this.
final class Test {
public $baseSymbol;
public $counterSymbol;
protected static $var;
//no constuction
private function __construct($baseSymbol,$counterSymbol) {
$this->baseSymbol = $baseSymbol;
$this->counterSymbol = $counterSymbol;
}
//no cloning
private function __clone(){}
public static getInstance(){
if( !self::$var ){
self::$var = new self('%', '#');
}
return self::$var
}
}
$Test = Test::getInstance();
Here I go rambling, now ponder saving the instance in an array with a key. Then you can have a Multiton ( is that a thing? );
public static getInstance($type, $baseSymbol,$counterSymbol){
if( !isset(self::$var[$type] )){
self::$var[$type] = new self($baseSymbol, $counterSymbol);
}
return self::$var[$type];
}
It makes a great database class, just saying.
You could access $var via a getter and initialize it there on demand:
private static $var;
public static function getVar()
{
if (null === self::$var) {
self::$var = new Whatever();
}
return self::$var;
}
what I'm trying to achieve (PHP 5.3) is to have an accessor to my representation of, for example, the HTML Body of a page. Instead of echoing everything directly it should be added to an array of entries in that singleton. Example: myBodyClass::add('<h1>Title</h1>');
add() is declared as public static function add($strEntry) {}
Now should I just add them to a static array $entries like self::$entries[] = $strEntry; (class VersionB) or should I use an instance like self::getInstance()->entries[] = $strEntry;? (class VersionA) (whereby getInstance() would of course instanciate ´...new self;´ if necessary)
I don't quite understand the difference yet, I'm afraid.
The second part of my question is how to print the object. The PHP manual is a bit thin about why __toString() cannot be static - but then again I would understand a parser to have a problem distinguishing echo myBodyClass from a constant (so is that the reason?)
Ideally I would like to call add() as often as needed to add all parts of the body, and then use something like echo myHeaderClass, myBodyClass, myFooterClass; at the end of the script, which should invoke the __toString() methods within the classes.
Thanks for pointing me into the correct direction.
Code Example
class VersionA
{
private static $instance = null;
private $entries = array();
private final function __construct(){}
private final function __clone(){}
private static final function getInstance()
{
if (self::$instance === null) :
self::$instance = new self;
endif;
return self::$instance;
}
public static function add($sString)
{
self::getInstance()->entries[] = $sString;
}
public static function getHtml()
{
return implode("\r\n", self::getInstance()->entries);
}
}
class VersionB
{
private static $entries = array();
private final function __construct(){}
private final function __clone(){}
public static function add($sString)
{
self::$entries[] = $sString;
}
public static function getHtml()
{
return implode("\r\n", self::$entries);
}
}
(Copied from comments, as requested by OP...)
You're missing the point of a singleton. There is a difference between a singleton object and a static class. If you want to use methods that act on an object (like __toString()), then you need it to be an object; a static class isn't good enough. If you want to avoid calling getInstance all the time, then set a variable to the object, and pass it around everywhere like you would with other objects, per the Dependency Injection pattern. That would probably be best practice advice anyway.
The thing with a static class is that it isn't really OOP; it's just a bunch of global functions with a shared class name. One may as well use plain functions with a namespace declaration.
But the main reason for using a genuine singleton is swappability. Assuming you follow my advice above and create a single reference to the object that you pass around your code, it becomes a lot easier to swap in an alternative object since you don't have the hard-coded class name being referenced all over the place. This makes it a lot easier to write decent unit tests for your code that uses the class.
Hope that helps.
You should probably not use a static add method.
The idea of a singleton is that you create a single instance of a class so that external objects can interact with that instance. That means that your add method should not be static.
You could do something like:
class MyBodyClass
{
protected $entries = array();
protected $instance;
public static function getInstance()
{
if (is_null($this->instance)) {
$this->instance = new self();
}
return $this->instance;
}
private function __construct() {}
public function add($strEntry)
{
$this->entires[] = $strEntry;
}
}
And call it like this:
MyBodyClass::getInstance()->add('<h1>blah</h1>');
Something like this should work:
class MySingleton
{
public static function getInstance()
{
static $inst = null;
if ($inst === null) {
$inst = new MySingleton();
}
return $inst;
}
private function __construct() { }
public static function add() {}
public function __toString() {
echo 'Something';
}
}
$body = MySingleton::getInstance();
$body::add('Something');
echo $body;
I have a class 'base' and a class 'loader', which looks like this.
class base {
protected $attributes = Array();
public $load = null;
function __construct() {
$this->load = loader::getInstance();
echo $this->load->welcome(); //prints Welcome foo
echo $this->load->name; //prints Foo
echo $this->name; //doesnt print anything and i want it to print Foo
}
public function __get($key) {
return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : null;
}
public function __set($key, $value) {
$this->attributes[$key] = $value;
}
}
class loader {
private static $m_pInstance;
private function __construct() {
$this->name = "Foo";
}
public static function getInstance() {
if (!self::$m_pInstance) {
self::$m_pInstance = new loader();
}
return self::$m_pInstance;
}
function welcome() {
return "welcome Foo";
}
}
$b = new base();
Now what I want is a way to store variables from loader class and access them from base class using $this->variablename.
How can I achieve this? I don't want to use extends. Any idea ?
I don't feel like you've fully understood what coding the OOP way means. And usually Singletons are code smells so I'll just warn you:
There's probably a better way of accomplish you goal. If you provide more informations we will help you out. In its current form the answer is the following; just remember that I higly discourage its implementation in your code.
Assuming that you want to access only public (and non static) loader's variables as this->varname in the base class you should just insert this line in the beginning of the base class constructor:
$this->attributes = get_object_vars(loader::getInstance());
This will basically initialize the attributes array with all the loader public vars so that via your __get() method you can access its value.
On a side note, take a look at Dependency Injection design pattern in order to avoid using Singletons.
Your __get/__set methods access $this->attributes but not $this->load.
You could e.g. do something like (pseudocode)
function __get($key) {
- if $attribute has an element $key->$value return $attribute[$key] else
- if $load is an object having a property $key return $load->$key else
- return null;
}
see also: http://docs.php.net/property_exists
You can make static variable and then you can access this variable from anywhere
public statis $var = NULL;
and you can access it like this
classname::$var;
Let's imagine that we have Registry pattern...
<?php
class Registry
{
private static $objects = array();
private static $instance = null;
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new Registry();
}
return self::$instance;
}
protected function _get($key) {
return ($this->objects[$key]) ? $this->objects[$key] : null;
}
protected function _set($key, $val) {
$this->objects[$key] = $val;
}
public static function get($key) {
return self::getInstance()->_get($key);
}
public static function set($key, $object) {
return self::getInstance()->_set($key, $object);
}
}
?>
Using this realization is really easy...
<?
Registry::set('db', $db_client);
Registry::set('redis', $redis_client);
//Using registered objects is really easy
Registry::get('db')->query("...");
Registry::get('redis')->get("...");
?>
But as you can see, we're adding instances into registry even if we don't need them (yes, it's all about performance).
So, the question is... How to modify Registry pattern to be able to do lazy instantiation?
Here is what I'm looking for...
<?
class Registry
{
private static $objects = array();
private static $instance = null;
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new Registry();
}
return self::$instance;
}
protected function _db() {
if (!$this->objects['db']) {
$this->objects['db'] = new DatabaseAdapter(DB_HOST, DB_NAME, DB_USER, DB_PASSWORD);
}
return $this->objects['db'];
}
protected function _redis() {
if (!$this->objects['redis']) {
$this->objects['redis'] = new Redis(REDIS_HOST, REDIS_DB, REDIS_USER, REDIS_PASSWORD);
}
return $this->objects['redis'];
}
public static function db() {
return self::getInstance()->_db();
}
public static function redis() {
return self::getInstance()->_redis();
}
}
?>
As you can see, DatabaseAdapter() or Redis() will be created only in we'll request them. Everything seems to be ok, but as you can see it's not a standalone class because _db(), _redis() methods contains connection constants etc.
How to avoid it? How can I define registry method within registry class to separate Registy class and objects inside it?
I'm really sorry about my English, but I hope it is clear for you.
Thank you.
PS: All code above was written 1 min. ago and wasn't tested.
If you use global constants you will always have a dependency on the global scope. It doesnt matter where it is. Also, even if you do not use constants, you still have the dependency on the Database class inside the Registry. If you want to dissolve those dependencies, you could use Factory methods on the to be created classes:
public function get($service)
{
if( !this->_data[$service] ) {
// requires PHP 5.2.3
this->_data[$service] = call_user_func($service .'::create');
}
return this->_data[$service];
}
So if you do get('DB'), the code would try to call the static DB::create() method inside the class you intend to create. But like I said, if you use global Constants for the configuration, you would just move the problem into another class.
Your db class could look like this:
class DB
{
protected static $_config;
public static setConfig(array $config)
{
self::_config = $config;
}
public static create()
{
return new self(
self::config['host'],
self::config['db'],
self::config['user'],
self::config['pass']);
}
}
The configuration can be stored inside an external configuration file, which you load and set to the DB class during bootstrap, e.g.
DB::setConfig(parse_ini_file('/path/to/db-config.ini'));
The disadvantage of this is, you have to add create() methods all over the place and all classes must be able to store their own configuration. You could centralize these responsibilities into a Builder pattern. But if you do this, you are half way to implementing an IoC Container anyways, so check out the following resources:
Fabien Potencier: What is Dependency Injection
Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern
Design pattern – Inversion of control and Dependency injection
Note: You are using a "static" modifier for $objects - as you are working with an instance, this is probaby not necessary.
How can I define registry method within registry class to separate Registy class and objects inside it?
They are always separate: Each object inside the registry class is just a reference to the (independent) object. But if this question is about including the appropriate class definition (?) you may use the class_exists() function to load the class as soon as required.
BurninLeo
I am going to use singleton classes to manage both DB connections and references to application settings.
It seems a little messy to have to use the following code in every method in order to access the db class.
$db = DB::getInstance();
Is there a more efficient way of going about it?
Any advice appreciated.
Thanks
I often use the Registry pattern, where this behavior occurs as well. I always set a instance variable in the constructor of my models to point to the Registry entry;
class Registry {
private static $_instance;
private $_registry;
private function __construct() {
$_registry = array();
}
public static function getInstance() {
if (!Registry::$_instance) {
Registry::$_instance = new Registry();
}
return Registry::$_instance;
}
public function add($key, &$entry) {
$this->_registry[$key] = &$entry;
}
public function &get($key) {
return $this->_registry[$key];
}
public function has($key) {
return ($this->get($key) !== null);
}
}
Model example;
class MyModel {
private $_db;
public function __construct() {
$this->_db = Registry::getInstance()->get('dbKey');
}
/* Every function has now access to the DAL */
}
Instantiation example;
$dal = new Db(...);
Registry::getInstance()->add('dbKey', $dal);
...
$model = new MyModel();
$model->doDbStuff();
Another approach is to always pass the reference as a parameter to each constructor.
Of course I only use this behavior when most of the methods in my model use the reference, if only a few (one or two) methods have use of the reference, I call the Registry/Singleton like you showed.
It is not messy. This is an intended behavior of Singletons. And, actually, this is just one line of code. Do you wish to make it even more compact? :)
My preferred method is to create a Base class which all the classes that need db access descend from. Base calls the singleton(s) in its constructor. All its children call their parent constructor. e.g.:
class Base {
protected $db;
public function __construct(){
$this->db = DB::getInstance();
}
}
class Achild extends Base {
protected $var1;
public function __construct($arg){
parent::__construct();
$this->var1=$arg;
}
}
I know what you mean... hate that ::getInstance() stuff! So go and use static methods:
class DB {
private static $db;
public static function getInstance() {
if(!self::$db) {
self::$db = new DBconnector();
}
}
public static function query($query) {
return self::$db->query($query);
}
}
Usage is much nicer:
$result = DB::query('SELECT whatever;');
And if you use PHP 5.3 you can write a __callStatic similar to this, to forward all the method calls to the object:
public static function __callStatic($method, $args) {
call_user_func_array(array(self::$db, $method), $args);
}
And to make me happy, add an __autoloader so that you can access DB without any worries any time!