diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4dababf..589085b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,20 +1,18 @@ name: CI on: - push: - branches: [ master, dev ] - pull_request: - branches: [ master, dev ] + - push + - pull_request jobs: tests: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] - name: PHP ${{ matrix.php }} tests + name: PHP ${{ matrix.php }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # required for "git tag" presence for MonorepoBuilder split and ChangelogLinker git tags resolver; default is 1 - run: git fetch --depth=100000 origin # see https://github.com/shivammathur/setup-php @@ -27,9 +25,9 @@ jobs: - run: composer phpunit tests_lowest_dependencies: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: git fetch --depth=100000 origin # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index f585f00..ffd0d20 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -8,12 +8,7 @@ ->in(__DIR__) ; - -$finder = PhpCsFixer\Finder::create() - ->exclude('vendor') - ->in(__DIR__); - -$config = new PhpCsFixer\Config(); +$config = new Config(); return $config->setRules([ '@PhpCsFixer' => true, '@PhpCsFixer:risky' => true, diff --git a/composer.json b/composer.json index 559f7a6..53bf31e 100644 --- a/composer.json +++ b/composer.json @@ -47,9 +47,9 @@ } }, "scripts": { - "phpstan": "phpstan analyse -l max lib tests", - "php-cs-fixer": "php-cs-fixer fix --allow-risky=yes", - "php-cs-fixer-dry-run": "php-cs-fixer fix --dry-run --allow-risky=yes", - "phpunit": "phpunit" + "phpstan": "@php phpstan analyse -l max lib tests examples .php-cs-fixer.php", + "php-cs-fixer": "@php php-cs-fixer fix --allow-risky=yes", + "php-cs-fixer-dry-run": "@php php-cs-fixer fix --dry-run --allow-risky=yes", + "phpunit": "@php phpunit" } } diff --git a/examples/callback_fields.php b/examples/callback_fields.php index a1a2d1b..05aea08 100644 --- a/examples/callback_fields.php +++ b/examples/callback_fields.php @@ -8,10 +8,7 @@ require_once __DIR__.'/../vendor/autoload.php'; -/** - * @param mixed $row - */ -function most_common($row): ?string +function most_common(array $row): ?string { $forms = ['pill', 'iud', 'condom', 'sterile_total', 'other_modern', 'traditional']; $maxForm = -1; @@ -40,7 +37,7 @@ function most_common($row): ?string 'condom' => ['field' => 'condom', 'type' => 'number'], 'sterile_total' => ['field' => 'steril_total', 'type' => 'number'], 'other_modern' => ['field' => 'other_modern', 'type' => 'number'], - 'traditional' => ['field' => 'traditional', 'type' => 'number'], + 'traditional' => ['field' => 'traditional', 'type' => 'number'], 'most_common' => [ 'callback' => 'most_common', 'fields' => ['pill', 'iud', 'condom', 'sterile_total', 'other_modern', 'traditional'], @@ -51,7 +48,7 @@ function most_common($row): ?string ]); $vis->handleRequest(); - exit(); + exit; } ?> diff --git a/examples/complete.php b/examples/complete.php index ed12d92..a30b282 100644 --- a/examples/complete.php +++ b/examples/complete.php @@ -34,7 +34,7 @@ $vis->setDefaultEntity('timeline'); $vis->handleRequest(); - exit(); + exit; } ?> diff --git a/examples/joins.php b/examples/joins.php index 3d685c9..ab67840 100644 --- a/examples/joins.php +++ b/examples/joins.php @@ -31,7 +31,7 @@ ]); $vis->handleRequest(); - exit(); + exit; } ?> diff --git a/examples/simple.php b/examples/simple.php index b3f7d70..729e403 100644 --- a/examples/simple.php +++ b/examples/simple.php @@ -21,7 +21,7 @@ ]); $vis->handleRequest(); - exit(); + exit; } ?> diff --git a/lib/MC/Google/Visualization.php b/lib/MC/Google/Visualization.php index 7ca010d..02a139e 100644 --- a/lib/MC/Google/Visualization.php +++ b/lib/MC/Google/Visualization.php @@ -21,6 +21,8 @@ * Visualizations can be found here: http://code.google.com/apis/visualization/documentation/querylanguage.html. * * @see \Tests\VisualizationTest + * + * @phpstan-type FieldSpec array{callback?:callable,field?:string,extra?:array,fields?:string[],sort_field?:string,type?:string,join?:string} */ class Visualization { @@ -82,9 +84,11 @@ class Visualization * @param null|PDO $db the database connection to use * @param string $dialect the SQL dialect to use - one of "mysql", "postgres", or "sqlite" * + * @phpstan-param 'mysql'|'postgres'|'sqlite' $dialect + * * @throws Visualization_Error */ - public function __construct(PDO $db = null, string $dialect = 'mysql') + public function __construct(?PDO $db = null, string $dialect = 'mysql') { if (!function_exists('json_encode')) { throw new Visualization_Error('You must include the PHP json extension installed to use the MC Google Visualization Server'); @@ -99,7 +103,7 @@ public function __construct(PDO $db = null, string $dialect = 'mysql') * * @param null|PDO $db the database connection to use - or null if you want to handle your own queries */ - public function setDB(PDO $db = null): void + public function setDB(?PDO $db = null): void { if (null !== $db) { $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -113,6 +117,8 @@ public function setDB(PDO $db = null): void * * @param string $dialect one of "mysql", "postgres", or "sqlite" * + * @phpstan-param 'mysql'|'postgres'|'sqlite' $dialect + * * @throws Visualization_Error */ public function setSqlDialect(string $dialect): void @@ -127,7 +133,10 @@ public function setSqlDialect(string $dialect): void /** * Change the default format string to use for a particular data type. * - * @param string $type the data type to change - one of "date", "datetime", "time", "boolean", or "number" + * @param string $type the data type to change - one of "date", "datetime", "time", "boolean", or "number" + * + * @phpstan-param 'date'|'datetime'|'time'|'boolean'|'number' $type + * * @param string $format the format string to use for the data type * * @throws Visualization_Error @@ -150,11 +159,11 @@ public function setDefaultFormat(string $type, string $format): void * @param bool $echo print response and set header * @param null|array $queryParams query parameters * - * @throws Visualization_Error - * * @return string the javascript response + * + * @throws Visualization_Error */ - public function handleRequest(bool $echo = true, array $queryParams = null): string + public function handleRequest(bool $echo = true, ?array $queryParams = null): string { if (null === $queryParams) { $queryParams = $_GET; @@ -201,7 +210,7 @@ public function handleQuery(string $query, array $params): string $response = ''; try { - if (!($this->db instanceof PDO)) { + 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'); } @@ -244,10 +253,10 @@ public function handleQuery(string $query, array $params): string * * @param array $query the visualization query broken up into sections * + * @return array the metadata array from merging the query with the entity table definitions + * * @throws Visualization_QueryError * @throws Visualization_Error - * - * @return array the metadata array from merging the query with the entity table definitions */ public function generateMetadata(array $query): array { @@ -410,6 +419,8 @@ public function generateMetadata(array $query): array * @param string $name the name of the entity - should be used in the "from" clause of visualization queries * @param array $spec optional spec array with keys "fields", "joins", "table", and "where" to define the mapping between visualization queries and SQL queries * + * @phpstan-param array{table?: string, fields?: array, joins?: array, where?: string} $spec + * * @throws Visualization_Error */ public function addEntity(string $name, array $spec = []): void @@ -441,7 +452,7 @@ public function addEntity(string $name, array $spec = []): void * * @throws Visualization_Error */ - public function setDefaultEntity(string $default = null): void + public function setDefaultEntity(?string $default = null): void { if (null !== $default && !isset($this->entities[$default])) { throw new Visualization_Error('No entity exists with name "'.$default.'"'); @@ -456,9 +467,9 @@ public function setDefaultEntity(string $default = null): void * @param array $row the row values as an array * @param array $meta the metadata for the query (use generateMetadata()) * - * @throws Visualization_Error - * * @return string the string fragment to include in the results back to the javascript client + * + * @throws Visualization_Error */ public function getRowValues(array $row, array $meta): string { @@ -510,8 +521,8 @@ public function getRowValues(array $row, array $meta): string $val = (float) $val; if (1 === preg_match('#^num:(\d+)(.*)$#i', $format, $matches)) { $digits = (int) $matches[1]; - $extras = $matches[2]; - if (is_array($extras) && (2 === count($extras))) { + $extras = str_split($matches[2]); + if (2 === count($extras)) { $formatted = number_format($val, $digits, $extras[0], $extras[1]); } else { $formatted = number_format($val, $digits); @@ -605,12 +616,12 @@ public function getRowValues(array $row, array $meta): string * * @param string $query the visualization query to run * + * @return string the SQL that should be sent to the database + * * @throws Visualization_QueryError * @throws Visualization_Error * @throws ParseError * @throws DefError - * - * @return string the SQL that should be sent to the database */ public function getSQL(string $query): string { @@ -623,9 +634,9 @@ public function getSQL(string $query): string /** * Use MC_Parser to generate a grammar that matches the query language specified here: http://code.google.com/apis/visualization/documentation/querylanguage.html. * - * @throws DefError - * * @return Def the grammar for the query language + * + * @throws DefError */ public function getGrammar(): Def { @@ -693,11 +704,11 @@ 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 + * * @throws ParseError * @throws Visualization_QueryError * @throws Parser\DefError - * - * @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 */ public function parseQuery(string $str): array { @@ -857,6 +868,8 @@ protected function handleError(int $reqid, string $detailMsg, string $handler = * @param string $field the name of the field * @param array $spec the metadata for the field as a set of key-value pairs - allowed keys are "field", "callback", "fields", "extra", "sort_field", "type", and "join" * + * @phpstan-param FieldSpec $spec + * * @throws Visualization_Error */ protected function addEntityField(string $entity, string $field, array $spec): void @@ -916,9 +929,9 @@ protected function setEntityWhere(string $entity, string $where): void * * @param array $meta the results of generateMetadata() on the parsed visualization query * - * @throws Visualization_QueryError - * * @return string the SQL version of the visualization query + * + * @throws Visualization_QueryError */ protected function generateSQL(array &$meta): string { @@ -1083,9 +1096,9 @@ protected function generateSQL(array &$meta): string * * @param array $meta the metadata for the query - generally generated by MC_Google_Visualization::generateMetadata * - * @throws Visualization_Error - * * @return string the initial output string for a successful query + * + * @throws Visualization_Error */ protected function getSuccessInit(array $meta): string { diff --git a/lib/MC/Parser/Def.php b/lib/MC/Parser/Def.php index c7dfa45..cd9f1b4 100644 --- a/lib/MC/Parser/Def.php +++ b/lib/MC/Parser/Def.php @@ -38,9 +38,9 @@ public function parse(string $str): Token /** * Parse a string, cleaning up whitespace when we're done. * - * @throws ParseError - * * @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions + * + * @throws ParseError */ public function parsePart(string $str, int $loc): array { @@ -61,9 +61,9 @@ public function parsePart(string $str, int $loc): array * @param string $str the string to parse * @param int $loc the index to start parsing * - * @throws ParseError - * * @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions + * + * @throws ParseError */ abstract public function _parse(string $str, int $loc): array; diff --git a/phpstan.neon b/phpstan.neon index 1d162c6..9f283d1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,4 +4,5 @@ includes: - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: checkMissingIterableValueType: false - reportUnmatchedIgnoredErrors: false \ No newline at end of file + reportUnmatchedIgnoredErrors: false + treatPhpDocTypesAsCertain: false \ No newline at end of file