@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 91 lines 2.9 kB view raw
1<?php 2 3final class PhabricatorComposeChartFunction 4 extends PhabricatorHigherOrderChartFunction { 5 6 const FUNCTIONKEY = 'compose'; 7 8 protected function newArguments() { 9 return array( 10 $this->newArgument() 11 ->setName('f') 12 ->setType('function') 13 ->setRepeatable(true), 14 ); 15 } 16 17 public function evaluateFunction(array $xv) { 18 $original_positions = array_keys($xv); 19 $remaining_positions = $original_positions; 20 foreach ($this->getFunctionArguments() as $function) { 21 $xv = $function->evaluateFunction($xv); 22 23 // If a function evaluates to "null" at some positions, we want to return 24 // "null" at those positions and stop evaluating the function. 25 26 // We also want to pass "evaluateFunction()" a natural list containing 27 // only values it should evaluate: keys should not be important and we 28 // should not pass "null". This simplifies implementation of functions. 29 30 // To do this, first create a map from original input positions to 31 // function return values. 32 $xv = array_combine($remaining_positions, $xv); 33 34 // If a function evaluated to "null" at any position where we evaluated 35 // it, the result will be "null". We remove the position from the 36 // vector so we stop evaluating it. 37 foreach ($xv as $x => $y) { 38 if ($y !== null) { 39 continue; 40 } 41 42 unset($xv[$x]); 43 } 44 45 // Store the remaining original input positions for the next round, then 46 // throw away the array keys so we're passing the next function a natural 47 // list with only non-"null" values. 48 $remaining_positions = array_keys($xv); 49 $xv = array_values($xv); 50 51 // If we have no more inputs to evaluate, we can bail out early rather 52 // than passing empty vectors to functions for evaluation. 53 if (!$xv) { 54 break; 55 } 56 } 57 58 59 $yv = array(); 60 $xv = array_combine($remaining_positions, $xv); 61 foreach ($original_positions as $position) { 62 if (isset($xv[$position])) { 63 $y = $xv[$position]; 64 } else { 65 $y = null; 66 } 67 $yv[$position] = $y; 68 } 69 70 return $yv; 71 } 72 73 public function getDataRefs(array $xv) { 74 // TODO: This is not entirely correct. The correct implementation would 75 // map "x -> y" at each stage of composition and pull and aggregate all 76 // the datapoint refs. In practice, we currently never compose functions 77 // with a data function somewhere in the middle, so just grabbing the first 78 // result is close enough. 79 80 // In the future, we may: notably, "x -> shift(-1 month) -> ..." to 81 // generate a month-over-month overlay is a sensible operation which will 82 // source data from the middle of a function composition. 83 84 foreach ($this->getFunctionArguments() as $function) { 85 return $function->getDataRefs($xv); 86 } 87 88 return array(); 89 } 90 91}