class Mediator {
protected $events = array();
public function attach($eventName, $callback) {
if (!isset($this->events[$eventName])) {
$this->events[$eventName] = array();
}
$this->events[$eventName][] = $callback;
}
public function trigger($eventName, $data = null) {
foreach ($this->events[$eventName] as $callback) {
$callback($eventName, $data);
}
}
}
$mediator = new Mediator;
$mediator->attach('stop', function() { echo "Stopping"; });
$mediator->attach('stop', function() { echo "Stopped"; });
$mediator->trigger('stop'); // prints "StoppingStopped"
I can't figure out how I can successfully pass data to the pattern, i.e. I would want to pass the database object, but it ends up like this.
$mediator->attach('test', function($test) { echo $test; });
$mediator->trigger('test', '123');
It prints out "test", not 123.
All you need is to replace :
$callback($eventName, $data);
With
$callback($data);
See Live Demo
Related
How to create a callback function that has multiple callback functions from an array:
$fn = function() { echo '1';};
$fn2 = function() { echo '2';};
$array = [
$fn,
$fn2
];
$callback = ... $array; // Calls first $fn then $fn2.
Bigger context:
I am using some library where some class has a callback function as a property, this refers to a function that can be executed before the actual operation.
public function before(callable $fn)
{
$this->before = $fn;
return $this;
}
By default, for my work, I fill it with a certain function, so you can't add another one.
Due to the fact that the class has $this->before and few key methods privately created, I am not able to overwrite by my own classes and I unfortunately it is a third-party library and I can't make changes to it
I came up with the idea of overriding the class and the main method that is used to set this callback so that my class will have an array, and at the point of adding the callback function before calling the parent, I will create one callback function from all the functions added to the array.
/**
* #var callable[]
*/
private array $beforeCallbacks = [];
public function before(callable $fn): ChildrenClass
{
$this->beforeCallbacks[] = $fn;
foreach ($this->beforeCallbacks as $callback) {
if (!isset($newCallback)) {
$newCallback = $callback;
}
$newCallback .= $callback; // As you can guess, it doesn't work:C
}
return parent::before($newCallback);
}
Any suggestions?
I wonder if that's even possible.
And what if I wanted to inject a parameter into each function, is there any way to handle this?
One option is to wrap your callbacks in a structure that can handle calling multiple and in the order you want. The version below uses __invoke but you could do whatever callable syntax for PHP that you want.
class MultipleCaller {
private $callbacks = [];
public function addCallback(callable $fn) {
$this->callbacks[] = $fn;
}
public function __invoke() {
foreach($this->callbacks as $callback) {
$callback();
}
}
}
$mc = new MultipleCaller();
$mc->addCallback(static function () { echo 1, PHP_EOL; } );
$mc->addCallback(static function () { echo 2, PHP_EOL; } );
$mc();
edit
Yes, arguments can be passed. One option is to use ... to pass things through
class MultipleCaller {
private $callbacks = [];
public function addCallback(callable $fn) {
$this->callbacks[] = $fn;
}
public function __invoke(...$args) {
foreach($this->callbacks as $callback) {
$callback(...$args);
}
}
}
$mc = new MultipleCaller();
$mc->addCallback(static function (...$args) { echo 'Function 1', PHP_EOL, var_dump($args), PHP_EOL; } );
$mc->addCallback(static function (...$args) { echo 'Function 2', PHP_EOL, var_dump($args), PHP_EOL; } );
function doWork(callable $fn, ...$args) {
$fn(...$args);
}
doWork($mc, 'alpha', 'beta');
Demo: https://3v4l.org/TGdJq
func_get_args could also be used in a similar fashion
edit 2
The magic __invoke can be skipped, too, if you'd rather have a more explicit method to call. You could then use [$mc, 'invoke'] or the more modern $mc->invoke(...) syntax.
<?php
class MultipleCaller {
private $callbacks = [];
public function addCallback(callable $fn) {
$this->callbacks[] = $fn;
}
public function invoke(...$args) {
foreach($this->callbacks as $callback) {
$callback(...$args);
}
}
}
$mc = new MultipleCaller();
$mc->addCallback(static function (...$args) { echo 'Function 1', PHP_EOL, var_dump($args), PHP_EOL; } );
$mc->addCallback(static function (...$args) { echo 'Function 2', PHP_EOL, var_dump($args), PHP_EOL; } );
function doWork(callable $fn, ...$args) {
$fn(...$args);
}
doWork([$mc, 'invoke'], 'alpha', 'beta');
doWork($mc->invoke(...), 'alpha', 'beta');
Demo: https://3v4l.org/Zd1De#v8.2.2
I found a solution:
$fn = function() { var_dump('First');};
$fn2 = function() { var_dump('Second');};
$fn3 = function() { var_dump(func_get_args());};
$array = [
$fn,
$fn2,
$fn3
];
$callback = function () use ($array) {
foreach ($array as $fn) {
$fn(...func_get_args());
}
};
$callback('Passed parameter');
will display:
string(5) "First"
string(6) "Second"
array(1) {
[0]=>
string(16) "Passed parameter"
}
I got two functions in my model:
function getPersonsByGroup($groupId, $callback) {
$group = StutGroup::where('stg_id', $groupId)->get();
$persons = [];
foreach($group as $gr) {
foreach($gr->students as $stud) {
$persons[] = $stud->person;
}
}
return $callback(collect($persons));
}
function joinStudentsToPersons($person) {
return $person->each(function ($pers) {
$pers->student = \DB::connection('pgsql2')->table('students')->where('stud_pers_id', $pers->pers_id)->get();
});
}
I'm trying to call the function getPersonsByGroup in my controller passing the reference to the callback as follows:
$students = $studGroup->getPersonsByGroup($request->group, $studGroup->joinStudentsToPersons);
But if I pass an anonymous function to the getPersonsByGroup everything works well:
$students = $studGroup->getPersonsByGroup($request->group, function($person) {
return $person->each(function ($pers) {
$pers->student = \DB::connection('pgsql2')->table('students')->where('stud_pers_id', $pers->pers_id)->get();
});
});
What am I doing wrong?
The solution to your problem, if you want to keep this kind of structure, is to make the method return the closure like so:
function joinStudentsToPersons() {
return function ($person) {
$person->each(function ($pers) {
$pers->student = \DB::connection('pgsql2')->table('students')
->where('stud_pers_id', $pers->pers_id)
->get();
});
};
}
And then call it like:
$students = $studGroup->getPersonsByGroup($request->group, $studGroup->joinStudentsToPersons());
I don't even know if this is possible but I'm trying to set an optional value to an existing object.
Here is a simplified version of the code I'm trying.
<?php
class configObject {
private $dataContainer = array();
public function set($dataKey, $dataValue) {
$this->dataContainer[$dataKey] = $dataValue;
return TRUE;
}
public function get($dataKey) {
return $this->dataContainer($dataKey);
}
$this->set('someValue', 'foobar');
} //End configObject Class
function getPaginationHTML($c = &$_config) {
$someOption = $c->get('someValue');
// Do other stuff
return $html;
}
$_config = new configObject();
$html = getPaginationHTML();
?>
I'm getting the error:
syntax error, unexpected '&' in
Any help is appreciated, again I'm not sure if it's even possible to do what I'm trying to do so sorry for being a noob.
Thanks
example with the decorator pattern:
class ConfigObject {
private $dataContainer = array();
public function set($dataKey, $dataValue) {
$this->dataContainer[$dataKey] = $dataValue;
return true;
}
public function get($dataKey) {
return $this->dataContainer[$dataKey];
}
}
class ConfigObjectDecorator {
private $_decorated;
public function __construct($pDecorated) {
$this->_decorated = $pDecorated;
}
public function getPaginationHTML($dataKey) {
$someOption = $this->get($dataKey);
// Do other stuff
$html = '<p>' . $someOption . '</p>';
return $html;
}
public function set($dataKey, $dataValue) {
return $this->_decorated->set($dataKey, $dataValue);
}
public function get($dataKey) {
return $this->_decorated->get($dataKey);
}
}
class ConfigFactory {
public static function create () {
$config = new ConfigObject();
return new ConfigObjectDecorator($config);
}
}
$config = ConfigFactory::create();
if ($config->set('mykey', 'myvalue'))
echo $config->getPaginationHTML('mykey');
Note that can easily rewrite ConfigFactory::create() to add a parameter to deals with other types of decoration (or none).
I want to have a function and then use it multiple times with different parameters.
For example:
<?php
class Test {
var $test;
public function func($val) {
$this->test = $val;
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = $this->test;
}
return $ret;
}
}
?>
Then on calling page:
$test = new Test;
$test->func("test1");
$test->func("test2");
echo $test->buildFunc();
Then it prints test2 on the screen. And I want it to print out both of them.
Either create 2 instances of your object;
$test1 = new Test;
$test1->func("test1");
$test2 = new Test;
$test2->func("test2");
echo $test1->buildFunc();
echo $test2->buildFunc();
Or make test an array;
class Test {
var $test = array();
public function func($val) {
$this->test[] = $val;
}
public function buildFunc() {
return print_r($this->test, true);
}
}
May be you mean that you want to store all values? Then use an array:
public function func($val) {
$this->test[] = $val;
}
public function buildFunc() {
return $this->test
}
And then work with the result as with an array.
Well.. your code does exactly what are you telling it to do. Consider situation when you have no OOP:
$str = 'test 1';
$str = 'test 2';
echo $str; //prints test 2
So you need to echo them separately as if it wont be an OOP situation.
$test = new Test;
$test->func("test1");
echo $test->buildFunc();
$test->func("test2");
echo $test->buildFunc();
When calling the method create 2 instances of the test object.
$test = new Test;
$test->func("test1");
echo $test->buildFunc();
$test2 = new Test;
$test2->func("test2");
echo $test2->buildFunc();
if you dont want to create 2 instances you have to make a array instead.
How about create a constructor and initialize the value of test and concat the second value.
<?php
class Test {
var $test;
public function __construct($init){
$this->test = $init;
}
public function func($val) {
$this->test .= $val;
return $this;
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = $this->test;
}
return $ret;
}
}
$test = new Test("test1");
$test->func("test2");
echo $test->buildFunc();
?>
When you say both do you mean something like
test1test2
or do you want
test1
test2
For the first option you can just append the string:
<?php
class Test {
var $test;
public function func($val) {
$this->test = $test . $val; <-- add val to the end
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = $this->test;
}
return $ret;
}
}
?>
For the second:
<?php
class Test {
var $test = array();
public function func($val) {
$this->test[] = $val; <-- add val to
}
public function buildFunc() {
if(!empty($this->test)) {
foreach($test as $item){
echo $item . "<br/>";
}
}
}
}
?>
Push the variables to an array
<?php
class Test {
var $test;
public function __construct(){
$this->test=array();//Declare $test as an array
}
public function func($val) {
$this->test[]=$val;//Push to array
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = implode(",",$this->test);
}
return $ret;
}
}
?>
which is the best way to "prepare/store a function call"* for to, in a later point, actually execute it?
(* with an undetermined number of parameters)
what I have now:
function addCall($className, [$parameter [, $parameter ...]])
{
$this->calls[] = func_get_args();
}
then I'll do:
foreach($this->calls as $args)
{
$r = new ReflectionClass(array_shift($args));
$instances[] = $r->newInstanceArgs($args);
}
which doesn't look very OOP to me, including the "undetermined number of parameters" characteristic
how can I improve my code?
thank you in advance
You might be interested in the Command pattern.
How you implement it is up to you - or the framework you're using.
But those patterns usually stack up. So have a good read of the "surrounding" patterns, too, to be able to make a good choice regarding the actual implementation (or choosing an existing library).
completely informal:
<?php
function foo($a, $b) {
return 'foo#'.($a+$b);
}
function bar($a,$b,$c) {
return 'bar#'.($a-$b+$c);
}
$cmds = array();
$cmds[] = function() { return foo(1,2); };
$cmds[] = function() { return bar(1,2,3); };
$cmds[] = function() { return bar(5,6,7); };
$cmds[] = function() { return foo(9,7); };
$s = new stdClass; $s->x = 8; $s->y = 8;
$cmds[] = function() use($s) { return foo($s->x,$s->y); };
// somewhere else....
foreach($cmds as $c) {
echo $c(), "\n";
}
or something like
<?php
interface ICommand {
public function /* bool */ Execute();
}
class Foo implements ICommand {
public function __construct($id) {
$this->id = $id;
}
public function Execute() {
echo "I'm Foo ({$this->id})\n";
return true;
}
}
class Bar implements ICommand {
public function __construct($id) {
$this->id = $id;
}
public function Execute() {
echo "I'm Bar ({$this->id})\n";
return true;
}
}
$queueCommands = new SplPriorityQueue();
$queueCommands->insert(new Foo('lowPrio'), 1);
$queueCommands->insert(new Foo('midPrio'), 2);
$queueCommands->insert(new Foo('highPrio'), 3);
$queueCommands->insert(new Bar('lowPrio'), 1);
$queueCommands->insert(new Bar('midPrio'), 2);
$queueCommands->insert(new Bar('highPrio'), 3);
// somewhere else....
foreach( $queueCommands as $cmd ) {
if ( !$cmd->execute() ) {
// ...
}
}
or something else ...