I have an Object that needs to access a previously declared array in my statistics. I can of course create the entire array inside of the object, but since multiple objects use the exact same array there is no reason to clog up memory or time by making a call to the Database to create the same array every time I create a new object.
So, I understood that Objects cannot access global variables, but is there any work-around to access an external Array from within the object?
example Code:
global $stats = array();
$stats[1]['value']= 10;
$stats[1]['value1'] =2;
$stats[2]['value']= 12;
$stats[2]['value1'] =1;
class Obj() {
private $valueA;
private $valueB;
function __construct($user) {
//access Database lets call $SQL;
$valueA = SQL->value;
}
function showA() {
return ( $valueA * $stats[1]['value1']) + $stats[1]['value'];
}
}
Yes how about changing your class to look like this:
class Obj() {
private $valueA;
private $valueB;
private $stats;
function __construct($user, $stats) {
$this->stats = $stats;
//access Database lets call $SQL;
$valueA = SQL->value * $this->stats[1]['value1'] + $this->stats[1]['value'];
$valueB = SQL->value * $this->stats[2]['value1'] + $this->stats[2]['value'];
}
function showA() {
return $valueA;
}
}
You than just pass $stats to the object at instantiation. Or if you don't want it in the constructor, just make a setStats($stats) method that does the same.
I'll tell you three ways to do this:
pass the array into the constructor of the class. e.g.: $myObject =
new Obj( $stats );
make a class that serves up the $stats array: $stats = new Stats(); $statsArray = $stats->getStats();
use the term global inside of a public method in your class itself (not construct) to get that variable: 3:
function() somePublicMethod() {
global $stats;
$valueA = SQL->value * $stats[1]['value1'] + $stats[1]['value'];
$valueB = SQL->value * $stats[2]['value1'] + $stats[2]['value'];
}
You can access variables from within a class, i.e.
$stats[1]['value']= 10;
$stats[1]['value1'] =2;
$stats[2]['value']= 12;
$stats[2]['value1'] =1;
class Obj {
var $myStats;
function __construct() {
global $stats;
$this->myStats= $stats;
print_r($this->myStats);
}
}
$obj=new Obj; // Array ( [1] => Array ( [value] => 10 [value1] => 2 ) [2] => Array ( [value] => 12 [value1] => 1 ) )
DEMO.
Thanks to Mike, Sheikh and Kristian,
I can't in all Faith give a tick to your answers, because your words did not help me to understand the answer, Putting 'global $stats;' into the class results in an Error which I pointed out in my responses. but I will 'up' your scores when I permission from the site to do so.
For anyone looking for the answer to this, a Reminder, the key point is not to store the entire Array in the class, creating a huge waste of memory. The Key point is to gain access to the Variable which exists outside of the class.
While adding access to the global $stats by including it in the functions of the class, does produce the required results, It still requires that the Data is being stored in the class, which is again, against the point. Sorry I wasn't clear on this from the very beginning.
Instead:
example Code:
function showA(&$stats) {
return ( $valueA * $stats[1]['value1']) + $stats[1]['value'];
}
}
This, if I understand correctly, will use the pointer to the $stats variable, only within the scope of returning the $valueA after it has been modified using the stats array. not copying the entire array into another memory location, nor the class.
Related
I am trying to convert my string on id's into variable names and inject them into my function.
I have a json object with the following information in it:
Object
(
[files] => file1.php,fie2.php
[dependencies] => db,templates
[classname] => SomeClass
)
I am able to loop through everything and get it to work with a single variable name, but when there is more than one dependency, I need to loop through them, make them variables, and then pass to a function depending on how many are needed dynamically from this object above.
// First I make sure the dependency object exists, this one has 'db,templates' in it
if (!empty($json_data->dependencies)) {
// I explode them into an array, to see if there is more than one
$dependence_string = explode(",", $json_data->dependencies);
if (is_array($dependence_string)) {
// I make a dummy variable
$dependencies = NULL;
foreach ($dependence_string as $dependency) {
$dependencies[] = '$' . $dependency;
}
// Now i have an array with two values "$db", "$templates"
// This gets inserted as new SomeClass(Array()); but I need to
// somehow be able to convert it to new SomeClass($db, $templates);
$some_value = new $json_data->classname($dependencies);
} else {
// This is easy to handle and is done already
}
Now i have an array with two values "$db", "$templates" and this gets inserted as new SomeClass(Array()); but I need to somehow be able to convert it to new SomeClass($db, $templates); and keep them comma separated as variables from their string names.
What method would I use for this? I tried implode but it still sends as a string and I need to convert it to individual items and send however many the current script needs to run.
Got it to work with the following code:
foreach ($dependence_string as $dependency) {
$dependencies[] = '$' . $dependency;
}
$some_value = (new ReflectionClass($json_data->classname))->newInstanceArgs($dependencies);
The splat operator could be the solution to your issue.
<?php
declare(strict_types=1);
namespace Marcel;
class SomeClass
{
protected string $db;
protected string $templates;
public function __construct(string $db, string $templates)
{
$this->db = $db;
$this->templates = $templates;
}
}
foreach ($dependence_string as $dependency) {
$dependencies[] = '$' . $dependency;
}
$class = new SomeClass(...$dependencies);
Type hinting is not required but would be nice. To keep the example simple, strings are used here.
I´m trying to learn construct and destruct.
So, I made this
<?php
class Numbers {
public function __construct($numberint,$numbername,$numberletter,$numberpos) {
$this->numberint = $numberint;
$this->numbername = $numbername;
$this->numberletter = $numberletter;
$this->numberpos = $numberpos;
}
public function __destruct() {
unset($this->numberpos);
}
}
$number1 = new Numbers(1,"One","A",0);
print_r($number1);
?>
As you can see, I create the class Number and then, use construct for the Objects. But after construct, I want to use destruct, in this case, unset numberpos. I´m trying to put them together to understand how the work.
Anyone can help me?
My idea is to change the result:
Numbers Object ( [numberint] => 1 [numbername] => One [numberletter] => A [numberpos] => 0 )
To...
Numbers Object ( [numberint] => 1 [numbername] => One [numberletter] => A )
Thanks and remember that I´m learning :D
The destructor is there to destroy the whole object, not parts of an object. If you want your desired output, you could just do:
$number1 = new Numbers(1,"One","A",0);
print_r($number1);
unset($number1->numberpos);
print_r($number1);
Demo.
If you want to see your destructor getting called, unset the object:
class Numbers {
public function __destruct() {
echo "Destructing!\n";
}
}
$number1 = new Numbers();
unset($number1);
echo "Done!";
Outputs:
Destructing!
Done!
So I've got a plugin which produces information for me based on a user's id.
This happens in the plugin's module 'pitch':
public function executeIndex(sfWebRequest $request)
{
$unique_id = $request->getParameter('unique_id');
$this->user = UserTable::getInstance()->getByToken($unique_id);
$this->forward404Unless($this->user);
$this->iplocation=new IPLocation();
$qualified_offers = new QualifiedOffers();
$this->creatives = $qualified_offers->applicableTo($this->user);
$this->match_type = UserDemoTable::getInstance()->getValue($this->user->id, 'match');
// Put the applicable creatives into the session for later use
$userCreatives = $this->creatives;
$this->getUser()->setAttribute('userCreatives', $userCreatives);
}
And then I try to call that attribute on the subsequent template (In a different module called 'home' with a different action):
public function executePage(sfWebRequest $request)
{
$template = $this->findTemplate($request->getParameter('view'), $this->getUser()->getCulture());
$this->forward404Unless($template);
$this->setTemplate($template);
// Grab the creatives applicable to the user
$userCreatives = $this->getUser()->getAttribute( 'userCreatives' );
}
Unfortunately it doesn't work at all.
If I try this from the action where $creatives is initially generated:
$this->getUser()->setAttribute('userCreatives', $userCreatives);
$foo = $this->getUser()->getAttribute('userCreatives');
// Yee haw
print_r($foo);
I am met with great success. I'm essentially doing this, only from two different controllers. Shouldn't that be irrelevant, given that I've added 'userCreatives' to the user's session?
It sounds like you're trying to store objects as user attributes (i.e., in the session).
From Jobeet Day 13:
You can store objects in the user session, but it is strongly discouraged. This is because the session object is serialized between requests. When the session is deserialized, the class of the stored objects must already be loaded, and that's not always the case. In addition, there can be "stalled" objects if you store Propel or Doctrine objects.
Try storing either array or stdClass representations of your objects and then loading them back into "full" objects once you retrieve them.
Here's an example that I used on another project:
class myUser extends sfGuardSecurityUser
{
...
public function setAttribute( $name, $var )
{
if( $var instanceof Doctrine_Record )
{
$var = array(
'__class' => get_class($var),
'__fields' => $var->toArray(true)
);
}
return parent::setAttribute($name, $var);
}
public function getAttribute( $name, $default )
{
$val = parent::getAttribute($name, $default);
if( is_array($val) and isset($val['__class'], $val['__fields']) )
{
$class = $val['__class'];
$fields = $val['__fields'];
$val = new $class();
$val->fromArray($fields, true)
}
return $val;
}
...
}
I'm creating a forum, and I want to keep track of which threads have been updated since the user last visited. So I have an array that I keep in $_SESSION that is basically structured as [$boardid][$threadid] = 1. If the threadid and boardid are set, then the thread has not been read and the board contains unread threads. When a user views a thread, I just unset() the appropriate board and thread id. However, I've having problems with getting unset to work with arrays like this.
Firstly, I have a session class to make handling session data a little nicer
class Session {
private $_namespace;
public function __construct($namespace = '_default') {
$this->_namespace = $namespace;
}
/**
* Erase all variables in the namespace
*/
public function clear() {
unset($_SESSION[$this->_namespace]);
}
public function __set($name, $value) {
$_SESSION[$this->_namespace][$name] = $value;
}
public function __get($name) {
if(isset($_SESSION[$this->_namespace]) && array_key_exists($name, $_SESSION[$this->_namespace])) {
return $_SESSION[$this->_namespace][$name];
}
return null;
}
public function __isset($name) {
return isset($_SESSION[$this->_namespace][$name]);
}
public function __unset($name) {
unset($_SESSION[$this->_namespace][$name]);
}
};
Then I have a CurrentUser class representing the current user. The CurrentUser class has a member named _data which is-a Session object. In the CurrentUser class I override the __get and __set methods to use the _data member.
public function __set($name, $value) {
$this->_data->$name = $value;
}
public function __isset($name) {
return isset($this->_data->$name);
}
public function __get($name) {
if(isset($this->_data->$name)) {
return $this->_data->$name;
}
return null;
}
Now to keep track of which threads have been unread, I fetch all threads whose date is >= the user's last_seen date. I also have methods to remove board and threads from the array.
public function buildUnreadList($since) {
// Build a "new since last visit" list
$forumModel = new Model_Forum();
$newThreads = $forumModel->fetchThreadsSinceDate($since);
foreach($newThreads as $thread) {
$tmp =& $this->unreadThreadsList;
$tmp[$thread['board']][$thread['id']] = 1;
}
}
public function removeThreadFromUnreadList($boardid, $threadid) {
$threads =& $this->unreadThreadsList;
unset($threads[$boardid][$threadid]);
}
public function removeBoardFromUnreadList($boardid) {
$threads =& $this->_data->unreadThreadsList;
unset($threads[$boardid]);
}
This is where I'm running into problems. I'm getting a Indirect modification of overloaded property Session::$unreadThreadsList has no effect error on $threads =& $this->_data->unreadThreadsList; How can I either fix this problem or design a better solution? I thought about creating a class that keeps track of the array so I don't have to have an array of arrays of arrays of arrays, but I'm not certain on persisting objects and creating an object just to manage an array feels really dirty to me.
Sorry if I'm a little bit off base; I'm trying to understand how the variables are being used (as their initialization is not shown). So $this->unreadThreadsList is an array where the indices (if value set to 1). Why not set everything directly?
Looking at what you're doing, here is an idea I had. It does the same thing but just does some extra checking on $this->unreadThreadsList and it accesses the variable directly.
Assuming I figured out the array structure properly, this should work.
public function buildUnreadList($since) {
// Build a "new since last visit" list
$forumModel = new Model_Forum;
$newThreads = $forumModel->fetchThreadsSinceDate($since);
foreach($newThread as $thread)
{
// Avoid an error if no list pre-exists
if(is_array($this->unreadThreadsList))
if(array_key_exists($thread['board'],$this->unreadThreadsList))
if(array_key_exists($thread['id'],$this->unreadThreadsList[$thread['board']]))
// Skip this result, already in
if($this->unreadThreadsList[$thread['board']][$thread['id']] == 1) continue;
$this->unreadThreadsList[$thread['board']][$thread['id']] = 1;
}
}
This assumes an array structure like:
array(
1 => array(
'board' => 1,
'id' => 2
),
2 => array(
'board' => 3,
'id' => 1
),
3 => array(
'board' => 7,
'id' => 2
));
for the result of "fetchThreadsSinceData($since)" and an array structure of
array(
1 => array(
2 => 1
),
2=> array(
2 => 1
),
3=> array(
2 => 1
));
for the $this->unreadThreadsList where the first index is the board and the second index is the thread id.
For the other functions why not simply unset them directly as well?
unset($this->unreadThreadsList[$boardid][$threadid]);
unset($this->unreadThreadsList[$boardid]);
Good luck!
Dennis M.
still getting used to PHP classes so a bit of "help /guidance" please
I have a class like this:
class ansa_accounturl_query {
function __construct() {
global $DBH;
global $limit;
$STH = $DBH->query("SELECT frm_url.frm_urlID,frm_url.frm_url FROM frm_url WHERE frm_url.accountID='SOMETHING' ".$limit." ");
$STH->setFetchMode(PDO::FETCH_OBJ);
$this->noforms = $STH->rowCount();
while($row = $STH->fetch()):
$this->frm_urlID[] = $row->frm_urlID;
$this->frm_url[] = $row->frm_url;
endwhile;
}
}
The limit comes from a PHP function - and works.
What I would really like to do is create extend classes for the above example which gives say $this-frm_url as a function. so that on the "page" I call the class $classcall = new class(); but rather than have to go echo $classcall->frm_url[$key]; I can just call a function like this echo frm_url(); So in the example above there would be 2 extend classes one for frm_urlID and one for frm_url.
Also, in the "master class" am I right in in setting as array? i.e. $this->frm_url[] as without that I cannot seem to run a loop but the loop does seem "over" complex if you do it this way as you (well I) have to get a count of the array items then run the loop so you (again I) have a for() statement then a foreach(). Seems longwinded to me.
Thanks in adavance.
First:
Please do not use globals.
If you have to use "global" there is a 90% percent chance that your design is bad.
Either pass $DBH and $limit as parameters to __construct($dbh,$limit) or define them as static propertys of ansa_accounturl_query.
If you define them as static propertys the values will still be identical for all instance of your class.
Second:
If you want to call a method without creating a instance first you can declare the methods static, too. Then you can call them like this:
classname::myMethod(parameter);
if you allways use the same db and the same setting I would suggest you create a class with static propertys and 3 static methods.
What I would really like to do is
create extend classes for the above
example which gives say $this-frm_url
as a function. so that on the "page" I
call the class $classcall = new
class(); but rather than have to go
echo $classcall->frm_url[$key]; I can
just call a function like this echo
frm_url(); So in the example above
there would be 2 extend classes one
for frm_urlID and one for frm_url.
echo frm_url(); won't work. You have to use $classcall->frm_url[$key]; unless you define a function like
function frm_url($key){
if (!$key) $key = 0;
$classcall = new ansa_accounturl_query();
return $classcall->frm_url[$key];
}
<?php
class ansa_accounturl_query {
private static $FIND_ALL_QUERY = "SELECT frm_url.frm_urlID,frm_url.frm_url FROM frm_url WHERE frm_url.accountID='SOMETHING' %s";
private $dbh;
public function __construct( $dbh ) {
$this->dbh = $dbh;
}
public function findUrls() {
$query = $this->dbh->query(vsprintf($queryString, array( func_get_args() ) ) );
$query->setFetchMode(PDO::FETCH_OBJ);
$result = array();
$result["count"] = $query->rowCount();
$result["records"] = array();
while( null !== ( $row = $query->fetch() ) ) {
$result["records"][] = array(
"id" => $row->frm_urlID,
"url" => $row->rm_url
);
}
return $result;
}
}
$ansaQuery = new ansa_accounturl_query($DBH);
$result = $ansaQuery->findUrls();
foreach ( $result['records'] as $row ) {
print sprintf("ID: %d; URL: %s", $row['id'], $row['url']);
}
print sprintf("URLs count: " . $result['count'] );