Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15392539
D20445.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
D20445.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2652,6 +2652,8 @@
'PhabricatorChartAxis' => 'applications/fact/chart/PhabricatorChartAxis.php',
'PhabricatorChartDataQuery' => 'applications/fact/chart/PhabricatorChartDataQuery.php',
'PhabricatorChartFunction' => 'applications/fact/chart/PhabricatorChartFunction.php',
+ 'PhabricatorChartFunctionArgument' => 'applications/fact/chart/PhabricatorChartFunctionArgument.php',
+ 'PhabricatorChartFunctionArgumentParser' => 'applications/fact/chart/PhabricatorChartFunctionArgumentParser.php',
'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php',
'PhabricatorChatLogChannel' => 'applications/chatlog/storage/PhabricatorChatLogChannel.php',
'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/PhabricatorChatLogChannelListController.php',
@@ -8629,6 +8631,8 @@
'PhabricatorChartAxis' => 'Phobject',
'PhabricatorChartDataQuery' => 'Phobject',
'PhabricatorChartFunction' => 'Phobject',
+ 'PhabricatorChartFunctionArgument' => 'Phobject',
+ 'PhabricatorChartFunctionArgumentParser' => 'Phobject',
'PhabricatorChatLogApplication' => 'PhabricatorApplication',
'PhabricatorChatLogChannel' => array(
'PhabricatorChatLogDAO',
diff --git a/src/applications/fact/chart/PhabricatorChartFunction.php b/src/applications/fact/chart/PhabricatorChartFunction.php
--- a/src/applications/fact/chart/PhabricatorChartFunction.php
+++ b/src/applications/fact/chart/PhabricatorChartFunction.php
@@ -7,6 +7,8 @@
private $yAxis;
private $limit;
+ private $argumentParser;
+
final public function getFunctionKey() {
return $this->getPhobjectClassConstant('FUNCTIONKEY', 32);
}
@@ -19,11 +21,51 @@
}
final public function setArguments(array $arguments) {
- $this->newArguments($arguments);
+ $parser = $this->getArgumentParser();
+ $parser->setRawArguments($arguments);
+
+ $specs = $this->newArguments();
+
+ if (!is_array($specs)) {
+ throw new Exception(
+ pht(
+ 'Expected "newArguments()" in class "%s" to return a list of '.
+ 'argument specifications, got %s.',
+ get_class($this),
+ phutil_describe_type($specs)));
+ }
+
+ assert_instances_of($specs, 'PhabricatorChartFunctionArgument');
+
+ foreach ($specs as $spec) {
+ $parser->addArgument($spec);
+ }
+
+ $parser->setHaveAllArguments(true);
+ $parser->parseArguments();
+
return $this;
}
- abstract protected function newArguments(array $arguments);
+ abstract protected function newArguments();
+
+ final protected function newArgument() {
+ return new PhabricatorChartFunctionArgument();
+ }
+
+ final protected function getArgument($key) {
+ return $this->getArgumentParser()->getArgumentValue($key);
+ }
+
+ final protected function getArgumentParser() {
+ if (!$this->argumentParser) {
+ $parser = id(new PhabricatorChartFunctionArgumentParser())
+ ->setFunction($this);
+
+ $this->argumentParser = $parser;
+ }
+ return $this->argumentParser;
+ }
public function loadData() {
return;
diff --git a/src/applications/fact/chart/PhabricatorChartFunctionArgument.php b/src/applications/fact/chart/PhabricatorChartFunctionArgument.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fact/chart/PhabricatorChartFunctionArgument.php
@@ -0,0 +1,129 @@
+<?php
+
+final class PhabricatorChartFunctionArgument
+ extends Phobject {
+
+ private $name;
+ private $type;
+
+ public function setName($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getName() {
+ return $this->name;
+ }
+
+ public function setType($type) {
+ $types = array(
+ 'fact-key' => true,
+ 'function' => true,
+ 'number' => true,
+ );
+
+ if (!isset($types[$type])) {
+ throw new Exception(
+ pht(
+ 'Chart function argument type "%s" is unknown. Valid types '.
+ 'are: %s.',
+ $type,
+ implode(', ', array_keys($types))));
+ }
+
+ $this->type = $type;
+ return $this;
+ }
+
+ public function getType() {
+ return $this->type;
+ }
+
+ public function newValue($value) {
+ switch ($this->getType()) {
+ case 'fact-key':
+ if (!is_string($value)) {
+ throw new Exception(
+ pht(
+ 'Value for "fact-key" argument must be a string, got %s.',
+ phutil_describe_type($value)));
+ }
+
+ $facts = PhabricatorFact::getAllFacts();
+ $fact = idx($facts, $value);
+ if (!$fact) {
+ throw new Exception(
+ pht(
+ 'Fact key "%s" is not a known fact key.',
+ $value));
+ }
+
+ return $fact;
+ case 'function':
+ // If this is already a function object, just return it.
+ if ($value instanceof PhabricatorChartFunction) {
+ return $value;
+ }
+
+ if (!is_array($value)) {
+ throw new Exception(
+ pht(
+ 'Value for "function" argument must be a function definition, '.
+ 'formatted as a list, like: [fn, arg1, arg, ...]. Actual value '.
+ 'is %s.',
+ phutil_describe_type($value)));
+ }
+
+ if (!phutil_is_natural_list($value)) {
+ throw new Exception(
+ pht(
+ 'Value for "function" argument must be a natural list, not '.
+ 'a dictionary. Actual value is "%s".',
+ phutil_describe_type($value)));
+ }
+
+ if (!$value) {
+ throw new Exception(
+ pht(
+ 'Value for "function" argument must be a list with a function '.
+ 'name; got an empty list.'));
+ }
+
+ $function_name = array_shift($value);
+
+ if (!is_string($function_name)) {
+ throw new Exception(
+ pht(
+ 'Value for "function" argument must be a natural list '.
+ 'beginning with a function name as a string. The first list '.
+ 'item has the wrong type, %s.',
+ phutil_describe_type($function_name)));
+ }
+
+ $functions = PhabricatorChartFunction::getAllFunctions();
+ if (!isset($functions[$function_name])) {
+ throw new Exception(
+ pht(
+ 'Function "%s" is unknown. Valid functions are: %s',
+ $function_name,
+ implode(', ', array_keys($functions))));
+ }
+
+ return id(clone $functions[$function_name])
+ ->setArguments($value);
+ case 'number':
+ if (!is_float($value) && !is_int($value)) {
+ throw new Exception(
+ pht(
+ 'Value for "number" argument must be an integer or double, '.
+ 'got %s.',
+ phutil_describe_type($value)));
+ }
+
+ return $value;
+ }
+
+ throw new PhutilMethodNotImplementedException();
+ }
+
+}
diff --git a/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php b/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php
new file mode 100644
--- /dev/null
+++ b/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php
@@ -0,0 +1,154 @@
+<?php
+
+final class PhabricatorChartFunctionArgumentParser
+ extends Phobject {
+
+ private $function;
+ private $rawArguments;
+ private $unconsumedArguments;
+ private $haveAllArguments = false;
+ private $unparsedArguments;
+ private $argumentMap = array();
+ private $argumentPosition = 0;
+ private $argumentValues = array();
+
+ public function setFunction(PhabricatorChartFunction $function) {
+ $this->function = $function;
+ return $this;
+ }
+
+ public function getFunction() {
+ return $this->function;
+ }
+
+ public function setRawArguments(array $arguments) {
+ $this->rawArguments = $arguments;
+ $this->unconsumedArguments = $arguments;
+ }
+
+ public function addArgument(PhabricatorChartFunctionArgument $spec) {
+ $name = $spec->getName();
+ if (!strlen($name)) {
+ throw new Exception(
+ pht(
+ 'Chart function "%s" emitted an argument specification with no '.
+ 'argument name. Argument specifications must have unique names.',
+ $this->getFunctionArgumentSignature()));
+ }
+
+ $type = $spec->getType();
+ if (!strlen($type)) {
+ throw new Exception(
+ pht(
+ 'Chart function "%s" emitted an argument specification ("%s") with '.
+ 'no type. Each argument specification must have a valid type.',
+ $name));
+ }
+
+ if (isset($this->argumentMap[$name])) {
+ throw new Exception(
+ pht(
+ 'Chart function "%s" emitted multiple argument specifications '.
+ 'with the same name ("%s"). Each argument specification must have '.
+ 'a unique name.',
+ $this->getFunctionArgumentSignature(),
+ $name));
+ }
+
+ $this->argumentMap[$name] = $spec;
+ $this->unparsedArguments[] = $spec;
+
+ return $this;
+ }
+
+ public function parseArgument(
+ PhabricatorChartFunctionArgument $spec) {
+ $this->addArgument($spec);
+ return $this->parseArguments();
+ }
+
+ public function setHaveAllArguments($have_all) {
+ $this->haveAllArguments = $have_all;
+ return $this;
+ }
+
+ public function parseArguments() {
+ $have_count = count($this->rawArguments);
+ $want_count = count($this->argumentMap);
+
+ if ($this->haveAllArguments) {
+ if ($want_count !== $have_count) {
+ throw new Exception(
+ pht(
+ 'Function "%s" expects %s argument(s), but %s argument(s) were '.
+ 'provided.',
+ $this->getFunctionArgumentSignature(),
+ $want_count,
+ $have_count));
+ }
+ }
+
+ while ($this->unparsedArguments) {
+ $argument = array_shift($this->unparsedArguments);
+ $name = $argument->getName();
+
+ if (!$this->unconsumedArguments) {
+ throw new Exception(
+ pht(
+ 'Function "%s" expects at least %s argument(s), but only %s '.
+ 'argument(s) were provided.',
+ $this->getFunctionArgumentSignature(),
+ $want_count,
+ $have_count));
+ }
+
+ $raw_argument = array_shift($this->unconsumedArguments);
+ $this->argumentPosition++;
+
+ try {
+ $value = $argument->newValue($raw_argument);
+ } catch (Exception $ex) {
+ throw new Exception(
+ pht(
+ 'Argument "%s" (in position "%s") to function "%s" is '.
+ 'invalid: %s',
+ $name,
+ $this->argumentPosition,
+ $this->getFunctionArgumentSignature(),
+ $ex->getMessage()));
+ }
+
+ $this->argumentValues[$name] = $value;
+ }
+ }
+
+ public function getArgumentValue($key) {
+ if (!array_key_exists($key, $this->argumentValues)) {
+ throw new Exception(
+ pht(
+ 'Function "%s" is requesting an argument ("%s") that it did '.
+ 'not define.',
+ $this->getFunctionArgumentSignature(),
+ $key));
+ }
+
+ return $this->argumentValues[$key];
+ }
+
+ private function getFunctionArgumentSignature() {
+ $argument_list = array();
+ foreach ($this->argumentMap as $key => $spec) {
+ $argument_list[] = $key;
+ }
+
+ if (!$this->haveAllArguments) {
+ $argument_list[] = '...';
+ }
+
+ return sprintf(
+ '%s(%s)',
+ $this->getFunction()->getFunctionKey(),
+ implode(', ', $argument_list));
+ }
+
+}
diff --git a/src/applications/fact/chart/PhabricatorConstantChartFunction.php b/src/applications/fact/chart/PhabricatorConstantChartFunction.php
--- a/src/applications/fact/chart/PhabricatorConstantChartFunction.php
+++ b/src/applications/fact/chart/PhabricatorConstantChartFunction.php
@@ -7,36 +7,26 @@
private $value;
- protected function newArguments(array $arguments) {
- if (count($arguments) !== 1) {
- throw new Exception(
- pht(
- 'Chart function "constant(...)" expects one argument, got %s. '.
- 'Pass a constant.',
- count($arguments)));
- }
-
- if (!is_int($arguments[0])) {
- throw new Exception(
- pht(
- 'First argument for "fact(...)" is invalid: expected int, '.
- 'got %s.',
- phutil_describe_type($arguments[0])));
- }
-
- $this->value = $arguments[0];
+ protected function newArguments() {
+ return array(
+ $this->newArgument()
+ ->setName('n')
+ ->setType('number'),
+ );
}
public function getDatapoints(PhabricatorChartDataQuery $query) {
$x_min = $query->getMinimumValue();
$x_max = $query->getMaximumValue();
+ $value = $this->getArgument('n');
+
$points = array();
$steps = $this->newLinearSteps($x_min, $x_max, 2);
foreach ($steps as $step) {
$points[] = array(
'x' => $step,
- 'y' => $this->value,
+ 'y' => $value,
);
}
diff --git a/src/applications/fact/chart/PhabricatorFactChartFunction.php b/src/applications/fact/chart/PhabricatorFactChartFunction.php
--- a/src/applications/fact/chart/PhabricatorFactChartFunction.php
+++ b/src/applications/fact/chart/PhabricatorFactChartFunction.php
@@ -5,40 +5,21 @@
const FUNCTIONKEY = 'fact';
- private $factKey;
private $fact;
private $datapoints;
- protected function newArguments(array $arguments) {
- if (count($arguments) !== 1) {
- throw new Exception(
- pht(
- 'Chart function "fact(...)" expects one argument, got %s. '.
- 'Pass the key for a fact.',
- count($arguments)));
- }
-
- if (!is_string($arguments[0])) {
- throw new Exception(
- pht(
- 'First argument for "fact(...)" is invalid: expected string, '.
- 'got %s.',
- phutil_describe_type($arguments[0])));
- }
+ protected function newArguments() {
+ $key_argument = $this->newArgument()
+ ->setName('fact-key')
+ ->setType('fact-key');
- $facts = PhabricatorFact::getAllFacts();
- $fact = idx($facts, $arguments[0]);
+ $parser = $this->getArgumentParser();
+ $parser->parseArgument($key_argument);
- if (!$fact) {
- throw new Exception(
- pht(
- 'Argument to "fact(...)" is invalid: "%s" is not a known fact '.
- 'key.',
- $arguments[0]));
- }
-
- $this->factKey = $arguments[0];
+ $fact = $this->getArgument('fact-key');
$this->fact = $fact;
+
+ return $fact->getFunctionArguments();
}
public function loadData() {
diff --git a/src/applications/fact/chart/PhabricatorSinChartFunction.php b/src/applications/fact/chart/PhabricatorSinChartFunction.php
--- a/src/applications/fact/chart/PhabricatorSinChartFunction.php
+++ b/src/applications/fact/chart/PhabricatorSinChartFunction.php
@@ -5,30 +5,20 @@
const FUNCTIONKEY = 'sin';
- private $argument;
-
- protected function newArguments(array $arguments) {
- if (count($arguments) !== 1) {
- throw new Exception(
- pht(
- 'Chart function "sin(..)" expects one argument, got %s.',
- count($arguments)));
- }
-
- $argument = $arguments[0];
-
- if (!($argument instanceof PhabricatorChartFunction)) {
- throw new Exception(
- pht(
- 'Argument to chart function should be a function, got %s.',
- phutil_describe_type($argument)));
- }
+ protected function newArguments() {
+ return array(
+ $this->newArgument()
+ ->setName('x')
+ ->setType('function'),
+ );
+ }
- $this->argument = $argument;
+ protected function assignArguments(array $arguments) {
+ $this->argument = $arguments[0];
}
public function getDatapoints(PhabricatorChartDataQuery $query) {
- $points = $this->argument->getDatapoints($query);
+ $points = $this->getArgument('x')->getDatapoints($query);
foreach ($points as $key => $point) {
$points[$key]['y'] = sin(deg2rad($points[$key]['y']));
diff --git a/src/applications/fact/chart/PhabricatorXChartFunction.php b/src/applications/fact/chart/PhabricatorXChartFunction.php
--- a/src/applications/fact/chart/PhabricatorXChartFunction.php
+++ b/src/applications/fact/chart/PhabricatorXChartFunction.php
@@ -5,13 +5,8 @@
const FUNCTIONKEY = 'x';
- protected function newArguments(array $arguments) {
- if (count($arguments) !== 0) {
- throw new Exception(
- pht(
- 'Chart function "x()" expects zero arguments, got %s.',
- count($arguments)));
- }
+ protected function newArguments() {
+ return array();
}
public function getDatapoints(PhabricatorChartDataQuery $query) {
diff --git a/src/applications/fact/controller/PhabricatorFactChartController.php b/src/applications/fact/controller/PhabricatorFactChartController.php
--- a/src/applications/fact/controller/PhabricatorFactChartController.php
+++ b/src/applications/fact/controller/PhabricatorFactChartController.php
@@ -23,6 +23,9 @@
$x_function = id(new PhabricatorXChartFunction())
->setArguments(array());
+ $functions[] = id(new PhabricatorConstantChartFunction())
+ ->setArguments(array(360));
+
$functions[] = id(new PhabricatorSinChartFunction())
->setArguments(array($x_function));
diff --git a/src/applications/fact/fact/PhabricatorFact.php b/src/applications/fact/fact/PhabricatorFact.php
--- a/src/applications/fact/fact/PhabricatorFact.php
+++ b/src/applications/fact/fact/PhabricatorFact.php
@@ -37,4 +37,8 @@
abstract protected function newTemplateDatapoint();
+ final public function getFunctionArguments() {
+ return array();
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Mar 16, 3:49 PM (6 d, 21 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7339713
Default Alt Text
D20445.diff (17 KB)
Attached To
Mode
D20445: Make chart function argument parsing modular/flexible with 900 pages of error messages
Attached
Detach File
Event Timeline
Log In to Comment