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 ...
Related
I'm currently trying to get back into object oriented programming How do i get my array inside my class? Global doesn't seam to cut it.
<?
$systems = file_get_contents('https://api.eveonline.com/map/Sovereignty.xml.aspx');
$systems = explode("<row ",$systems);
//print_r($systems);
for ($i = 1; $i <= count($systems); $i++) {
//system name
$systemnames=explode("solarSystemName=",$systems[$i]);
$systemnames=explode('"',$systemnames[1]);
$systemnames=$systemnames[1];
//system id
$systemid=explode("solarSystemID=",$systems[$i]);
$systemid=explode('"',$systemid[1]);
$systemid=$systemid[1];
$systembyid[$systemid]=$systemnames;
$systembyname[$systemnames]=$systemid;
}
class Systems{
public function __construct()
{
global $systembyid;
global $systembyname;
}
function getSystems($system)
{
if (is_numeric($system) && $systembyid[$system]) {
return $systembyid[$system];
}
elseif($systembyname[$system]){
return $systembyname[$system];
}
else{
return "Error: Invalid system id or name";
}
}
}
?>
Try passing the values into the constructor like this, also if you use the & you are just passing a reference and not making a copy of the whole array.
class Systems{
private $sysyembyid;
private $systembyname;
public function __construct(&$systembyid, &$systembyname)
{
$this->systembyid = $systembyid;
$this->systembyname = $systembyname;
}
function getSystems($system){
if(is_numeric($system) && $this->systembyid[$system]){
return $this->systembyid[$system];
}
elseif($this->systembyname[$system]){
return $this->systembyname[$system];
}
else{
return "Error: Invalid system id or name";
}
}
}
I prefer to use Dependency Injection. Dependency Injection is when you inject your object's dependencies via the constructor. This ensures that the object will have its dependencies at creation.
class Systems {
protected $systembyid;
protected $systembyname;
public function __construct($systembyid, $systembyname)
{
$this->systembyid = $systembyid;
$this->systembyname = $systembyname;
}
public function getSystems($system) {
//Access them with $this-> like below
$this->systembyid[$system];
$this->systembyname[$system];
}
}
Note If you want to be able to modify $systembyid and $systembyname outside of the class, and see the changes within the class, you can pass references to __construct() instead, by specifying the parameters as references:
public function __construct(&$systembyid, &$systembyname)
{
$this->systembyid = $systembyid;
$this->systembyname = $systembyname;
}
Alternatively, you can pass them as parameters to your getSystems() method.
class Systems() {
public function getSystems($system, $systembyid, $systembyname) {
//Do stuff
}
}
The main drawbacks with this approach is that you always have to pass them as parameters to the method, and the method signature could get quite long.
You either need to use the global key word with var in the function where you use it, in this case getSystems() (bad) or pass them into the constructor or the function where you use them, or set them:
Probably the most common case:
public function __construct($s1, $s2)
{
$this->systembyid = $s1
$this->systembyname = $s2
}
//then use $this->systembyid etc in other functions
Or better yet, why not put all that processing code in a function off the class like processSystems() and set the vars there:
public function processSystems($file) {
$systems = file_get_contents($file);
$systems = explode("<row ",$systems);
//print_r($systems);
for ($i = 1; $i <= count($systems); $i++) {
//system name
$systemnames=explode("solarSystemName=",$systems[$i]);
$systemnames=explode('"',$systemnames[1]);
$systemnames=$systemnames[1];
//system id
$systemid=explode("solarSystemID=",$systems[$i]);
$systemid=explode('"',$systemid[1]);
$systemid=$systemid[1];
$systembyid[$systemid]=$systemnames;
$systembyname[$systemnames]=$systemid;
}
$this->systembyid = $systemnames;
$this->systembyname = $systemid;
}
Aside from that, I would say look into simple_xml or DOM for the XML parsing.
Also, you are storing the exact same data in each array. Just use one and either lookup the key or the value.
I try to create some sort of setup class, like global values for the page.
The PHP-code
class globals
{
public $page;
public function __construct()
{
}
public function set_page($value)
{
$this->page = $value; // Maybe from a database
}
}
class get
{
public function page()
{
$globals = new globals();
return $globals->page;
}
}
$globals = new globals();
$globals->set_page('My value');
echo get::page(); // Short function to be in a template
Question
My class forget the value I set. Why is that?
Do I have to use global variables?
Is this the correct approach for the problem?
The variable is set on an object, not on a class.
For each class, you can instantiate multiple objects. Each of those have their own variable scope.
Edit:
I forgot to include the easiest, and least verbose solution to your problem. AFAIK, you're looking for a way to check what page you're on. Constants will do just that:
defined('MY_CURRENT_PAGE') || define('MY_CURRENT_PAGE','My Value');
//use anywhere like so:
echo 'Currently on page: '.MY_CURRENT_PAGE;
My class forget the value I set. Why is that?
Quite simple: your page member function isn't static, yet you call it as though it is: get::page(). Even if you were to fix this, you're creating a new instance in the page method, but you're not preserving a reference too it anywhere, so each page call will create a new globals instance, that has nothing set.
Do I have to use global variables?
No, unless you're Really desperate, never use globals
Is this the correct approach for the problem?
No, if it doesn't work, it's not correct (IMHO).
Well, what is, you might ask. There are several ways to go about this:
class globals
{
public static $page = null;//make this static, meaning all instances will share this var
public function set_page($value)
{
self::$page = $value; // Maybe from a database
}
}
class get
{
private $_globalsInstance = null;
public function __construct(globals $instance = null)
{
$this->_globalsInstance = $instance;
}
private function _getGlobals()
{
if (!$this->_globalsInstance instanceof globals)
{
$this->_globalsInstance = new globals();
}
return $this->_globalsInstance;
}
public function page()
{
return $this->_getGlobals()::$page;
}
}
Personally, however, I wouldn't work like this, I'd just pass my instances to wherever I need them (as arguments to functions/methods or just instantiate them in a scope that will be accessible:
class globals
{
public $page = null;//make this static, meaning all instances will share this var
public function set_page($value)
{
$this->page = $value; // Maybe from a database
}
}
$page = new globals();
$page->set_page('foobar');
someFunction($page);
$someObject->renderPage($page);
require_once('specificScript.php');
//inside required script:
echo $page->page;
Do I have to use global variables?
Not, if your can use PHP 5.3
Is this the correct approach for the problem?
Better to use a generic class for this, or use static properties of objects
<?php
class globals
{
public static $page;
public function __construct()
{
}
public function set_page($value)
{
self::$page = $value; // Maybe from a database
}
}
class get
{
public static function page()
{
return globals::$page;
}
}
$globals = new globals();
$globals->set_page('My value');
echo get::page(); // Short function to be in a template
P.S.
But this is not a nice approach
$globals there
class get
{
public function page()
{
$globals = new globals();
return $globals->page;
}
}
and there
$globals = new globals();
$globals->set_page('My value');
are different inctances of globals class.
One of the solutions is to make $page var static
public static $page;
I hope this helps
UPD:
Also you might apply Singleton to globals class and request for its insnance instead of creating new one directly:
globals::getInstance()->setPage('Page');
and
return globals::getInstance()->getPage();
In this case $page doesn't have to be static.
I'm not sure the other answers are very clear. You have created 2 classes. As such they have different scopes. As writen you can't access the original variable $page from the get class because it's outside the scope. Your page function in fact creates a new version of the object $globals without $page set. Normally you would place both your set and get functions in the initial object/class. Though it would be possible to use two class by calling the first class from the second and setting the page. Why you would want to do that I'm not sure.
if I were writing the class it would look like this.
class globals
{
public $page;
public function __construct()
{
}
public function set_page($value)
{
$this->page = $value; // Maybe from a database
}
public function get_page()
{
return $this->page;
}
}
Actually I would probably set page to private not public. As public I guess you don't need a get function.
for using methods of the class without object you must use static definition. but anyway you put value for one class object and try to get it from another...
Perhaps this will help you continue on your coarse:
class globals
{
public static $page;
public function set_page($value)
{
self::$page = $value; // Maybe from a database
}
}
class get extends globals
{
public function page()
{
$globals = new globals();
return parent::$page;
}
}
$globals = new globals();
$globals->set_page('My value');
echo get::page();
?>
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.
Does anyone know how to reset the instance variables via a class method. Something like this:
class someClass
{
var $var1 = '';
var $var2 = TRUE;
function someMethod()
{
[...]
// this method will alter the class variables
}
function reset()
{
// is it possible to reset all class variables from here?
}
}
$test = new someClass();
$test->someMethod();
echo $test->var1;
$test->reset();
$test->someMethod();
I know I could simply do $test2 = new SomeClass() BUT I am particularly looking for a way to reset the instance (and its variables) via a method.
Is that possible at all???
You can use reflection to achieve this, for instance using get_class_vars:
foreach (get_class_vars(get_class($this)) as $name => $default)
$this -> $name = $default;
This is not entirely robust, it breaks on non-public variables (which get_class_vars does not read) and it will not touch base class variables.
Yes, you could write reset() like:
function reset()
{
$this->var1 = array();
$this->var2 = TRUE;
}
You want to be careful because calling new someClass() will get you an entirely new instance of the class completely unrelated to the original.
this could be easy done;
public function reset()
{
unset($this);
}
Sure, the method itself could assign explicit values to the properties.
public function reset()
{
$this->someString = "original";
$this->someInteger = 0;
}
$this->SetInitialState() from Constructor
Just as another idea, you could have a method that sets the default values itself, and is called from within the constructor. You could then call it at any point later as well.
<?php
class MyClass {
private $var;
function __construct() { $this->setInitialState(); }
function setInitialState() { $this->var = "Hello World"; }
function changeVar($val) { $this->var = $val; }
function showVar() { print $this->var; }
}
$myObj = new MyClass();
$myObj->showVar(); // Show default value
$myObj->changeVar("New Value"); // Changes value
$myObj->showVar(); // Shows new value
$myObj->setInitialState(); // Restores default value
$myObj->showVar(); // Shows restored value
?>
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)