I'm sure this question has been asked a thousand times, but i had trouble finding an answer i could understand or use anywhere.
In my project, i need to use my sql class and other misc classes, in alot of the classes. Then i'm qurious to know what's the best way performance wise to pass the objects.
Should i pass the objects to the classes construct as reference?
class MyClass {
private $_link;
function __construct(&$db) {
$this->_link =& $db;
}
}
or value..
class MyClass {
private $_link;
function __construct($db) {
$this->_link = $db;
}
}
or simply create a new object?
class MyClass {
private $_link;
function __construct() {
$this->_link = new DB();
}
}
If you are using PHP5+, in almost all cases, objects are passed by reference by default.
As objects are already passed "by reference" in PHP5+ then using & you would actually pass a "reference to a reference to an object" not just a simple "reference to an object". This can be critical because it will allow the function within its local scope to change the actual reference globally and potentially remove the object entirely. For example one would think that the following example of passing the object by ref and passing by "normally" is completely the same:
$aa = new stdclass;
$aa->aa = 1;
byRef($aa);
function byRef(&$aaa) {
$aaa->aa = 2;
}
var_dump($aa); // Outputs {aa:2}
$bb = new stdclass;
$bb->bb = 1;
byVal($bb);
function byVal($bba) {
$bba->bb = 2;
}
var_dump($bb); // Outputs {bb:2}
Judging by the output it looks the same. But is it a good practice? Depends what you wanted to do. Look at the same example where we destroyed the original reference and "deleted" the object:
$aa = new stdclass;
$aa->aa = 1;
byRef($aa);
function byRef(&$aaa) {
$aaa->aa = 2;
$aaa = 0; // We are changing $aa, not $aaa, because $aaa is just a reference
}
var_dump($aa); // Outputs int:0, $aa is not an object anymore
$bb = new stdclass;
$bb->bb = 1;
byVal($bb);
function byVal($bba) {
$bba->bb = 2;
$bba = 0;
}
var_dump($bb); // Outputs {bb:0}
The code speaks for itself. In some cases this is needed, in other this is critical. In my opinion unless you really know what are you doing do not pass by reference.
Related
How do I pass a reference to an object constructor, and allow that object to update that reference?
class A{
private $data;
function __construct(&$d){
$this->data = $d;
}
function addData(){
$this->data["extra"]="stuff";
}
}
// Somewhere else
$arr = array("seed"=>"data");
$obj = new A($arr);
$obj->addData();
// I want $arr to contain ["seed"=>"data", "extra"=>"stuff"]
// Instead it only contains ["seed"=>"data"]
You must store it everywhere as a reference.
function __construct (&$d) {
$this->data = &$d; // the & here
}
You'll have to tell PHP to assign a reference also to the private member data like this:
$this->data = &$d;
Depending on the context, you may not want to use references to external arrays, and it might be better to have that array inside an object that handles it.
Aslo notice that the constructor is called __construct not __construction.
This would do what you are asking for:
class Test {
private $storage;
public function __construct(array &$storage)
{
$this->storage = &$storage;
}
public function fn()
{
$this->storage[0] *= 10;
}
}
$storage = [1];
$a = new Test($storage);
$b = new Test($storage);
$a->fn();
print_r($a); // $storage[0] is 10
print_r($b); // $storage[0] is 10
$b->fn();
print_r($a); // $storage[0] is 100
print_r($b); // $storage[0] is 100
Alternative 1
Instead of using an array, you can also use an ArrayObject, ArrayIterator or SplFixedArray. Since those are objects, they will be passed by reference. All of these implement ArrayAccess so you can access them via square brackets, e.g.
$arrayObject = new ArrayObject;
$arrayObject['foo'] = 'bar';
echo $arrayObject['foo']; // prints 'bar'
Alternative 2
Instead of using a generic type, use a dedicated type. Find out what you are storing in that array. Is it a Config? A Registry? A UnitOfWork? Find out what it really is. Then make it an object and give it an API reflecting the responsibilities. Then inject that object and access it through that API.
See this paper by Martin Fowler to some guidance on When To Make A Type
How do I pass a reference to an object constructor, and allow that object to update that reference?
class A{
private $data;
function __construct(&$d){
$this->data = $d;
}
function addData(){
$this->data["extra"]="stuff";
}
}
// Somewhere else
$arr = array("seed"=>"data");
$obj = new A($arr);
$obj->addData();
// I want $arr to contain ["seed"=>"data", "extra"=>"stuff"]
// Instead it only contains ["seed"=>"data"]
You must store it everywhere as a reference.
function __construct (&$d) {
$this->data = &$d; // the & here
}
You'll have to tell PHP to assign a reference also to the private member data like this:
$this->data = &$d;
Depending on the context, you may not want to use references to external arrays, and it might be better to have that array inside an object that handles it.
Aslo notice that the constructor is called __construct not __construction.
This would do what you are asking for:
class Test {
private $storage;
public function __construct(array &$storage)
{
$this->storage = &$storage;
}
public function fn()
{
$this->storage[0] *= 10;
}
}
$storage = [1];
$a = new Test($storage);
$b = new Test($storage);
$a->fn();
print_r($a); // $storage[0] is 10
print_r($b); // $storage[0] is 10
$b->fn();
print_r($a); // $storage[0] is 100
print_r($b); // $storage[0] is 100
Alternative 1
Instead of using an array, you can also use an ArrayObject, ArrayIterator or SplFixedArray. Since those are objects, they will be passed by reference. All of these implement ArrayAccess so you can access them via square brackets, e.g.
$arrayObject = new ArrayObject;
$arrayObject['foo'] = 'bar';
echo $arrayObject['foo']; // prints 'bar'
Alternative 2
Instead of using a generic type, use a dedicated type. Find out what you are storing in that array. Is it a Config? A Registry? A UnitOfWork? Find out what it really is. Then make it an object and give it an API reflecting the responsibilities. Then inject that object and access it through that API.
See this paper by Martin Fowler to some guidance on When To Make A Type
I need to construct a class with alot of variables directly from the Database, For simplicity we'll name them 'userX', I've looked into ORM just a little, but its way over my head.
Essentially I thought I could use my procedural code
for ($i=0; $i<100; $i++) {
public ${'user'.$i};
}
But, in a class
class test() {
private $var1;
for ($i=0; $i<10000; $i++) {
public ${'user'.$i};
}
function __constructor .....
}
Obviously not.. but it leaves me with the same problem, how can I add $user0, $user1, $user2, etc etc, without having to type all 10k of them in..
Obviously, it would be 1000x easier to just grab the names from the Database, but again, that looks even harder to code. Should I buckle down and grab them all ORM style?
You could simply use the magic accessors to have as many instance attributes as you wish :
class test{
private $data;
public function __get($varName){
if (!array_key_exists($varName,$this->data)){
//this attribute is not defined!
throw new Exception('.....');
}
else return $this->data[$varName];
}
public function __set($varName,$value){
$this->data[$varName] = $value;
}
}
Then you could use your instance like this :
$t = new test();
$t->var1 = 'value';
$t->foo = 1;
$t->bar = 555;
//this should throw an exception as "someVarname" is not defined
$t->someVarname;
And to add a lot of attributes :
for ($i=0;$i<100;$i++) $t->{'var'.$i} = 'somevalue';
You could also initialize a newly created instance with a given set of attributes
//$values is an associative array
public function __construct($values){
$this->data = $values;
}
Try $this->{$varname}
class test
{
function __construct(){
for($i=0;$i<100;$i++)
{
$varname='var'.$i;
$this->{$varname}=$i;
}
}
}
You can use variable variables ($$var) - content of one variable is used as a name for other variable (double $$)
Therefore not $this->varname but $this->$varname.
class test
{
for($i=0;$i<100;$i++)
{
$varname='var'.$i;
$this->$varname=$i;
}
}
This will dynamically create 100 variables with names $var0, $var1 ...
In PHP, is it possible to have a function within a class that's non-static, but also isn't an instance function?
For example, if I have the following:
class A
{
public $i;
function setValue($val) {
$this->i = $val;
}
}
$a1 = new A;
$a1->setValue(5);
echo $a1->i; // result: 5
$a2 = new A;
$a2->setValue(2);
echo $a2->i; // result: 2
Can I add a function to that class that can have "visibility" of all instances of itself so I can do something like so (which I know doesn't work, but communicates my thought):
class A
{
public $i;
function setValue($val) {
$this->i = $val;
}
function getTotal() {
return sum($this->i); // I know sum() isn't a built-in function, but it helps explain what I want. I'm not sure if $this makes sense here too.
}
}
$a1 = new A;
$a1->setValue(5);
echo $a1->i; // result: 5
$a2 = new A;
$a2->setValue(2);
echo $a2->i; // result: 2
echo A::getTotal(); // returns: 7
I guess A::getTotal() means getTotal() would need to be static, but if it was static then it wouldn't then be able to "see" each class instance.
Is this type of thing possible, and what's the correct terminology I should be using?
No, there is no built-in instance enumeration, you will need to keep references to each instantiated object yourself. You can keep an array of instances in a static property of the class and populate it in your __construct(). You can then have a static method loop over this array and process all instances.
I think you would like something like this:
class A
{
public $i;
function setValue($val) {
$this->i = $val;
}
}
$a1 = new A;
$a1->setValue(5);
echo $a1->i; // result: 5
$a2 = new A;
$a2->setValue(2);
echo $a2->i; // result: 2
$total = 0;
foreach( get_defined_vars() as $name => $obj ) {
if ( $obj instanceof A ) {
$total += $obj->i;
}
}
echo $total; // returns: 7
The function you need here is "get_defined_vars". But it only gets the variables within the current scope!
Just make the class member of the sum static as well. If you do this, then you will need to make sure it's maintained properly within each class (i.e. setValue needs to update that sum appropriarely).
This is probably not a great way to do things, though. I think it would get pretty confusing. In what context do you need the sum that you don't have access to all the instances?
Are you looking for a protected function foo($s){...} which the class can use but cannot be accessed from the outside? (PHP5 only)
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)