PHP array references; holding references in an array for later use - php

I'm trying to hold onto a variable reference for later use.
Not certain this is even possible, but I'm hoping I can initialize an array element, and reference it with a variable. Then, set the value of said array element to something, therefore making the value accessible from the referenced variable.
For example, this works:
class Test{
private $_vars = array();
public function bind($key, &$var){
$this->_vars[$key] = &$var;
return $this;
}
public function fetch($key, &$var){
$var = $this->_vars[$key];
return $this;
}
}
$test = new Test();
$string_set = 'This is a string';
$test->bind('string', $string_set)
->fetch('string', $string_get);
var_dump($string_get);
// expected: string(16) "This is a string"
// actual: string(16) "This is a string"
Now here's the problem; the ordering of method calls. I can't have the call() function returning a reference to $this, as the call() function needs to pass up the return value of the stored anonymous function (otherwise I'd reorder the calls to be ->call()->fetch() instead of ->fetch()->call())
Anyways, the fetch() method should be setting the appropriate element by key in $_vars to NULL (to empty any existing value, or initialize it, whichever) and then referencing that element to the passed $var.
When the anonymous function is called (after the fetch() binding is done), it calls bind(), now binding the element in $_vars to whatever (a $string_set containing This is a string in this case) If my logic is correct, the fetch() bound variable ($string_get in this case) should now reference the array element in $_vars which is referencing $string_set which contains This is a string.
Doesn't seem that way though. Here's the code that's failing (stripped down for brevity, but all the important parts are there)
class Test{
private $_vars = array();
private $_function;
public static function factory(){
return $test = new self(function() use(&$test){
$string_set = 'This is a string';
$test->bind('string', $string_set);
return true;
});
}
private function __construct($function){
$this->_function = $function;
}
public function bind($key, &$var){
$this->_vars[$key] = &$var;
return $this;
}
public function fetch($key, &$var){
$this->_vars[$key] = null;
$var = &$this->_vars[$key]; // edited; was not assigning by reference
return $this;
}
public function call(){
return (bool) call_user_func($this->_function);
}
}
$return = Test::factory()
->fetch('string', $string_get)
->call();
var_dump($return, $string_get);
// expected: bool(TRUE), string(16) "This is a string"
// actual: bool(TRUE), NULL
Am I chasing daisies here, is this even possible? Either way, I appreciate and thank you in advance for even glancing at this problem, any insight is really appreciated.
Edit: the line in fetch() - $var = $this->_vars[$key]; wasn't assigning the array element by reference. I've edited it now to $var = &$this->_vars[$key];, though it seemingly has no effect.
Bonus: If this problem is solvable, that's obviously great; I'm actually hoping that bind() can take $var by value, rather than by reference. The method signature would be changed to something like set($key, $value). Anyways, thanks again in advance.
To elaborate for the seemingly curious (looking in your direction #Tomalak) I'll provide the more complete class, and usage scenario:
class Action{
private static $_cache = array();
private static $_basePath;
private $_vars = array();
private $_function;
public static function setBasePath($basePath){
$basePath = rtrim($basePath, '/') . '/';
if(!is_dir($basePath)){
// throw exception, $basePath not found
}
self::$_basePath = $basePath;
}
public static function load($actionPath){
$actionPath = self::$_basePath . $actionPath;
if(array_key_exists($actionPath, self::$_cache)){
return self::$_cache[$actionPath];
}
if(!is_file($actionPath)){
// throw exception, $actionPath not found
}
$action = call_user_func(function() use(&$action, $actionPath){
return require($actionPath);
});
if(!($action instanceof self)){
// throw exception, $action of invalid type
}
self::$_cache[$actionPath] = $action;
return $action;
}
public function __construct($function){
if(!is_callable($function)){
// throw exception, $function not callable
}
$this->_function = $function;
}
public function bindReturn($key, &$var){
$this->_vars[$key] = &$var;
return $this;
}
public function fetchInto($key, &$var){
$this->_vars[$key] = null;
$var = &$this->_vars[$key];
return $this;
}
public function run(){
return (bool) call_user_func_array($this->_function, func_get_args());
}
}
############################################################################
// actions/test.php
return new Action(function($name)
use(&$action){
if($name == 'Alice'){
return false;
}
$data = "Hi, my name is {$name}.";
$action->bindReturn('data', $data);
return true;
});
############################################################################
// index.php (or whatever)
$result = Action::load('actions/test.php') // loaded
->fetchInto('data', $data)
->run('Alice');
// Failed
echo $result
? 'Success - ' . $data
: 'Failed';
$result = Action::load('actions/test.php') // called from cache
->fetchInto('data', $data)
->run('Bob');
// Success - Hi, my name is Bob
echo $result
? 'Success - ' . $data
: 'Failed';

What you want do is simply not possible (at least with referencces), because you cannot "redirect" a reference. Here's what happens:
$instance->fetch('foo', $myVar);
public function fetch($key, &$var){
// Here $var is a reference to $myVar.
$var = &$this->_vars[$key]; // now $var is a reference to $this->_vars[$key]
// it is not connected to $myVar anymore.
}
Here's what you can do: You can pass fetch() a reference to an array and set an element in that array to be a reference to $this->_vars[$key] or you can pass fetch() an object and set a member variable to be the reference.
Oh, sry missed the obvious: You can of course just use your bindReturn() function in the use-case you presented. That would work without problems.

Looks like you have problem with
public function fetch($key, &$var){
$this->_vars[$key] = null;
$var = $this->_vars[$key];
return $this;
}
If you want to remove the key, don't set it to null, unset it:
Edit: changed the code to avoid uninitialized variable exception.
public function fetch($key, &$var){
if(isset($this->_vars[$key]))
{
$var = $this->_vars[$key];
unset($this->_vars[$key]);
}
else
{
$var = null;
}
return $this;
}

Related

How to load data with new self construction php

I can not load data to properties using this construction I receive null in dump
<?php
namespace App\Domain\Good;
class GoodDto
{
public $name;
public $articul;
public $price;
public $type;
public $qnt;
public $discount;
public $category;
public $description;
public $description2;
public $color;
public function load($data)
{
$this->name = $data['name'];
$this->articul = $data['artikul'];
$this->price = $data['price'];
$this->type = (isset($data['type'])) ? $data['type'] : null;
$this->qnt = $data['count'];
$this->discount = $data['spinner-decimal'];
$this->category = $data['id_cat'];
$this->description = $data['editor1'];
$this->description2 = '';
$this->color = $data['color'];
//$this->user_id = Auth::user()->id;
}
public static function fromRequest($request)
{
dump('inp=>',(new self ())->load($request->input()));
return (new self ())->load($request->input());
}
}
Please explain to me why I receive null while request->input() is an array, I call it from another place
$dto=GoodDto::fromRequest($request);
Method chaining, returns the last return from the chain. The other returns are used to call the next link in the chain.
(new self ())->load()
So load() needs to return $this
public function load($data)
{
...
return $this;
}
Currently it returns null, which is why it returns null.
See you are not saving the instance from the constructor, instead you pass it to load by enclosing it within the (....). By pass it I mean you call the load method on the return from the constructor.
You can test this like so:
class foo{
function load(){
return $this;//return this
}
}
var_dump((new foo)->load());
class bar{
function load(){
//return null
}
}
var_dump((new bar)->load());
Output
//return this
object(foo)#1 (0) {
}
//return null
NULL
sandbox
The second class in the example above class bar, is essentially what you are doing.
PS. forgot to scroll down on your post at first ... lol ... So I had to update my answer.
Bonus
You can also simplify the load code like this:
public function load($data)
{
foreach($data as $prop=>$value){
if(property_exists($this,$prop)) $this->$prop = $value;
}
return $this;
}
This way if you add new properties you don't have to edit the load method ever again, you just have to name the array elements the same as the class properties. You can even throw an error if the property does not exist if you want, by adding an else to the condition etc...
Personally, when I do this I prefer to call a set method like this:
//eg. $data = ['foo' => '2019-06-16']
public function load(array $data)
{
foreach($data as $prop=>$value){
$method = 'set'.$prop; //$method = 'setfoo' using the example above
if(method_exists($this,$method )){
$this->$method($value); //calls 'setfoo' with '2019-06-16'
}else{
throw new Exception('Unknown method '.$method);
}
}
return $this;
}
public function setFoo($date){
$this->foo = new DateTime($date);
}
Then you can apply some transforms to the data etc... PHP method names are not case sensitive. You can even combine these by first checking for a method then a property then throw the error etc...
Cheers.

Correct array_push usage in a class?

I have a PHP class like so:
<?php
class MyClass {
public $my_variable = array();
public function func1() {
$var1 = $this->my_variable;
array_push($var1, 'var1');
return $this->my_variable;
}
public function func2() {
$var2 = $this->my_variable;
array_push($var2, 'var2');
return $this->my_variable;
}
}
$my_class = new MyClass;
print_r($my_class->func1());
print_r($my_class->func2());
?>
The two print_r functions return an empty array, and there are no errors displayed.
How can I get the "var1" and "var2" strings added to the $my_variable array? I'm not sure where I am going wrong here...!
Thanks.
$var1 = $this->my_variable actually creates a copy of the array, which you then push a value onto.
Instead, you can do this: $var1 = &$this->my_variable to create a reference instead, but it would just be better to not have the pointless variable at all:
public function func1() {
$this->my_variable[] = 'var1';
return $this->my_variable;
}
public function func2() {
$this->my_variable[] = 'var2';
return $this->my_variable;
}
Or, more appropriately:
public function add($value) {
$this->my_variable[] = $value;
return $this->my_variable;
}
// call with `$my_class->add('var1'); $my_class->add('var2');
You have to assign the $var's by reference. You copy the array and then add to the copy some entry and then return the initial array.
$var2 = &$this->my_variable;
would be right. The & is marking here a reference.

Getting class of variable

How can I figure out in what class a reference to a variable was initiated (and currently exists)?
Example:
<?php
class MyClass {
public $array = array(
"this",
"is",
"an",
"array"
);
}
$class = new MyClass();
$arrayReference = &$class->array;
GetClassForVariable($arrayReference); //Should return "MyClass"
?>
My best bet is some kind of Reflection, but I haven't found any functions that seem suitable for this.
Edit:
A better suited example for what I want is the following:
<?php
class API_Module {
public $module;
public $name;
private $methods = array();
public function __construct($module, $name) {
$this->module = $module;
$this->name = $name;
$this->methods["login"] = new API_Method($this, "login", "Login");
}
public function GetMethod($method) {
return $this->methods[$method];
}
public function GetURL() {
return $this->module; //Should return "session"
}
}
class API_Method {
public $method;
public $name;
private $parentReference;
private $variables = array();
public function __construct(&$parentReference, $method, $name) {
$this->parentReference = $parentReference;
$this->method = $method;
$this->name = $name;
$this->variables["myvar"] = new API_Variable($this, "myvar");
}
public function GetURL() {
return $this->GetParentURL() . "/" . $this->method; //Should return "session/login"
}
public function GetVariable($variableName) {
return $this->variables[$variableName];
}
private function GetParentURL() {
// Need to reference the class parent here
return $this->parentReference->GetURL();
}
}
class API_Variable {
public $name;
private $parentReference;
public function __construct(&$parentReference, $name) {
$this->parentReference = $parentReference;
$this->name = $name;
}
public function GetURL() {
return $this->GetParentURL() . "/" . $this->name; //Should return "session/login/myvar"
}
private function GetParentURL() {
// Need to reference the class parent here
return $this->parentReference->GetURL();
}
}
$sessionModule = new API_Module("session", "Session");
var_dump($sessionModule->GetMethod("login")->GetVariable("myvar")->GetURL()); //Should return "session/login/myvar"
?>
Now, this works fine, but I'd love to be able to do this without using $parentReference in every single subvariable. It might not be possible, but I'd love to know whether it is or not.
For your example:
$class = new MyClass();
$arrayReference = &$class->array;
GetClassForVariable($arrayReference); //Should return "MyClass"
to find out to which variable originally the alias $arrayReference refers to is not possible in PHP. There is no function available resolving the aliases.
Additionally $class->array is just a variable on it's own. So you would also need to find out based on a value in which class it was defined. That is not possible as well, similar to that PHP does not offer anything to resolve a variable alias, it also does not offer anything to learn about the definition of a variable.
So in short PHP does not have a ReflectionVariable class available ;) I wonder if it is even possible.
The get_class() function should work:
http://php.net/manual/en/function.get-class.php
I agree with GRoNGoR that you shouldn't need to get the parent class of a property of an instantiated object. You could instead just get the name of the class before accessing the property. For example:
$class = new MyClass();
$parent_class = get_class($class); // returns "MyClass"
$arrayReference = &$class->array;
Not sure why you'd need the parent class of the property when you have the object instance and can easily get the parent class from there.

Setting array values in $_SESSION through an accessor method

Can anyone please explain to me why the following code does not set the values on the array as expected? $_SESSION['foo'] stays empty, even after assigning time() and rand(). I've checked, the __get accessor method is actually called when assigning the variables but they aren't stored for one reason or another.
$test = Session::getSession('test');
$test->foo = array();
$test->foo[] = time();
$test->foo['baz'] = rand(1,9);
var_dump($_SESSION);
Using this simple Session wrapper
class Session
{
protected $namespace = null;
public static function getSession($namespace)
{
return new Session($namespace);
}
public static function destroySession($namespace)
{
if(isset($_SESSION[$namespace])) {
unset($_SESSION[$namespace]);
return true;
}
return false;
}
private function __construct($namespace)
{
$this->namespace = $namespace;
if(!isset($_SESSION[$namespace])) {
$_SESSION[$namespace] = null;
}
}
public function &__get($name)
{
return (isset($_SESSION[$this->namespace][$name])) ? $_SESSION[$this->namespace][$name] : null;
}
public function __set($name, $value)
{
$_SESSION[$this->namespace][$name] = $value;
}
}
In case it might be relevant, i'm using php 5.3.6
I 'm not sure if this can be made to work at all.
For one, to return by reference you should add the & operator at the call site as well. I 'm not sure how that might be possible without screwing up the nice syntax you 're trying to achieve.
Also, you cannot return expressions by reference (only variables). So this won't work:
public function &__get($name)
{
return (isset($_SESSION[$this->namespace][$name]))
? $_SESSION[$this->namespace][$name]
: null;
}
At the very least it should be written as
public function &__get($name)
{
$value = isset($_SESSION[$this->namespace][$name])
? $_SESSION[$this->namespace][$name]
: null;
return $value;
}

Initialize static array for use it in other static variable of other class

class Assignation {
private $VVal_1 = 1;
private $VNam_1 = "One";
//....Multiple Items
private $VVal_2000 = 2000; //For Example
private $VNam_2000 = "Two Thousands"; //For Example
private static $Hash = array(); //How to initialize???
private static function Assigning(){
//This function for to assign the array elements (initialize)
global $Hash;
$this->Hash = array(); //to empty Hash variable and not add more data than it should.
$this->Hash[$this->VVal_1] = $this->VNam_1;
//....Multiple Items
$this->Hash[$this->VVal_2000] = $this->VNam_2000;
}
public static function GetVal($Nam) {
$this->Assigning(); //or use self::Assigning(); //I want to avoid this call
if (array_search($Nam, $this->Hash))
return array_search($Nam, $this->Hash);
return -1;//error
}
public static function GetNam($Val) {
$this->Assigning(); //or use self::Assigning(); //I want to avoid this call
if (array_key_exists($Val, $this->Hash))
return $this->Hash[$Val];
return "Error";
}
}
Class Testing {
static $OtherVal = Assignation::GetVal("BLABLA"); //for example
static $OtherNam = Assignation::GetNam(20); //for example
//Other functions...
}
Hi, you can see my script or code php...
I need to initialize the Hash array, this have static word because I need to use it in other static function. And this "other function" need to use it for other static variable...
I need to know how to implement it the right way..
Thanks chep.-.
<?php
echo "pre-Class Assignation<br/>";
class Assignation {
private $VVal_1 = 1;
private $VNam_1 = "One";
private $VVal_2K = 2000;
private $VNam_2K = "Two Thousands";
private static $Hash = array();
private static function Assigning(){
if(!empty(self::$Hash)) return;
self::$Hash[$this->VVal_1] = $this->VNam_1;
self::$Hash[$this->VVal_2K] = $this->VNam_2K;
}
public static function GetVal($Nam) {
self::Assigning();
if (array_search($Nam, self::$Hash)) return array_search($Nam, self::$Hash);
return -1;//error
}
public static function GetNam($Val) {
self::Assigning();
if (array_key_exists($Val, self::$Hash)) return self::$Hash[$Val];
return "Error";
}
}
echo "post-Class Testing<br/>";
echo Assignation::GetVal("BLABLA");
echo "post-Class Mid<br/>";
echo Assignation::GetNam(20);
echo "post-Class Sample<br/>";
//Testing::MyPrint();
?>
This code is not running, somebody help me testing the code...
result:
pre-Class Assignation
post-Class Assignation
post-Class Testing
that mean:
" echo Assignation::GetVal("BLABLA");"
have error...
In Assigning(), try using self::$Hash rather than $this->Hash and remove the global $Hash. Same applies for calling Assigning(): self::Assigning() as your comments suggest.
$this references the current object, so you must use self:: for all static functions and member data when inside the class.
Also, if this is your real code and not just a sample, you may want to check whether you have already done initialization, otherwise you will be doing it for every call to GetVal() and GetNam(). You could do this by adding something like if(!empty(self::$Hash)) return at the beginning of Assigning()
EDIT
private static function Assigning() {
if(!empty(self::$Hash)) return; // already populated
self::$Hash = array();
self::$Hash[$this->VVal_1] = $this->VNam_1;
//....Multiple Items
self::$Hash[$this->VVal_2K] = $this->VNam_2K;
}

Categories