Using array_push with closures overwriting previous element - php

I have a strange problem. I am writing a callback-like system where some code calls a function in a class which adds the function to an array and then later on executes it. Here is my code:
class BaconClass {
private $array;
public function __construct() {
$this->array = array();
}
public function AddBacon($function) {
array_push($this->array, $function);
}
/* ... */
public function GetBacon() {
foreach($this->array as $function) {
echo $function();
}
}
}
Then I have some other code like this:
$bacon = new BaconClass();
$bacon->AddBacon(function() {
echo "Om nom nom";
});
/* And somewhere else I might have */
$bacon->AddBacon(function() {
echo "I like bacon";
});
/* And then after all of this I have */
$bacon->GetBacon();
This code will only print:
I like bacon
I have made sure that the array being passed to the AddBacon function is actually working, but whenever I use var_dump to see what is inside the array after I add an element to it, it always shows one object, which is always the latest one added.
Why is the code overwriting the previous element? If there is a better way to implement this code, I am open to suggestions.
What I have tried
I have tried using $this->array[] = $function, and I have also tried using $this->array[count($this->array)] = $function. Neither are working.

Just tested the provided code in php console, got this output;
class BaconClass {
private $array;
public function __construct() {
$this->array = array();
}
public function AddBacon($function) {
array_push($this->array, $function);
}
/* ... */
public function GetBacon() {
foreach($this->array as $function) {
echo $function();
}
}
}
$bc = new BaconClass();
$bc->AddBacon(function() {echo 'something'; });
$bc->AddBacon(function() {echo 'something else'; });
$bc->GetBacon();
// outputs: somethingsomething else
Seems to be working fine to me

Related

How to output value of array which is set by php class?

I hope someone can help me with my problem. I'm still a beginner in PHP so sorry.
My code looks like this:
class Component
{
public $title;
// value if nothing gets set
public function __construct($title = "Test") {
$this->title = $title;
}
public function setTitle($value)
{
$this->title = $value;
}
public function getTitle()
{
return "Title: ".$this->title;
}
public function returnInfo()
{
$info = array(
'Titel' => $this->title,
);
return $info;
}
So in the class "Component" the functions should set and get a specific value. If nothing is set for a.e. title it should get the value "Test". With returnInfo() the informations like title should get returned.
My other class (where someone can add the informations like title) looks like this:
abstract class ComponentInfo extends Component
{
protected function getComponentInfo ()
{
$button1 = new Component;
$button1->setTitle("Button-Standard");
// should return all infos for button1
$button1Info = $button1->returnInfo();
foreach ($button1Info as $info)
{
echo ($info);
}
}
}
So it should work like this: in a other class named ComponentInfo a user can add a component like a button. Then the user can set informations like the title. And after that the information should get saved in an array and now I want to display all informations like this:
Title: Button-Standard
How can it work? And where is the mistake in my code?
It would be helpful to get a working code where the user can make as much ComponentInfo classes he want and where he can add different components with information that can be saved into an array.
And at the end it should get outputed as text on a main page.
You can't instantiate an abstract class. You need to remove the abstract keyword from the ComponentInfo class.
UPDATE given the info in your comment, I'd go this
Component.php
abstract class Component
{
private $key;
private $title;
public function __construct($key, $title) {
$this->setKey($key);
$this->setTitle($title);
}
public function setKey($key)
{
$this->key = $key;
}
public function getKey()
{
return $this->key;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
public function __toString()
{
return $this->getKey().': '.$this->getTitle();
}
}
ComponentInfo.php
class ComponentInfo extends Component
{
public function __construct($key='Info', $title='example title')
{
parent::__construct($key, $title);
}
}
And then use it in your code
somefile.php
$components = [];
$components []= new ComponentInfo();
$components []= new ComponentInfo('Different Key', 'Other info');
$components []= new ComponentNavigation('longitude', 'latidude'); //create another class like ComponentInfo
[... later you want to print this info in a list for example]
echo '<ul>';
foreach($components as $components) {
echo '<li>'.$component.'</li>'; //the __toString() method should get called automatically
}
echo '</ul>';
This should work, however, having different components with no other specificities than a different title and key is pointless. Instead, you could simply have different Components with a different key and title.

PHP: Dynamically retrieving global variables

I am attempting to write a simple wrapper class to get the value of a global variable. I was thinking to use it like this:
print_r($class->session()->getAll());
print_r($class->cookie()->getAll());
Here's what I have:
class GlobalVars() {
private $current;
public function session() {
$this->current = 'SESSION';
return $this;
}
public function cookie() {
$this->current = 'COOKIE';
return $this;
}
public function getAll() {
return $_{$this->current}; // Obviously wrong
}
public function get($key) {
if (!isset($_{$this->current}[$key])) { // Obviously wrong
return false;
}
return $_{$this->current}[$key]; // Obviously wrong
}
public function set($arr) {
if (is_array($arr)) {
foreach ($arr as $k => $v) {
$_{$this->current}[$k] = $v;
}
}
}
}
$class = new GlobalVars();
print_r($class->session()->getAll());
With this example, I get a Notice: Undefined variable: _ message. What do I need to modify to get this to work?
In my opinion this is just a simple syntactical error you have made. What you did:
public function getAll() {
return $_{$this->current}; // Obviously wrong
}
But the correct way to emulate a variable from string is:
public function getAll() {
return ${"_".$this->current};
}
I have tested it. Similar behaviour for the other variables. More information on variable variables in the docs: http://php.net/manual/en/language.variables.variable.php
It's not gonna work like this. You need variable variables:
$var = "_{$this->current}";
var_dump($$var['rnd']);
Example
It's very bad way to use varVars, because it's not readable and usually IDE does not know what are you using and it's easy to get buggy code.

pre and post processes' function in php

if have a bunch of functions :
function one () { ... }
function two () { ... }
...
function ten () { ... }
is there a way to execute a pre and post function for each ten functions above WITHOUT MODIFY THEM?
for instance,
function one () { print '1'; }
function pre () { print 'start'; }
function post () { print 'finish'; }
set_pre_function (one, pre);
set_post_function (one, post);
one();
result :
start
1
finish
What about this approach?
You will need to add your functions to the class, and make them private or protected, but you call them directly.
class prePostClass {
private $preFunc;
private $postFunc;
public function set_pre_function($func) {
$this->preFunc = $func;
}
public function set_post_function($func) {
$this->postFunc = $func;
}
public function __call($name, $arguments) {
if (!is_null($this->preFunc)) {
$this->preFunc();
}
$return = call_user_func_array(__CLASS__ . '->' . $name, $arguments);
if (!is_null($this->postFunc)) {
$this->postFunc();
}
return $return;
}
}
try call_user_func :
function callfunc($name,$params=false){
//Call pre
//Call real function
call_user_func($name,$params);
//and the last, call post
}
thank #KatrinRaimond :)
Like this. I haven't used it but its supposed to work.
function one ()
{
global $pre_one;
global $post_one;
$pre_one();
/* code here */
$post_one();
}
func_one() { }
func_two() {}
$pre_one = 'func_one';
$post_one = 'func_two';
You can accomplish this by simply calling your relevant pre and post functions at the beginning and end of the one function. That is, if they need to be their own functions.
function pre () { print 'start'; }
function post () { print 'finish'; }
function one () { pre(); print '1'; post(); }
one();

How do you bind a function to another function 'event status' in PHP

To ease up the debuging for my class(es) I want to bind a function to the status of other function events. The current set-up I have, is similair to following the code:
class config {
function __construct($file) {
$this->functions = array(); // The array with function run/succes information
if (!empty($file)) {
$this->checkFile($file);
}
}
public function checkFile($file) {
$this->functionStatus('run',true);
if (file_exists($file)) {
$this->functionStatus('succes',true);
return true;
} else {
$this->functionStatus('succes',true);
return false;
}
}
/* Simplified functionStatus function */
private function functionStatus($name,$value) {
/* - validations removed -*/
// Get parent function name
$callers = debug_backtrace();
$function = $callers[1]['function'];
/* - validations removed -*/
$this->functions[$function][$name] = $value;
}
}
An example of the principle in use:
$config = new config('example.ini');
print var_dump($config->functions);
/* Results in:
array(1) {
["checkFile"]=> array(2) {
["run"]=> bool(true)
["succes"]=> bool(true)
}
}
*/
While this set-up works fine. I would like to improve it by removing the manually placed $this->functionStatus('run',true) function everytime I create a function to keep the code a bit cleaner and prevent assuming a function isn't run because someone forgat defining the functionStatus at top of the function. Same goes for the definitions at the return events.
*Note, the best solution would also support this binding with other classes
Is there any way to accomplish this 'event binding' with PHP ?
You can do this using the __call magic method. Change all your public functios to private methods, and add a prefix to the name, e.g.
private function internal_checkFile($file) {
...
}
Then add the magic method:
public function __call($name, $arguments) {
$this->functionStatus('run', true);
return call_user_func_array(array($this, "internal_$name"), $arguments);
}

How do I make PHP's Magic __set work like a natural variable?

Basically, what I want to do is create a class called Variables that uses sessions to store everything in it, allowing me to quickly get and store data that needs to be used throughout the entire site without working directly with sessions.
Right now, my code looks like this:
<?php
class Variables
{
public function __construct()
{
if(session_id() === "")
{
session_start();
}
}
public function __set($name,$value)
{
$_SESSION["Variables"][$name] = $value;
}
public function __get($name)
{
return $_SESSION["Variables"][$name];
}
public function __isset($name)
{
return isset($_SESSION["Variables"][$name]);
}
}
However, when I try to use it like a natural variable, for example...
$tpl = new Variables;
$tpl->test[2] = Moo;
echo($tpl->test[2]);
I end up getting "o" instead of "Moo" as it sets test to be "Moo," completely ignoring the array. I know I can work around it by doing
$tpl->test = array("Test","Test","Moo");
echo($tpl->test[2]);
but I would like to be able to use it as if it was a natural variable. Is this possible?
You'll want to make __get return by reference:
<?php
class Variables
{
public function __construct()
{
if(session_id() === "")
{
session_start();
}
}
public function __set($name,$value)
{
$_SESSION["Variables"][$name] = $value;
}
public function &__get($name)
{
return $_SESSION["Variables"][$name];
}
public function __isset($name)
{
return isset($_SESSION["Variables"][$name]);
}
}
$tpl = new Variables;
$tpl->test[2] = "Moo";
echo($tpl->test[2]);
Gives "Moo".

Categories