I have a class with a method that returns a new instance of itself.
This allows me to use one single object without having to declare new ones over and over with the same values. For those wondering why not extending the class or something else, it just makes my life easier and it works like a charm, but my final purpose is to remove them all in one line.
for example:
$foo = new myClass('name1','ab','cd');
$bar = $foo->duplicate();
//duplicate() saves the new object in itself in an array:
array_push($this->instance,new myClass('name1','ab','cd'));
//and returns a pointer to the saved instance.
return end($this->instance);
$foo and $bar now share the same methods and values but those values can be modified separately.
$foo->changeName('newName');
$bar->changeName('newName2');
My question here is, if I unset the first class created unset($foo) will PHP automatically unset the other instances ($bar) or will the garbage collector remove them eventually?
I have tested it by unsettling $foo and gives me an error when I call $foo but not when I call $bar.
What I am trying to do is to unset all the instances of that class at once.
Thanks.
Automatic cleanup?
No, PHP isn't awared about your architecture. It will not remove objects "cascade", they are independent entities - and, more, can belong to different scopes, for example. Simple code:
class Test
{
protected $id = null;
protected $uniqid = null;
public function __construct($id)
{
$this->id = $id;//user-passed variable
$this->uniqid = uniqid();//internal: to identify instance
}
public function getCopy()
{
return new self($this->id);
}
public function getIdentity()
{
return $this->uniqid;
}
}
$foo = new Test(3);
$bar = $foo->getCopy();
var_dump($foo->getIdentity());//valid
unset($foo);
//still valid: bar has nothing to do with foo
var_dump($bar->getIdentity());
By the way, for copying you can use clone in PHP (that, however, will result in object cloning, obviously)
Simple way
Most simple way to resolve a matter is to iterate through $GLOBALS, checking it with instanceof. This has serious weakness: inner function/method scopes would not be affected:
//static since doesn't belong to any instance:
public static function cleanup()
{
foreach($GLOBALS as $name=>$var)
{
if($var instanceof self)
{
unset($GLOBALS[$name]);
}
}
}
-and
$foo = new Test(3);
$bar = $foo->getCopy();
var_dump($foo->getIdentity(), $bar->getIdentity());//valid
Test::cleanup();
//2 x notice:
var_dump($foo, $bar);
Note, that is has nothing to do with "child" mechanics (i.e. it will clean all instances in global scope - no matter which was copied from which).
Common case
Sample above will not do the stuff in common case. Why? Imagine that you'll have holder class:
class Holder
{
protected $obj = null;
public function __construct($obj)
{
$this->obj = $obj;
}
public function getData()
{
return $this->obj;
}
}
and you'll pass instance to it:
$foo = new Test(3);
$bar = $foo->getCopy();
$baz = new Holder($bar);
-so then you'll have no chances to handle even this simple situation in common case. And with more complex situations you will also be stuck.
What to do?
I'd recommend: destroy objects explicitly when you need to do that. Implicit unset is a side-effect, and even if you'll maintain that somehow (I can imagine Observer pattern + some global registry for that) - it will be horrible side-effect, that will kill readability for your code. And same is about code, that uses $GLOBALS I've written above - I do not recommend to act such way in any case.
Try the below code to clear all php objects.
public function clearAllVars()
{
$vars = get_object_vars($this);
foreach($vars as $key => $val)
{
$this->$key = null;
}
}
}
From what I understand from the PHP reference here :
http://www.php.net/manual/en/features.gc.refcounting-basics.php
destroying the main instance will not automatically destroy all other instances since they do not ultimately point to the same 'zval'.
However the garbage collection will eventually destroy all instances if they are not referenced anymore. Since in your example $bar still references the second instance, it will not be destroyed.
What you could do if you want to unset them all at the same time :
Use a static array referencing all the instances of your object
Every time you create a new object add a reference to this object in the static array of the class
Use a static function unsetAll() which loops through this array and unset one by one all instances
Related
I've found the piece of code below in several places around the web and even here on Stack Overflow, but I just can't wrap my head around it. I know what it does, but I don't know how it does it even with the examples. Basically it's storing values, but I don't know how I add values to the registry. Can someone please try to explain how this code works, both how I set and retrieve values from it?
class Registry {
private $vars = array();
public function __set($key, $val) {
$this->vars[$key] = $val;
}
public function __get($key) {
return $this->vars[$key];
}
}
It's using PHP's hacked on property overloading to add entries to and retrieve entries from the private $vars array.
To add a property, you would use...
$registry = new Registry;
$registry->foo = "foo";
Internally, this would add a foo key to the $vars array with string value "foo" via the magic __set method.
To retrieve a value...
$foo = $registry->foo;
Internally, this would retrieve the foo entry from the $vars array via the magic __get method.
The __get method should really be checking for non-existent entries and handle such things. The code as-is will trigger an E_NOTICE error for an undefined index.
A better version might be
public function __get($key)
{
if (array_key_exists($key, $this->vars)) {
return $this->vars[$key];
}
// key does not exist, either return a default
return null;
// or throw an exception
throw new OutOfBoundsException($key);
}
You might want to check out PHP.NET - Overloading
Basically, you would do...
$Registry = new Registry();
$Registry->a = 'a'; //Woo I'm using __set
echo $Registry->a; //Wooo! I'm using __get
So here, I'm using __set($a, 'This value is not visible to the scope or nonexistent')
Also, I'm using __get($a);
Hope this helped!
I'm trying to determine whether or not a given object has been created. I see there are methods for class_exists and method_exists but what I'm trying to figure out is if new Foo() has been called (and hopefully figure out what variable it was assigned to, but that is not as important).
If I understand you correctly you are trying to initialize object only once. If this is the case why not to use singleton pattern? This will free you from checking of existence of object:
class MyClass {
private static $instance;
private function __construct() {}
public static function getInstance() {
if (empty(self::$instance)) {
self::$instance = new __CLASS__();
}
return self::$instance;
}
}
You can use this code like this:
$obj = MyClass::getInstance();
With similar approach you can define additional helper static methods which will check whether object was instantiated or not. You just need to keep instance statically inside your class.
Edit: After seeing the reason you are needing this in the comments above, this is definitely not the way to go about it.
Here ya go. It could be optimized a little, but should work fine.
Also, passing get_defined_vars() to the function every time is necessary because that function only retrieves the vars within the scope it's called. Calling it inside the function would only give the vars within the scope of that function.
<?php
function isClassDeclared($class_name, $vars, $return_var_name = FALSE) {
foreach ($vars AS $name => $val) {
if (is_object($val) && $val instanceof $class_name)
return $return_var_name ? $name : TRUE;
}
return FALSE;
}
class Foo {}
$foo = new Foo;
echo '<pre>';
var_dump(isClassDeclared('foo', get_defined_vars(), TRUE));
var_dump(isClassDeclared('bar', get_defined_vars(), TRUE));
echo '</pre>';
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
To pass value of a variable in one function to another function in same class
Can a value of a variable in one function be made available in another function on the same class using "GLOBAL" in PHP. If so please suggest how can this be achieved using Global.
You don't need to make a variable GLOBAL if you're within an object.
class myClass {
public $myVar = "Hello";
function myFunction() {
echo $this->$myVar;
}
}
This is one of the main points of objects - that you can assign different values to variables and get/set those variables within the different methods. And also that you can create multiple instances of objects each holding different information within the same structure and with the same methods available.
Additionally to what #Codecraft said (about using public properties), you can use:
indeed global variable (which is really something you should avoid doing),
passing values in parameters,
static variable within class,
Below is the example of using static variable (private), because I think this suits your needs best:
class MyClass {
private static $_something;
public function write() {
static::$_something = 'ok';
}
public function read() {
return static::$_something;
}
}
$x = new MyClass();
$x->write();
var_dump($x->read());
which outputs:
string(2) "ok"
This is in fact something like a global, but available only from inside your class (because of the keyword "private") and common among every instance of the class. If you use setting some non-static property, it will change across different instances of the class (one object may have different value stored in it than the other object has).
Comparison of solutions based on static and non-static variables:
Solution based on static variable will give you really global-like behaviour (value passed across different instances of the same class):
class MyClass {
private static $_something;
public function write() {
static::$_something = 'ok';
}
public function read() {
return static::$_something;
}
}
// first instance
$x = new MyClass();
$x->write();
// second instance
$y = new MyClass();
var_dump($y->read());
which outputs:
string(2) "ok"
And the solution based on non-static variables will look like:
class MyClass {
private $_something;
public function write() {
$this->_something = 'ok';
}
public function read() {
return $this->_something;
}
}
// first instance
$x = new MyClass();
$x->write();
// second instance
$y = new MyClass();
var_dump($y->read());
but will output:
NULL
which means that in this case the second instance has no value assigned for the variable you wanted to behave like "global".
Yes, a value of a variable in one function can be made available in another function on the same class using "GLOBAL". The following code prints 3:
class Foo
{
public function f1($arg) {
GLOBAL $x;
$x = $arg;
}
public function f2() {
GLOBAL $x;
return $x;
}
}
$foo = new Foo;
$foo->f1(3);
echo $foo->f2();
However, the usage of global variables is usually a sign of poor design.
Note that while keywords in PHP are case-insensitive, it is custom to use lower case letters for them. Not also that the superglobal array that contains all global variables is called $GLOBALS, not GLOBAL.
I'm not sure if this is a trivial questions but in a PHP class:
MyClass:
class MyClass {
public $var1;
public $var2;
constructor() { ... }
public method1 () {
// Dynamically create an instance variable
$this->var3 = "test"; // Public....?
}
}
Main:
$test = new MyClass();
$test->method1();
echo $test->var3; // Would return "test"
Does this work?? How would I get this to work? Ps. I wrote this quickly so please disregard any errors I made with setting up the class or calling methods!
EDIT
What about making these instance variables that I create private??
EDIT 2
Thanks all for responding - Everyone is right - I should have just tested it out myself, but I had an exam the next morning and had this thought while studying that I wanted to check to see if it worked. People keep suggesting that its bad OOP - maybe but it does allow for some elegant code. Let me explain it a bit and see if you still think so. Here's what I came up with:
//PHP User Model:
class User {
constructor() { ... }
public static find($uid) {
$db->connect(); // Connect to the database
$sql = "SELECT STATEMENT ...WHERE id=$uid LIMIT 1;";
$result = $db->query($sql); // Returns an associative array
$user = new User();
foreach ($result as $key=>$value)
$user->$$key = $value; //Creates a public variable of the key and sets it to value
$db->disconnect();
}
}
//PHP Controller:
function findUser($id) {
$User = User::find($id);
echo $User->name;
echo $User->phone;
//etc...
}
I could have just put it in an associative array but I can never correctly name that array something meaningful (ie. $user->data['name'] ... ugly.) Either way you have to know what is in the database so I do not really understand what the argument is that its confusing, especially since you can just var dump objects for debugging.
Why dont you just write the code and see for yourself?
<?php
class Foo
{
public function __construct()
{
$this->bar = 'baz';
}
}
$foo = new Foo;
echo $foo->bar; // outputs 'baz'
and
var_dump($foo);
gives
object(Foo)#1 (1) {
["bar"] => string(3) "baz"
}
but
$r = new ReflectionObject($foo);
$p = $r->getProperty('bar');
var_dump($p->isPublic());
will throw an Exception about 'bar' being unknown, while
$r = new ReflectionObject($foo);
$p = $r->getProperties();
var_dump($p[0]->isPublic());
will return true.
Now, should you do this type of assignment? Answer is no. This is not good OOP design. Remember, OOP is about encapsulation. So, if bar is describing some public property of the class, make it explicit and declare it in your class as public $bar. If it is supposed to be private declare it as private $bar. Better yet, dont use public properties at all and make them protected and provide access to them only through getters and setters. That will make the interface much more clearer and cleaner as it conveys what interaction is supposed to be possible with an object instance.
Assigning properties on the fly here and there across your code, will make maintaining your code a nightmare. Just imagine somewhere along the lifecylce of Foo someone does this:
$foo = new Foo;
$foo->monkey = 'ugh';
echo $foo->monkey; // outputs 'ugh'
Now, from looking at the class definition above, there is absolutely no way, a developer can see there is now a monkey patched into Foo. This will make debugging a pain, especially if code like this is frequent and distributed across multiple files.
Yes that will indeed work. Auto-created instance variables are given public visibility.
yes that works as you'd hope/expect.
I you wanted to make private variables on the fly you could use php magic functions to emulate this, e.g
MyClass
<?php
class MyClass {
public $var1;
public $var2;
private $data = array();
public function __get($key) {
// for clarity you could throw an exception if isset($this->data[$key])
// returns false as it is entirely possible for null to be a valid return value
return isset($this->data[$key]) ? return $this->data[$key] : null;
}
public function __set($key, $value) {
$this->data[$key] = $value;
}
}
?>
Main
<?php
$test = new MyClass();
$test->myVar = 'myVar is technically private, i suppose';
echo $this->myVar; // 'myVar is technically private
?>
Although these dynamically created variables are technically private, they are infact publicly accessible... i cannot image the purpose for wanting to dynamically create private instance variables. I would question your design.
Did you try it?
It is possible but you might get strict errors. If you dynamically need to create these variables, you are probably doing something wrong.
You should either change this into a function:
function var($no) { .. }
or use __get (http://ca.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members)
I have built a class in PHP and I must declare a class variable as an object. Everytime I want to declare an empty object I use:
$var=new stdClass;
But if I use it to declare a class variable as
class foo
{
var $bar=new stdClass;
}
a parse error occurs. Is there a way to do this or must I declare the class variable as an object in the constructor function?
PS: I'm using PHP 4.
You can only declare static values this way for class members, i.e. ints, strings, bools, arrays and so on. You can't do anything that involves processing of any kind, like calling functions or creating objects.
You'll have to do it in the constructor.
Relevant manual section:
In PHP 4, only constant initializers for var variables are allowed. To initialize variables with non-constant values, you need an initialization function which is called automatically when an object is being constructed from the class. Such a function is called a constructor (see below).
Classes and Objects (PHP 4). A good read everytime!
You should not create your object here.
You should better write setter and getter
<?php
class foo
{
var $bar = null;
function foo($object = null)
{
$this->setBar($object);
}
function setBar($object = null)
{
if (null === $object)
{
$this->bar = new stdClass();
return $this;
}
$this->bar = $object;
return $this;
}
}
By the way, you should use PHP5 to work with OOP, which is more flexible...