How to update value by Events in CodeIgniter 4 - php

Here is my code:
\app\Controllers\My_controller.php
namespace App\Controllers;
use CodeIgniter\Events\Events;
class My_controller extends App_Controller {
function __construct() {
parent::__construct();
require_once(APPPATH . "update_value.php");
}
function index() {
$array = array("foo", "bar");
Events::trigger('add_value_to_array', $array);
print_r($array);
}
}
\app\update_value.php
use CodeIgniter\Events\Events;
Events::on('add_value_to_array', function($array) {
error_log("add_value_to_array" . PHP_EOL, 3, "events.txt");
array_push($array, "new");
return $array;
});
I'm getting this output:
Array ( [0] => foo [1] => bar )
But it's triggering the event because it's giving a log containing this text: add_value_to_array
I want the updated array containing the 'new' item. My expected result:
Array ( [0] => foo [1] => bar [2] => new )
Is it possible? Thanks in advance.

Passing by reference vs. passing by value
When you pass a parameter to a function and subsequently make changes to that parameter within the function, the PHP interpreter passes that parameter by copy, not by reference. This means that changes you make to the parameter inside the function have no effect on the original value outside of the function.
Hence why you don't see the value of $array updated outside of the Event callback. The array you're expecting to see is returned by that Event callback, but you're not capturing it.
If you need to modify $array in-place, you tell PHP to pass by reference by prefixing the parameter with an ampersand (&$array) when passing it.

Related

PHP - adding an object with its properties to array

I have a problem with modifying an array.
foreach ($page->getResults() as $lineItem) {
print_r($lineItem->getTargeting()->getGeoTargeting()->getExcludedLocations());
}
This code gives a result:
Array
(
[0] => Google\AdsApi\Dfp\v201611\Location Object
(
[id:protected] => 2250
[type:protected] => COUNTRY
[canonicalParentId:protected] =>
[displayName:protected] => France
)
)
I'm trying to add another, [1] , same type of object to this array.
I made a class to create and add an object:
class Location{
public function createProperty($propertyName, $propertyValue){
$this->{$propertyName} = $propertyValue;
}
}
$location = new Location();
$location->createProperty('id', '2792');
$location->createProperty('type', 'COUNTRY');
$location->createProperty('canonicalParentId', '');
$location->createProperty('displayName', 'Turkey');
array_push($lineItem->getTargeting()->getGeoTargeting()->getExcludedLocations(), $location);
Then, if I pass this into print_r() function
print_r($lineItem->getTargeting()->getGeoTargeting()->getExcludedLocations());
It shows the same result.
In the end, I need to send this updated whole $lineItem to this function
$lineItems = $lineItemService->updateLineItems(array($lineItem));
But seems like before sending I can't properly add an object to the array.
Thanks in advance.
PHP returns arrays as a value instead of as a reference. This means you must set the modified value back somehow.
Looking at the library apparently in question, there seems to be setExcludedLocations method for that purpose.
So your code should be something like:
$geo_targeting = $lineItem->getTargeting()->getGeoTargeting();
$excluded_locations = $geo_targeting->getExcludedLocations();
array_push($excluded_locations, $location);
$geo_targeting->setExcludedLocations($excluded_locations);

PHP paradoxical (weird) behavior. Sending array to function by reference

Could someone explain me, why this code works properly without crashing initial array structure?
function setArrayValueByPath($path, $value, &$array)
{
foreach ($path as $p) {
$array = &$array[$p];
}
$array = $value;
return true;
}
$array = [
'a' => 'v1',
'b' => 'v2',
];
setArrayValueByPath(['hello', 'world'], '!!!', $array);
echo '<pre>';
print_r($array);
echo '</pre>';
When I run the code, I see:
Array
(
[a] => v1
[b] => v2
[hello] => Array
(
[world] => !!!
)
)
Due to the line in function:
$array = $value;
it should replace $array value, but it does not happen.
My function is based on code snippets are given here: Using a string path to set nested array data
Thank you.
Let's examine this one step at a time.
The parameter $array is a local variable within the function which contains a reference to some external array being passed in.
foreach ($path as $p) {
This iterates over ['hello', 'world']
$array = &$array[$p];
Take the original array, and "index" it with $p (i.e. [hello]). This does not currently exist so it is added to the original array. Then take a reference to that new member and save it in the local variable $array. I.e. you just created a new member of the original array, and the local variable $array no longer points to the original external array.
On the second iteration, take the variable currently pointed to by $array (see just above) and index it with $p (world). This does not exist, so create it.
}
At this point $array points to the member {original array}[hello][world]. I use the syntax {original array} here because you no longer have a reference to it, only a reference to an array two levels nested within it.
$array = $value;
This sets the value of that member to !!!, giving exactly the data structure you see.

How to change data of reference passed Array in Closure

I have an Eventbus that takes a filter name as its first parameter and a Closure as second parameter. Like this:
$this->EventBus->subscribe('FilterTestEvent', function(){/*Do Something*/});
It's called like this:
$filteredValue = $this->EventBus->filter('FilterTestEvent', $anyValue);
What I want now is to pass an array as reference to the Closure that then is changed in any way (here: add elements) and then return something as the filtered value:
$item_to_change = array('e1' => 'v1', 'e2' => 'v2');
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return true;
});
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
Now I would a print_r($item_to_change) expect to look like the following:
Array
(
[e1] => v1
[e2] => v2
[new] => LoremIpsum
)
But instead it looks like the original array:
Array
(
[e1] => v1
[e2] => v2
)
The eventbus internally stores all closures and calls them if needed through call_user_func_array() with the closure as first argument and the value as the only argument array element.
How can I achieve what it's meant to do?
Source Code to the Eventbus: http://goo.gl/LAAO7B
Probably this line:
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
is supposed to return a new filtered array, not modify the original one.
So check it:
print_r($filtered);
Passing by reference is possible by modifying a function (adding &):
function filter(&$array){ //Note & mark
$array['new_index'] = "Something new" ;
}
$array = array("a"=> "a");
filter($array); //The function now receives the array by reference, not by value.
var_dump($array); //The array should be modified.
Edit:
Make your callback return the filtered array:
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return $item ;
});
Passing by reference should not work here, because in the source code that $value variable is swapped with another value and returned after.
Ok. I found the answer. The filter function needs to be changed so that it accepts arrays as value, in which I can save the reference. For details see difference Revision 1 and Revision 2 of the Eventbus source code, here: goo.gl/GBocgl

in_array() expects parameter 2 to be array

I have this idea of an multiarray with my files sorted into different groups/arrays.
Thinking it would be the easiest and most clean way to check what file we're inside, and should the navigation state be set to active on navigation tab 1,2 or 3?
I have this array;
Array
(
[home] => Array
(
[0] => index.php
[1] => something.php
)
[tour] => Array
(
[0] => tour.php
)
[tutorials] => Array
(
[0] => tutorials.php
)
)
The idea is, if i click on the home navigation button it goes to the index.php and a function checks whether it's the Home button that needs the active state, or the Tour button. In this case its the Home button.
[home] => Array
(
[0] => index.php
I made this function
function findNavigationActive($tap, $filename) {
if(in_array($filename, $topNavigationPages[$tap])) {
return 1;
}
return 0;
}
it should check, if index.php ($filename) is in the Home ($tap) array or navi-tap, then return 1. Unfortunately i get this error insted;
Warning: in_array() expects parameter 2 to be array, null given in /project/../../topNavigationHandler.php on line 21
What am I doing wrong?
FIXED
Used global $topNavigationPages; outside the function, and set $topNavigationPages values (array) beneath, and again used global $topNavigationPages inside the function to pull the array inside.
Thanks #tombs !
What this error means is that the second parameter isn't an array, therefore you can try something like this.:
if (is_array($topNavigationPages[$tap])) {
if(in_array($filename, $topNavigationPages[$tap])) {
return 1;
}
}
return 0;
You can do this without nesting the if loops by using a strict and operator.
Hope this helps
Note:
To resolve your scope issue you can use either a global value or a constant. To use a constant use the 'define' function, for a global declare the variable as such at the beginning of your file and in every subsequent function that you use it in. I strongly recommend using a constant.

PHP Session, why array is set to 1

I am creating a notification class which uses the session to store messages. I need to create them as a multidimensional array, so I can take advantage of different 'namespaces', so as to keep messages from displaying on the wrong pages.
Here is an example:
print_r($_SESSION)
Array
(
[EVENT_CMS] => Array
(
[Notifier] => Array
(
[0] => 'Your settings have been saved.'
[1] => 'You must re-upload...'
)
)
)
Now on the settings page, these messages will print with a call to the proper method.
I am having trouble setting up the message container within the class. This is what my constructor looks like:
public function __construct($namespace = 'Notifier') {
$this->_session_start();
if(defined('SESSION_NAMESPACE')){
$this->notifications =& $_SESSION[SESSION_NAMESPACE][$namespace];
} else {
$this->notifications =& $_SESSION[$namespace];
}
}
(The SESSION_NAMESPACE constant is defined, so the true block is executed.)
$Notify = new Notifier();
$Notify->add($_GET['test']);
print_r($_SESSION);
The above code yields me this array:
$_SESSION
Array
(
[EVENT_CMS] => Array
(
[Notifier] => 1
)
)
The add message method should update the session, right? Since the notifications array is a reference? The call to update_session() has no effect on the output...
public function add($message, $class = NULL) {
$message_node = $message;
$this->notifications[] = $message_node;
$this->update_session();
}
public function update_session(){
$this->SESSION[$this->namespace] &= $this->notifications;
}
You are mixing up the bitwise operator with the reference operator. The wrong one is used in your update_session() method.

Categories