Im making a class in php, but Im having some problems with one of the class variables. I declare a private variable, then in the constructor set it. However, later in the class I have a method that uses that variable. The variable in this case is an array. However, the method says the array is blank, but when I check it in the constructor, it all works out fine. So really the question is, why does my array clear, or seem to clear, after the constructor?
<?php
class Module extends RestModule {
private $game;
private $gamearray;
public function __construct() {
require_once (LIB_DIR."arrays/gamearray.php");
$this->gamearray = $gamesarray;
$this->game = new Game();
$this->logger = Logger::getLogger(__CLASS__);
$this->registerMethod('add', array(Rest::AUTH_PUBLIC, Rest::AUTH_USER, Rest::AUTH_ADMIN), true);
$this->registerMethod('formSelect', array(Rest::AUTH_PUBLIC, Rest::AUTH_USER, Rest::AUTH_ADMIN), false);
}
public function add(){
$game = Utility::post('game');
}
public function formSelect(){
$gamename = Utility::get('game');
$this->$gamearray[$gamename];
}
}
The array is pulled in from another file because the array contains a lot of text. Didn't want to mash up this file with a huge array declared in the constructor. The scrolling would be tremendous. Any explanation would be nice, I like to understand my problems, not just fix em'.
You have a typo:
public function formSelect(){
$gamename = Utility::get('game');
$this->gamearray[$gamename]; // Remove the $ before gamearray
}
Moreover, in your case, include is better than require_once.
If you want to go deeper, you could rewrite $gamearray assignment like this:
// Module.php
$this->gamearray = include LIB_DIR.'arrays/gamearray.php';
// gamearray.php
return array(
// Your data here
);
Related
A problem haunting me since early days of CodeIgniter and now, with the new CI 3 i want to see if there is a more elegant way to solve it.
// file: application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
public $GLO;
function __construct(){
parent::__construct();
$this->GLO['foo'] = 'bar';
$this->GLO['arr'] = array();
}
}
then, later in the code, I need to get and set the values of the $GLO variable dynamically. So for instance:
// file: application/controllers/dispatcher.php
class Dispatcher extends MY_Controller {
function __construct() {
parent::__construct();
$this->load->model('public/langs');
print_r($this->GLO);
}
}
will print array('foo'=>'bar, 'arr'=>Array()) which is correct. Also in my models I can get the values of the $GLO array in the same manner. However, as soon as I need to set any values in the $GLO array, I get the Indirect modification of overloaded property notice and so I am stuck. In my model (after executing a DB query):
// file: application/models/public/langs.php
class Langs extends CI_Model {
function __construct(){
parent::__construct();
}
function set_global_languages(){
print_r($this->GLO); // <<< prints the same values as in the controller above
$temp = array();
// [stripped db code]
$temp['label'] = $row->label;
$temp['id'] = $row->id;
$this->GLO['arr'][] = $temp; // <<< this is where the notice happens
}
Any clues of how I can use $this->GLO['foo'] = 'baz'; for setting properties of this global array in my models?
Cheers.
I stumbled upon this problem a ton of times. Since you are asking for an elegant solution - i try to give you some idea.
There is a library called Registry, which is written totally fine and can be extended any time. You can find the library here.
Put it in your libraries folder.
This library should get autoloaded so it is always available.
Add it to your config/autoload.php
$autoload['libraries'] = array(...,"Registry");
After that you should have a really easy way of storing things globally within CI - in your code for example it would look like
class MY_Controller extends CI_Controller {
function __construct()
{
parent::__construct();
$arrSomething = [
'foo' => "bar",
"arr" => array()
];
$this->registry->set("arrSomething",$arrSomething);
}
}
class Dispatcher extends MY_Controller
{
function __construct()
{
parent::__construct();
$this->load->model('public/langs');
}
}
class Langs extends CI_Model
{
function __construct(){
parent::__construct();
}
function set_global_languages()
{
$arrSomething = $this->registry->get("arrSomething");
$temp = array();
// [stripped db code]
$temp['label'] = $row->label;
$temp['id'] = $row->id;
$arrSomething['arr'][] = $temp;
//in case of an array i guess you've to reset it because there is no reference or try to call it by reference with "&"
$this->registry->set("arrSomething");
}
}
sintakonte, thanks a lot for the tip. Last night I had to rewrite a lot of code to see if and how it works. The Registry class works as expected! The only issue with this set/get method is the handling of deep arrays, especially those created dynamically and having their data pushed dynamically too. So today morning I did something very bad :) and just created this class:
class Globals {
public $data = array();
}
So now I am going from $this->GLO['foo'] to $this->globals->data['foo'] and get to keep existing syntax intact! It also enables me to reuse the class in different controllers and models under different names, once initializing it as "globals" and the other time as "admin". Seems to work alright, too!
I'm currently working on a project where I have to work with huge arrays. With huge, I mean 1k elements or more. Since these are a lot of arrays and i sometimes mess things up, I decided to create a class with static functions so i can call the functions which would make the entire project easier to read. This is what I currently have:
ArrayAccess.class.php:
require "dep/arrays/elements.php";
class ArrayAccess {
public static function get_value_from_element($element) {
return $elements[$element];
}
}
elements.php:
<?php
$elements = array(
"sam" => 6, ... and so on ...
I simply want to be able to use ArrayAccess::get_value_from_element($element) in my project. It is so much easier to read than all these indexes everywhere. However, the array is defined in the elements.php file - I can't use that in the class.
So how can I access the array in my class? Please note, I cannot copy it into the class, the file would be larger than 400k lines, this is not an option.
You can return a value from an include (or require in this case) and store that to a static property the first time the function is called.
elements.php:
<?php
return array("sam" => 6, ...);
DataAccess.php:
class DataAccess {
private static $elements = array();
public static function get_value_from_element($element) {
if(self::$elements === array()) {
self::$elements = require "elements.php";
}
return self::$elements[$element];
}
}
You should also avoid naming your class ArrayAccess, since it already exists in PHP.
In elements.php
<?php
return array( // use return so you can retrieve these into a variable
"sam" => 6, ... and so on ...
Then in the class
<?php
class ArrayAccess {
public static $elements = null; // set up a static var to avoid load this big array multiple times
public static function get_value_from_element($element) {
if(self::$elements === null) { // check if null to load it from the file
self::$elements = require('elements.php');
}
return self::$elements[$element]; // there you go
}
}
If you don't want do the if statement in the getter every time, you should probably find some where else to load the file into the static variable before using the getter.
An alternative is to declare $elements as global in your class:
require "dep/arrays/elements.php";
class ArrayAccess {
public static function get_value_from_element($element) {
global $elements;
return $elements[$element];
}
}
Second update
I think I've been approaching this problem from the wrong side of the coin. Would I be correct in assuming that I should be making 'First' an abstract class and just finding a way to reference 'Second' and 'Third' at a later time?
Update
Based on some of the feedback, I have added some content to try and clear up what I would like to do. Something similar to this effect.
I know from just looking at the code below that, it is a waste of performance "if" it did work and because it doesn't, know I am approaching the problem from the wrong angle.The end objective isn't all to uncommon at a guess from some of the frameworks I've used.
I'm more trying to base this particular bit of code on the CodeIgniter approach where you can define (what below) is STR_CLASS_NAME in a config file and then at any point through the operation of the program, use it as i have dictated.
STR_CLASS_NAME = 'Second';
class First {
protected $intTestOne = 100;
public function __construct() {
$strClassName = STR_CLASS_NAME;
return new $strClassName();
}
public function TestOne() {
echo $this->intTestOne;
}
protected function TestThreePart() {
return '*Drum ';
}
}
class Second extends First{
/* Override value to know it's working */
protected $intTestOne = 200;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'roll*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 200.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum roll*'
You may be asking, why do this and not just instantiate Second, well, there are cases when it is slightly different:
STR_CLASS_NAME = 'Third';
class Third extends First{
/* Override value to know it's working */
protected $intTestOne = 300;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'snare*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 300.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum snare*'
Situation
I have a an abstract class which extends a base class with the actually implementation; in this case a basic DB wrapper.
class DBConnector ()
class DBConnectorMySQLi extends DBConnector()
As you can see, MySQLi is the implementation. Now, dependant upon a value in the configuration process, a constant becomes the class name I wish to use which in this case (as shown below builds DBConnectorMySQLi.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
Objective
To have a base class that can be extended to include the implementation
For the code itself not to need know what the name of the implementation actually is
To (in this case) be able to type or use a project accepted common variable to create DBConnectorMySQLi. I.E. $db or something similar. W
Issue
When it comes to actually calling this class, I would like the code to be shown as below. I was wondering whether this is at all possible without the need to add any extra syntax. On a side note, this constant is 100% guaranteed to be defined.
$DBI = new DB_CLASS();
Solution 1
I know it is possible to use a reflection class ( as discussed in THIS QUESTION) and this works via:
$DBI = new ReflectionClass(DB_CLASS);
However, this creates code that is "dirtier" than intended
Solution 2
Start the specific implementation of DBConnectorMySQLi within the constructor function of DBConnector.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
class DBConnector() { public function __construct() { $this->objInterface = new DBConnectorMySQLi(); }
class DBConnectorMySQLi()
This however would result in the need to keep on "pushing" variables from one to the other
Any advice is much appreciate
You can use variables when you instantiate a class.
$classname = DB_CLASS;
$DBI = new $classname();
Source: instantiate a class from a variable in PHP?
The Situation
I have a table in a DB that contains job types, basically defined by a label and a price. I'm trying to do a simple SELECT * FROM jobtype but I can't seem to get it to work, although I've use the same block over and over again in the rest of the code. The main difference here, is that it is a singleton trying to execute the function.
The problem is that as soon as I uncomment the line $rs_job_types = mysql_query($query_job_types, $vtconnection) or die(mysql_error()); the page will stop loading at this particular point in the code.
The Code
Following is the code of my function getJobTypes():
require_once('Connections/vtconnection.php');
class JobTypes extends Singleton{
static private $job_types;
public static function getJobTypes(){
if (self::$job_types == null){
echo 'DEBUG: For now, $job_types is NULL.'."\n";
mysql_select_db($database_vtconnection, $vtconnection);
$query_job_types = 'SELECT * FROM jobtype';
$rs_job_types = mysql_query($query_job_types, $vtconnection) or die(mysql_error());
while ($rs_row = mysql_fetch_assoc($rs_job_types)){
// let the job type identifier in the db be its index in our array
self::$job_types[$rs_row['id']]['label'] = $rs_row['label']; // job type label
self::$job_types[$rs_row['id']]['price'] = $rs_row['price']; // job type price
}
if (self::$job_types != null) echo 'DEBUG: $job_types has been populated.'."\n";
}
return self::$job_types;
}
}
Which I am calling like so:
$jt = JobTypes::getJobTypes();
Here is my singleton pattern:
class Singleton{
private static $instances = array();
final private function __construct(){
}
final public function __clone(){
trigger_error('You don\'t clone a singleton!', E_USER_ERROR);
}
final public static function getInstance(){
$c = get_called_class();
if(!isset(self::$instances[$c])){
self::$instances[$c] = new $c;
}
return self::$instances[$c];
}
}
I have turned the problem in my head, commented everything inside the getJobtypes() function and uncommented step by step. I found out the problem does happen with the mysql_query() line, just can't seem to work out why. Is something obviously wrong in my code?
Solved
As suggested here, I used global $vtconnection,$database_vtconnection; at the start of my static function and all went smooth. It is not an optimal solution but it pointed out the scope issue which I will now try to resolve.
I also got rid of the singleton pattern.
Well the most obvious thing is $database_vtconnection $vtconnection are defined nowhere in your getJobTypes function. If they're part of the class, you need $this (object) or self (static) references. More likely it looks like you're trying to use global variables, in which case you have to pull them into the scope of the function.
mysql_select_db can auto-connect (if $vtconnection isn't supplied) but only if it knows how - there's a previous connection (or possible INI config with db/host/user/pass).
To pull them into scope you need to add the line at the beginning of the function:
global $vtconnection,$database_vtconnection;`
... Or use the $GLOBALS superglobal array:
mysql_select_db($GLOBALS["database_vtconnection"],$GLOBALS["vtconnection"]);
For the record using globals isn't a particularly good solution.
My aim is to retrieve some data from a global array which is defined in another PHP file. My code is running inside database.php file and the array I want to use is inside config.php file. I understand that accessing a global array inside class is not a good idea, but I want to do it so because of some reasons.
My code is as below:
config.php
$CONFIG = array();
// ...
$CONFIG["DATABASE"] = array();
$CONFIG["DATABASE"]["USERNAME"] = "user";
$CONFIG["DATABASE"]["PASSWORD"] = "pass";
$CONFIG["DATABASE"]["HOSTNAME"] = "127.0.0.1";
$CONFIG["DATABASE"]["DATABASE"] = "my_db";
// ...
database.php
require('config.php');
class Database
{
protected $m_Link;
private $m_User;
private $m_Pass;
private $m_Host;
private $m_Data;
private $m_bConnected;
public function __construct()
{
global $CONFIG;
$this->m_User = $CONFIG["DATABASE"]["USERNAME"];
$this->m_Pass = $CONFIG["DATABASE"]["PASSWORD"];
$this->m_Host = $CONFIG["DATABASE"]["HOSTNAME"];
$this->m_Data = $CONFIG["DATABASE"]["DATABASE"];
$this->m_bConnected = false;
$this->Connect();
}
// ...
};
There is no error given (except for the failed database connection notification).
I can't access to the array elements. For instance $CONFIG["DATABASE"]["USERNAME"] returns an empty value even though it was initialized with "user" string in the config.php.
How should I modify my code so that it can this global array can be accessible inside the class constructor?
(Note: PHP version is 5.3.0)
Your code looks right, so I think you should just debug it. Try to output $CONFIG before creating instance of Database class, $CONFIG may be redefined/changed somewhere in your code. And don't just check one value in array - output whole array with var_dump/print_r.
Instead of
$CONFIG = array();
use
$GLOBALS['CONFIG'] = array();
I think some how global is not working in __construct(). I am not sure if it is a bug or it is designed as it is.
For code
<?php
class Test {
public $value;
function __construct() {
global $value;
$value = "I am a test.";
}
}
$test = new Test();
echo $test->value;
You will see nothing when above php runs.
However, if you do not use global, but use $this->value, everything works fine.
<?php
class Test {
public $value;
function __construct() {
$this->value = "I am a test.";
}
}
$test = new Test();
echo $test->value;
If you insist to get a reason. I think maybe __construct() is design to initialize the properties. Some code like $this->value = $value is using a lot in __construct(). So maybe the php designer thinks it is not good practice to use global in __construct(). However. I can not find a word mentioned it in php manual after all.