Reputation: 987
I'm programming an app (php) which requires a very long list of similar yet different functions, which are being called by a set of keys:
$functions = [
"do this" => function() {
// does this
},
"do that" => function() {
// does that
}
]
etc.
I've chosen to place the similar functions in an array because they are not similar enough - getting the same result with one big function which is full of conditional statements isn't gonna work. And I do need to be able to call them only by key, for example:
$program = ["do this", "do that", "do this"];
foreach ($program as $k => $v) {
$functions[$v]();
}
Thing is this functions-array structure is causing many problems, for example I'm having a hard time calling one array function from within another array function, e.g. this doesn't work:
"do that" => function() {
$functions["do this"]();
}
nor this:
"do that" => function() {
global $functions;
$functions["do this"]();
}
or this:
"do that" => function($functions) {
$functions["do this"]();
}
$functions["do that"]($functions);
I guess I could have one giant function with a long switch statement:
function similar_functions($key) {
switch ($key) {
case "do this":
// does this
break;
case "do that":
// does that
break;
}
}
But that doens't really seem like good practice. Or maybe it is?
So, what are my alternatives? Should I go with the switch structure? Or is there another, better solution?
Upvotes: 12
Views: 1216
Reputation: 21817
I'm all in favor of what Mamuz describes in his answer, but I do want to give you a "quick fix":
$functions = [];
$functions['do this'] = function () {
// does this
};
$functions['do call'] = function () use ($functions) {
// calls 'do this'
$functions['do this']();
}
$functions['do manipulation'] = function () use (&$functions) {
// manipulates $functions
$functions['do something else'] = function () {
// does something else
};
}
I think this is pretty self explanatory.
Upvotes: 3
Reputation: 10807
Maybe something like:
/**
* Save an array with function friendly names and its associated function and params
*/
$functions = array(
'Do This' => array(
'fn_name' => 'fn1',
'fn_args' => array('fn1', 'from fn1')),
'Do That' => array(
'fn_name' => 'fn2'
)
);
/**
* Call this per each value of the functions array, passing along that function data as parameter
*/
function execute_fn($fn_data) {
$fn_name = $fn_data['fn_name'];
if(isset($fn_data['fn_args'])) {
call_user_func_array($fn_name, $fn_data['fn_args']);
} else {
call_user_func($fn_name);
}
}
function fn1($val1, $val2) {
echo "I'm being called from $val1 and my second argument value is $val2 \n";
}
function fn2() {
echo "Doing something for fn2 \n";
fn1('fn2', 'from fn2');
}
/**
* Call this to execute the whole set of functions in your functions array
*/
foreach ($functions as $key => $value) {
execute_fn($value);
}
That way you can define in $functions
an array with function names and argument values if that particular function expects any.
Upvotes: 2
Reputation: 16055
You can do it in a cleaner way, that is, encapsulate all of these functions into a class. They can be static or dynamic, it doesn't matter much I guess in this case. I'll show both examples...
1. dynamic approach
class MyFunctions
{
public function doThis()
{
/* implement do this here */
}
public function doThat()
{
/* implement do that here */
}
/* ... */
}
/* then somewhere else */
$program = ["doThis", "doThat", "doThis"];
$myFunctions = new MyFunctions;
foreach ($program as $method) {
$myFunctions->$method();
}
2. static approach
class MyFunctions
{
public static function doThis()
{
/* implement do this here */
}
public static function doThat()
{
/* implement do that here */
}
/* ... */
}
/* then somewhere else */
$program = ["doThis", "doThat", "doThis"];
foreach ($program as $method) {
MyFunctions::$method();
}
IMHO this is cleaner than implementing those functions in an array of closures... And better than implementing a switch
as You still have to call this in a loop - so why slowing down with another switch
?
Upvotes: 7
Reputation: 1730
Closures are expensive for performance and memoryusage in php. Procedural coding provoked a big ball of mud, spaghetti code and other anti patterns. Switch structures are very hard to test and it's a violation of OCP.
You should prefer OOP in SOLID way to avoid redundancy, improving scalability and maintainablity. That is the best practice to provide a set of functions which are reuseable. You can also seperate your code in layers and modules to reduce complexity and improve interchangability.
In your case your classes can implement __invoke to call it as invokable and your keys could be the fullqualified namespaces for these classes, so you can call it like a function. From now on you can also use inheritence, polymorphism or design patterns like composite, decorator etc. to reuse other functions or to add functions.
Here a simple Example..
<?php
use Foo\Bar;
class This
{
public function __invoke()
{
$this->execute();
}
public function execute()
{
/* ... */
}
}
class That extends This
{
public function execute()
{
/* ... */
}
}
$namespaces = array("\Foo\Bar\This", "\Foo\Bar\That");
foreach ($namespaces as $fullQualifiedNamespace) {
/** @var callable $fullQualifiedNamespace */
$fullQualifiedNamespace();
}
This behavior is also reachable by implementing a specific interface to This and That. Within iteration you can check the interface to call the defined contract. Or you build a Process Class where you can add objects which implements this interface. The Process class can execute all attached objects (Chain of Responsibility).
I would prefer the Chain of Responsibility Pattern this is understandable by most developers and not so magic like PHP's __invoke interceptor. In a factory you can define your chain and you are able to define other dependencies to the chain or to the attached chain objects.
Upvotes: 20
Reputation: 879
What about the good old Command Pattern? ;)
<?php
interface Command{
public function execute();
}
class DoThatCommand implements Command{
public function execute()
{
echo "doing that...";
}
}
class DoThisCommand implements Command{
public function execute(){
echo "doing this...";
$doThatCommand = new DoThatCommand();
$doThatCommand->execute();
}
}
$program = [new DoThatCommand(), new DoThisCommand(), new DoThatCommand()];
foreach ($program as $command) {
$command->execute();
echo "\n";
}
?>
or in a more elegant dependecy injection way you can do it like:
<?php
interface Command{
public function execute();
}
class DoThatCommand implements Command{
public function execute()
{
echo "doing that... \n";
}
}
class DoThisCommand implements Command{
public function execute(){
echo "doing this... \n";
}
}
class DoTheThirdThingCommandThatDependsOnDoThisCommand implements Command{
public $doThisCommand;
public function __construct(DoThisCommand $doThisCommand)
{
$this->doThisCommand = $doThisCommand;
}
public function execute()
{
echo "doing the ThirdThingCommand that depends on DoThisCommand... \n";
$this->doThisCommand->execute();
}
}
$command1 = new DoThatCommand();
$command2 = new DoThisCommand();
$command3 = new DoTheThirdThingCommandThatDependsOnDoThisCommand($command2);
$program = [$command1, $command2, $command3];
echo "<pre>";
foreach ($program as $command) {
$command->execute();
}
?>
Upvotes: 2
Reputation: 4021
The giant function with the long switch
statement is better because it allows you to call similar_functions("do this")
as a part of similar_functions("do that")
:
function similar_functions($key) {
switch ($key) {
case "do this":
// does this
break;
case "do that":
similar_functions("do this");
// and then does that
break;
}
}
Upvotes: 1