HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpMinor

Calling functions with variable functions

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withfunctionscallingvariable

Problem

I have some PHP code where I use variable functions to call the right function. I need to build a chart array (for example), and the chart array that comes out has a fixed format. But the data that goes into the chart or table comes from varying places in the underlying objects.

An example:

private function buildChartGroups(PropelCollection $groups, $propertyForDisplay) {
    $groupDisplayFunction = "getSum$propertyForDisplay";

    $chart = array();
    foreach ($groups as $group) {
            $chart[] = array('name' => $group->getName(), 
                             'y' => $group->$groupDisplayFunction()
                       );
        }
    }
    return $chart;
}


and then the function gets called something like:

$result = $this->buildChartGroups($groups, "Total");
...elsewhere...
$result = $this->buildChartGroups($groups, "Count");


where $groups is an array of Group objects:

class Group {
   private $name;
   private $sumTotal;
   private $sumCount;

   public function getName() {
       return $this->name;
   }

   public function getSumTotal() {
        return $this->sumTotal;  
   }

   public function getSumCount() {
        return $this->sumCount;
   }
}


This works fine and lets me re-use buildChartGroups() which I would otherwise not be able to do. But using variable functions and passing function names as strings always seems a little dirty, even though I'm sure it has its place.

Is there a way to refactor this or is there some pattern I can use to avoid them?

Solution

I agree, it does feel dirty to compute the function name like that.

A typical way to do dynamic dispatching is using polymorphism. You could formalize the mechanism for determining which function to call by making some property-extracting objects:

abstract class PropertyExtractor {
    public abstract function getProperty($group);
}

class SumTotalExtractor extends PropertyExtractor {
    public function getProperty($group) {
        return $group->getSumTotal();
    }
}

class SumCountExtractor extends PropertyExtractor {
    public function getProperty($group) {
        return $group->getSumCount();
    }
}


Your buildChartGroups() would take one of those extractors as the second parameter:

private function buildChartGroups(PropelCollection $groups, $propertyExtractor) {
    $chart = array();
    foreach ($groups as $group) {
        $chart[] = array('name' => $group->getName(), 
                         'y' => $propertyExtractor->getProperty($group));
    }
    return $chart;
}


Here it is in use:

$result = $this->buildChartGroups($groups, new SumTotalExtractor());

Code Snippets

abstract class PropertyExtractor {
    public abstract function getProperty($group);
}

class SumTotalExtractor extends PropertyExtractor {
    public function getProperty($group) {
        return $group->getSumTotal();
    }
}

class SumCountExtractor extends PropertyExtractor {
    public function getProperty($group) {
        return $group->getSumCount();
    }
}
private function buildChartGroups(PropelCollection $groups, $propertyExtractor) {
    $chart = array();
    foreach ($groups as $group) {
        $chart[] = array('name' => $group->getName(), 
                         'y' => $propertyExtractor->getProperty($group));
    }
    return $chart;
}
$result = $this->buildChartGroups($groups, new SumTotalExtractor());

Context

StackExchange Code Review Q#46855, answer score: 5

Revisions (0)

No revisions yet.