I have a class similar to this:
class My_Class {
private static $array = null;
private static $another_array = null;
private function __construct() {
self:$another_array = array( 'data' );
}
// This gets executed from jQuery Ajax when user clicks a button
public static function process_ajax() {
self::generate_html();
}
private static function generate_html() {
if ( ! self::$array ) {
self::$array = array( 'some data' );
}
}
// This gets executed when user is trying to save Ajax generated form
public static function save_ajax_form() {
print_r( self::$another_array ); // prints [0] => 'data'
self::validate_data();
}
private static function validate_data() {
// WHY DOES THIS EVALUATE TRUE?
if ( ! is_array( self::$array ) ) {
}
}
}
How can I access My_Class::$array property from an Ajax call?
Even though you are declaring the variable static it is going to be initialized to null on every request - PHP is "stateless" in this way, static variables will not persist accross requests. Since you do want to persist the value you will need to use something like $_SESSION, APC or memcached to hold the value of $array.
When your ajax calls save_ajax_form() it immediately then calls validate_data(). The $array variable is still initialized to null since the call to generate_html() happened in a different request, so the check to see if it is not an array will return true.
See: Does static variables in php persist across the requests?
Obviously you could either change the scope declaration from private to public, or if you want to keep private, add a public accessor:
public function getArray()
{
self::process_ajax();
return self::$array;
}
Related
I'm still new to OOP and this is probably a simple question, not sure if I'm overthinking this.
Let's say we have a simple class like the following that we can use to instantiate an object that can generate an array:
class gen_arr {
public $arr = array();
public function fill_arr() {
$this->arr["key"] = "value";
}
}
// instantiate object from gen_arr
$obj = new gen_arr();
Now if you wanted to get the value of the object's array's item, would you generate an array first and then echo the value like:
$arr = $obj->fill_arr();
echo $arr["key"];
Or would you access the object's property directly?
echo $obj->arr["key"]
In the actual code the property is private and there is a method that allows the viewing of the property array, the above is just to simplify the question.
Are there performance considerations and/or just best practices when it comes to this kind of case?
UPDATE:
It's still unclear from the answers if the best way is to generate an array from the property and access that array or just access the property directly (through the getter method)
Since you are filling the array with items only on fill_arr, those items wont be availabl until you call $arr = $obj->fill_arr();.
If you want to directly call the array, then you have to fill this array on the constructor function of this call like this:
class gen_arr {
public $arr = array();
function __construct() {
$this->arr["key"] = "value";
}
}
First off, the class you shared with us has a range of problems:
its sole instance property is public and can be modified by anyone
you have some temporal coupling, the method fill_arr() needs to be invoked before accessing the the value makes any sense
Encapsulation
Reduce the visibility of the instance property from public to private, so that the property can only be modified by the object itself, and provide an accessor instead:
class gen_arr
{
private $arr;
public function fill_arr()
{
$this->arr["key"] = "value";
}
public function arr()
{
return $this->arr;
}
}
Temporal Coupling
Remove the method fill_arr() and instead initialize the property $arr in one of the following options:
initialize field lazily when accessed the first time
initialize field in the constructor
initialize field with a default value
initialize field with a value injected via constructor
Initialize field lazily when accessed the first time
Initialize the field when it's accessed the first time:
class gen_arr
{
private $arr;
public function arr()
{
if (null === $this->arr) {
$this->arr = [
'key' => 'value',
];
}
return $this->arr;
}
}
Initialize field in the constructor
Assign a value during construction:
class gen_arr
{
private $arr;
public function __construct()
{
$this->arr = [
'key' => 'value',
];
}
public function arr()
{
return $this->arr;
}
}
Initialize field with a default value
Assign a value to the field directly, which works fine if you don't need to do any computation:
class gen_arr
{
private $arr = [
'key' => 'value',
];
public function arr()
{
return $this->arr;
}
}
Initialize field with a value injected via constructor
If the values are not hard-coded or otherwise calculated (as in the previous examples), and you need to be able to instantiate objects with different values, inject values via constructor:
class gen_arr
{
private $arr;
public function __construct(array $arr)
{
$this->arr = $arr;
}
public function arr()
{
return $this->arr;
}
}
Accessing and dereferencing values
This seems like this is your actual question, so the answer is - of course - It depends!.
Let's assume we have provided an accessor instead of accessing the otherwise public field directly:
Since PHP 5.4, the following is possible:
$object = new gen_arr();
echo $object->arr()['key'];
If you are still using an older version of PHP, you obviously can't do that and have to do something like this instead:
$object = new gen_arr();
$arr = $object->arr();
echo $arr['key'];
Largely, though, the answer to this question depends on the circumstances, and what you want to achieve. After all, readability is key for maintenance, so it might just make sense for you to introduce an explaining variable.
Note About your example, you could just use an ArrayObject instead:
$arr = new \ArrayObject([
'key' => 'value',
]);
echo $arr['key']);
For reference, see:
http://wiki.c2.com/?EncapsulationDefinition
http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/
http://php.net/manual/en/language.oop5.properties.php
http://wiki.c2.com/?ItDepends
http://php.net/manual/en/migration54.new-features.php
https://refactoring.com/catalog/extractVariable.html
http://wiki.c2.com/?IntroduceExplainingVariable
http://php.net/manual/en/class.arrayobject.php
For an example, see:
https://3v4l.org/qVVBM
First fill up the array
$gen_arr = new gen_arr();
$gen_arr->fill_arr();
then get the values with a getter method
$val = $gen_arr->getValue($key);
A getter method would be like this
public function getValue($key) {
return $this->arr[$key];
}
And certailny make the $arr property private
I have index.php with included file strings.php for translating phrases. In strings.php is included file based on user language: $lang.php. This $lang.php file is automatically generated from $lang.ini and contains single array:
<?php $translation = array (
'USER_PROFILE' => 'Uživatelský profil',
'MESSAGES' => 'Zprávy',
'NOTIFICATIONS' => 'Oznámení',
'SETTINGS' => 'Nastavení',
...
There is a class Strings with static function and globally assigned array $translation from $lang.php in strings.php:
include_once('cache/'.$lang.'.php');
class Strings {
static public function translate($string) {
global $translation;
...
}
}
But $translation in translate() function returns null in this case. If I include $lang.php in index.php it suddenly works, but if I call Strings::translate() function in other file, $translation again returns null. I don't understand this behavior. (Sorry for english)
Because the way the file $lang.php is included, the variable $translation lands as a local variable in a function and not as a global variable as method Strings::translate() expects it.
After we discussed in chat and I understood the problem, I can suggest two solutions:
Solution 1 (the quick workaround)
Change file $lang.php:
<?php global $translation = array (
'USER_PROFILE' => 'Uživatelský profil',
...
This way the variable $translation is created in the global context and everything works as expected.
Solution 2 (a better design)
Limit the scope of the variable $translation.
File $lang.php:
<?php return array (
'USER_PROFILE' => 'Uživatelský profil',
...
This way no variable is create (neither local nor global) and this removes the source of confusion.
File strings.php:
class Strings {
static $translation;
static private function initialize() {
global $lang;
static::$translation = include_once('cache/'.$lang.'.php');
}
static public function translate($string) {
if (! isset(static::$translation)) {
static::initialize();
}
// use static::$translation instead of global $translation
}
}
Solution 3 (the correct design)
An even better design (the correct one, in fact) is to make method initialize() public, change $lang as its parameter and call it from your code as soon as you determined the value of $lang.
File strings.php:
class Strings {
static $translation;
// Call this method once, as soon as you determined what
// language you need to use
static public function initialize($lang) {
static::$translation = include_once('cache/'.$lang.'.php');
}
static public function translate($string) {
// use static::$translation instead of global $translation
}
}
<?php
class User {
private $id;
private $username;
public function __construct($id = null) {
$this->id = $id;
if (!is_null($this->id)) {
$this->load();
}
}
public function load() {
}
}
?>
In the 'load' method I am going to load all the information from the current user (id) But I wonder how is the best way to load all info. I could grab all data and then just assign all private variables, but I assume there must be another "cheaper" way to get all the data so I can use as such
$this->variable;
And not have to ASSIGN every single data row, I select in the load method. How?
I am assuming the following:
Each User object represents 1 row in your database
You can retrieve information for your user in an associative array:
$user = array('name' => 'John', 'age' => 20);
Then perhaps using variables variables could be a viable solution in your load() method:
foreach($user as $key => $userData){
$this->$$key = $userData; //Note the use of variables variable
}
You can assign multiple variables like this in php:
private function getUserData() {
return array("42", "JohnDoe");
}
public function load() {
list($this->id, $this->username) = getUserData();
}
I have a class which contains an array of objects and has methods to return an object from that array by reference. There is also a method to unset an object from the array.
However if I have a variable that references an object from the array, and that element is unset, the variable still has a reference to it. What do I need to do in the remove method that will destroy that object for good, including references to it.
class myClass
{
public $objectList = array();
public function __construct()
{
$objectList[] = new myObject();
}
public function &getObjectByReference()
{
return $this->objectList[0];
}
public function removeObject()
{
unset($this->objectList[0]);
}
}
$myClass = new myClass();
$referencedObject = $myClass->getObjectByReference();
// this can now use the methods from myObject
$myClass-> removeObject();
// supposed to delete the object from memory. However $referencedObject is still
// able to use all the methods from myObject.
So thats the problem I am having, I need to be able to remove from the array and delete the object from memory so variables that reference that object are no longer usable.
Have you tried doing:
$referencedObject = &$myClass->getObjectByReference();
Is $referencedObject still there after putting in that &?
To create a reference from a return value both the function must return by reference (you already do so) and the assignment has to by by reference. So you should write:
$referencedObject =& $myClass->getObjectByReference();
If you do this the reference really will be destroyed.
But if you want to do destroy all variables having this object as value (and which are not references) then this is impossible. You can only remove the real references, not variables having the same value ;)
Php is working with garbage collector : if there is still a reference to the object then the object is not deleted.
unset($this->objectList[0])
Does not delete the object but the value in $this->objectList, the object still exists since he is referenced by $referencedObject.
One solution : when you delete the object, tell him he is being deleted and in that object you have a boolean "isDeleted". Then for every method of that object, check if isDeleted is true and in that case, just do nothing.
This is the nature of PHP's garbage collector. To make sure a caller doesn't maintain a reference to your object you have to ensure they can never touch the original object. Here is an idea:
class myClass
{
public $objectList = array();
public function __construct()
{
$objectList[] = new Wrapper(new myObject());
}
public function getObject()
{
return $this->objectList[0];
}
public function removeObject()
{
$this->objectList[0]->emptyWrapper();
unset($this->objectList[0]);
}
}
class Wrapper {
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($method, $args) {
return call_user_func_array(array($this->object, $method), $args);
}
public function __get($attr) {
return $this->object->$attr;
}
public function __set($attr, $value) {
$this->object->$attr = $value;
}
public function emptyWrapper() {
$this->object = null;
}
}
You can use this wrapper idea or you can use forced indirection and handles. I might prefer using forced indirection instead; otherwise, a caller can still keep the wrapper object alive - though it is fairly cheap.
class myClass
{
public $objectList = array();
public function __construct()
{
$objectList[] = new myObject();
}
public function getObjectHandle() {
return 0;
}
public function removeObject($h)
{
unset($this->objectList[$h]);
}
public function call($h, $method, $args) {
call_user_func(array($this->objectList[$h], $method), $args);
}
public function get($h, $attr) {
return $this->objectList[$h]->$attr;
}
public function set($h, $attr, $value) {
$this->objectList[$h]->$attr = $value;
}
}
$myClass = new myClass();
$objectHandle = $myClass->getObjectHandle();
// example method call
$myClass->call($objectHandle, 'some_method', array('arg1', 'arg2'));
$myClass->removeObject($objectHandle);
I have a class with a static method. There is an array to check that a string argument passed is a member of a set. But, with the static method, I can't reference the class property in an uninstantiated class, nor can I have an array as a class constant.
I suppose I could hard code the array in the static method, but then if I need to change it, I'd have to remember to change it in two places. I'd like to avoid this.
You can create a private static function that will create the array on demand and return it:
class YourClass {
private static $values = NULL;
private static function values() {
if (self::$values === NULL) {
self::$values = array(
'value1',
'value2',
'value3',
);
}
return self::$values;
}
}
I put arrays in another file and then include the file wherever I need it.
I am having a really really hard time understanding your question. Here is essentially what I understood:
I need to maintain a proper set, where
no two elements are the same.
PHP does not have a set type, not even in SPL! We can emulate the functionality of a set but any solution I can think of is not pleasant. Here is what I think is the cleanest:
<?php
class Set {
private $elements = array();
public function hasElement($ele) {
return array_key_exists($ele, $elements);
}
public function addElement($ele) {
$this->elements[$ele] = $ele;
}
public function removeElement($ele) {
unset($this->elements[$ele]);
}
public function getElements() {
return array_values($this->elements);
}
public function countElements() {
return count($this->elements);
}
}
Example usage:
<?php
$animals = new Set;
print_r($animals->getElments());
$animals->addElement('bear');
$animals->addElement('tiger');
print_r($animals->getElements());
$animals->addElement('chair');
$animals->removeElement('chair');
var_dump($animals->hasElement('chair'));
var_dump($animals->countElements());