Skip to content

Commit

Permalink
More typing
Browse files Browse the repository at this point in the history
  • Loading branch information
ddebin committed Jun 13, 2023
1 parent 12db529 commit 750de6d
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 48 deletions.
93 changes: 66 additions & 27 deletions lib/MC/Google/Visualization.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,32 @@
*
* @see \Tests\VisualizationTest
*
* @phpstan-type FieldSpec array{callback?:callable,field?:string,extra?:array,fields?:string[],sort_field?:string,type?:string,join?:string}
* @phpstan-type FieldSpec array{
* type: string,
* field?: string,
* fields?: string[],
* callback?: callable,
* extra?: mixed[],
* sort_field?: string,
* join?: string
* }
* @phpstan-type QueryParsed array{
* select?: array<string|string[]>,
* from?: string,
* where?: null|array<array{type: string, value: string}>,
* groupby?: string[],
* pivot?: string[],
* orderby?: string[],
* limit?: string,
* offset?: string,
* label?: string
* }
* @phpstan-type Entity array{
* table: string,
* fields: array<string, FieldSpec>,
* joins: string[],
* where?: string
* }
*/
class Visualization
{
Expand All @@ -37,7 +62,7 @@ class Visualization
/**
* The entity schema that defines which tables are exposed to visualization clients, along with their fields, joins, and callbacks.
*
* @var array
* @var array<string, Entity>
*/
protected $entities = [];

Expand All @@ -60,7 +85,7 @@ class Visualization
/**
* If a format string is not provided by the query, these will be used to format values by default.
*
* @var array
* @var array<string, string>
*/
protected $defaultFormat = [
'date' => 'm/d/Y',
Expand Down Expand Up @@ -156,8 +181,8 @@ public function setDefaultFormat(string $type, string $format): void
* Handle the entire request, pulling the query from the $_GET variables and printing the results directly
* if not specified otherwise.
*
* @param bool $echo print response and set header
* @param null|array $queryParams query parameters
* @param bool $echo print response and set header
* @param null|array{tq:string, tqx:string, responseHandler?:string} $queryParams query parameters
*
* @return string the javascript response
*
Expand Down Expand Up @@ -199,22 +224,22 @@ public function handleRequest(bool $echo = true, ?array $queryParams = null): st
/**
* Handle a specific query. Use this if you want to gather the query parameters yourself instead of using handleRequest().
*
* @param string $query the visualization query to parse and execute
* @param array $params all extra params sent along with the query - must include at least "reqId" key
* @param string $query the visualization query to parse and execute
* @param array<string, float|int|string> $params all extra params sent along with the query - must include at least "reqId" key
*
* @return string the javascript response
*/
public function handleQuery(string $query, array $params): string
{
$reqId = null;
$reqId = -1;
$response = '';

try {
if (!$this->db instanceof PDO) {
throw new Visualization_Error('You must pass a PDO connection to the MC Google Visualization Server if you want to let the server handle the entire request');
}

$reqId = $params['reqId'];
$reqId = (int) $params['reqId'];
$queryParsed = $this->parseQuery($query);
$meta = $this->generateMetadata($queryParsed);
$sql = $this->generateSQL($meta);
Expand All @@ -236,13 +261,13 @@ public function handleQuery(string $query, array $params): string

$response .= $this->getSuccessClose();
} catch (Visualization_Error $visualizationError) {
$response .= $this->handleError($reqId, $visualizationError->getMessage(), $params['responseHandler'], $visualizationError->type, $visualizationError->summary);
$response .= $this->handleError($reqId, $visualizationError->getMessage(), (string) $params['responseHandler'], $visualizationError->type, $visualizationError->summary);
} catch (PDOException $pdoException) {
$response .= $this->handleError($reqId, $pdoException->getMessage(), $params['responseHandler'], 'invalid_query', 'Invalid Query - PDO exception');
$response .= $this->handleError($reqId, $pdoException->getMessage(), (string) $params['responseHandler'], 'invalid_query', 'Invalid Query - PDO exception');
} catch (ParseError $parseError) {
$response .= $this->handleError($reqId, $parseError->getMessage(), $params['responseHandler'], 'invalid_query', 'Invalid Query - Parse Error');
$response .= $this->handleError($reqId, $parseError->getMessage(), (string) $params['responseHandler'], 'invalid_query', 'Invalid Query - Parse Error');
} catch (Exception $exception) {
$response .= $this->handleError($reqId, $exception->getMessage(), $params['responseHandler']);
$response .= $this->handleError($reqId, $exception->getMessage(), (string) $params['responseHandler']);
}

return $response;
Expand All @@ -251,9 +276,11 @@ public function handleQuery(string $query, array $params): string
/**
* Given the results of parseQuery(), introspect against the entity definitions provided and return the metadata array used to generate the SQL.
*
* @param array $query the visualization query broken up into sections
* @param array<string, mixed> $query the visualization query broken up into sections
*
* @return array the metadata array from merging the query with the entity table definitions
* @phpstan-param QueryParsed $query
*
* @return array<string, mixed> the metadata array from merging the query with the entity table definitions
*
* @throws Visualization_QueryError
* @throws Visualization_Error
Expand Down Expand Up @@ -704,7 +731,9 @@ public function getGrammar(): Def
*
* @param string $str the query string to parse
*
* @return array the parsed query as an array, keyed by each part of the query (select, from, where, groupby, pivot, orderby, limit, offset, label, format, options
* @return array<string, mixed> the parsed query as an array, keyed by each part of the query (select, from, where, groupby, pivot, orderby, limit, offset, label, format, options
*
* @phpstan-return QueryParsed
*
* @throws ParseError
* @throws Visualization_QueryError
Expand All @@ -727,8 +756,10 @@ public function parseQuery(string $str): array
break;

case 'from':
$vals = $token->getValues();
$query['from'] = $vals[1];
$from = $token->getValues();
$from = $from[1];
assert(null !== $from);
$query['from'] = $from;

break;

Expand All @@ -744,7 +775,7 @@ public function parseQuery(string $str): array
$groupby = $token->getValues();
array_shift($groupby);
array_shift($groupby);
$query['groupby'] = $groupby;
$query['groupby'] = array_filter($groupby);

break;

Expand All @@ -754,7 +785,7 @@ public function parseQuery(string $str): array
}
$pivot = $token->getValues();
array_shift($pivot);
$query['pivot'] = $pivot;
$query['pivot'] = array_filter($pivot);

break;

Expand Down Expand Up @@ -784,13 +815,15 @@ public function parseQuery(string $str): array
case 'limit':
$limit = $token->getValues();
$limit = $limit[1];
assert(null !== $limit);
$query['limit'] = $limit;

break;

case 'offset':
$offset = $token->getValues();
$offset = $offset[1];
assert(null !== $offset);
$query['offset'] = $offset;

break;
Expand All @@ -803,6 +836,7 @@ public function parseQuery(string $str): array
$count = count($labels);
for ($i = 0; $i < $count; $i += 2) {
$field = $labels[$i];
assert(null !== $labels[$i + 1]);
$label = trim($labels[$i + 1], '\'"');
$queryLabels[$field] = $label;
}
Expand All @@ -818,6 +852,7 @@ public function parseQuery(string $str): array
$count = count($formats);
for ($i = 0; $i < $count; $i += 2) {
$field = $formats[$i];
assert(null !== $formats[$i + 1]);
$queryFormats[$field] = trim($formats[$i + 1], '\'"');
}
$query['formats'] = $queryFormats;
Expand Down Expand Up @@ -1290,15 +1325,18 @@ protected function getFieldSQL(string $name, array $spec, bool $alias = false, s
/**
* Recursively process the dependant fields for callback entity fields.
*
* @param array $field the spec array for the field to add (must have a "callback" key)
* @param array $entity the spec array for the entity to pull other fields from
* @param array $meta the query metadata array to append the results
* @param array $fieldSpec the spec array for the field to add (must have a "callback" key)
* @param array $entity the spec array for the entity to pull other fields from
* @param array $meta the query metadata array to append the results
*
* @phpstan-param FieldSpec $fieldSpec
* @phpstan-param Entity $entity
*
* @throws Visualization_Error
*/
protected function addDependantCallbackFields(array $field, array $entity, array &$meta): void
protected function addDependantCallbackFields(array $fieldSpec, array $entity, array &$meta): void
{
foreach ($field['fields'] as $dependant) {
foreach ($fieldSpec['fields'] ?? [] as $dependant) {
if (!isset($entity['fields'][$dependant])) {
throw new Visualization_Error('Unknown callback required field "'.$dependant.'"');
}
Expand Down Expand Up @@ -1335,6 +1373,7 @@ protected function parseFieldTokens(Token $token, array &$fields = null): void
if ($token->hasChildren()) {
if ('function' === $token->name) {
$field = $token->getValues();
assert(null !== $field[0]);
$field[0] = strtolower($field[0]);
$fields[] = $field;
} else {
Expand All @@ -1350,8 +1389,8 @@ protected function parseFieldTokens(Token $token, array &$fields = null): void
/**
* Helper method for the query parser to recursively scan and flatten the where clause's conditions.
*
* @param Token $token the token or token group to parse
* @param null|array $where the collector array of tokens that make up the where clause
* @param Token $token the token or token group to parse
* @param null|array<array{type: string, value: string}> $where the collector array of tokens that make up the where clause
*/
protected function parseWhereTokens(Token $token, array &$where = null): void
{
Expand Down
8 changes: 4 additions & 4 deletions lib/MC/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ class Parser
/**
* Return a Set with the function arguments as the subexpressions.
*/
public function set(): Set
public function set(Def ...$args): Set
{
return new Set(func_get_args());
return new Set($args);
}

/**
* Return a OneOf with the function arguments as the possible expressions.
*/
public function oneOf(): OneOf
public function oneOf(Def ...$args): OneOf
{
return new OneOf(func_get_args());
return new OneOf($args);
}

/**
Expand Down
10 changes: 4 additions & 6 deletions lib/MC/Parser/Def.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function parse(string $str): Token
$str = ltrim($str);

[$loc, $tok] = $this->parsePart($str, 0);
if ($loc !== strlen($str)) {
if ((null === $tok) || ($loc !== strlen($str))) {
throw new ParseError('An error occurred: "'.substr($str, $loc).'"', $str, $loc);
}

Expand All @@ -38,7 +38,7 @@ public function parse(string $str): Token
/**
* Parse a string, cleaning up whitespace when we're done.
*
* @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions
* @return array{int, null|Token} A two-item array of the string location where parsing stopped and the MC_Token instance that matches the grammar conditions
*
* @throws ParseError
*/
Expand All @@ -61,7 +61,7 @@ public function parsePart(string $str, int $loc): array
* @param string $str the string to parse
* @param int $loc the index to start parsing
*
* @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions
* @return array{int, null|Token} A two-item array of the string location where parsing stopped, and the Token instance that matches the grammar conditions
*
* @throws ParseError
*/
Expand Down Expand Up @@ -103,11 +103,9 @@ public function suppress(): self
/**
* Return a token instance, copying over this Def's name and flagging suppression.
*
* @param mixed $value
*
* @return null|Token the new token, or null if the token should be suppressed
*/
public function token($value): ?Token
public function token(?string $value): ?Token
{
if ($this->suppress) {
return null;
Expand Down
3 changes: 3 additions & 0 deletions lib/MC/Parser/Def/OneOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class OneOf extends Def
/** @var Def[] */
public $exprs = [];

/**
* @param Def[] $exprs
*/
public function __construct(array $exprs = [])
{
$this->exprs = $exprs;
Expand Down
6 changes: 3 additions & 3 deletions lib/MC/Parser/Def/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
*/
class Set extends Def
{
/** @var array */
/** @var Def[] */
public $exprs = [];

/**
* Set constructor.
* @param Def[] $exprs
*/
public function __construct(array $exprs = [])
{
Expand All @@ -31,7 +31,7 @@ public function _parse(string $str, int $loc): array
$res = $this->tokenGroup();
foreach ($this->exprs as $expr) {
[$loc, $toks] = $expr->parsePart($str, $loc);
if ((null !== $toks) && (!is_array($toks) || (count($toks) > 0))) {
if (null !== $toks) {
$res->append($toks);
}
}
Expand Down
12 changes: 8 additions & 4 deletions lib/MC/Parser/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,29 @@ class Token
/** @var null|string */
public $name;

/** @var mixed */
/** @var null|string */
public $value;

/**
* Token constructor.
*
* @param mixed $value
*/
public function __construct($value, ?string $name)
public function __construct(?string $value, ?string $name)
{
$this->value = $value;
$this->name = $name;
}

/**
* @return array<null|string>
*/
public function getValues(): array
{
return [$this->value];
}

/**
* @return array<array{null|string, null|string}>
*/
public function getNameValues(): array
{
return [[$this->name, $this->value]];
Expand Down
11 changes: 7 additions & 4 deletions lib/MC/Parser/Token/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ class Group extends Token implements Countable
/** @var Token[] */
public $subtoks = [];

/**
* @param null|string $name
*/
public function __construct($name)
public function __construct(?string $name)
{
parent::__construct(null, $name);
}
Expand All @@ -43,6 +40,9 @@ public function count(): int
return count($this->subtoks);
}

/**
* @return array<null|string>
*/
public function getValues(): array
{
$values = [];
Expand All @@ -53,6 +53,9 @@ public function getValues(): array
return $values;
}

/**
* @return array<array{null|string, null|string}>
*/
public function getNameValues(): array
{
$values = [];
Expand Down
Loading

0 comments on commit 750de6d

Please sign in to comment.