diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8228838..62ccadd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,15 +6,15 @@ on: jobs: tests: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: - php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] name: PHP ${{ matrix.php }} steps: - - 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 + - uses: actions/checkout@v4 + with: + fetch-depth: 5 # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 with: @@ -25,10 +25,11 @@ jobs: - run: composer phpunit tests_lowest_dependencies: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 - - run: git fetch --depth=100000 origin + - uses: actions/checkout@v4 + with: + fetch-depth: 5 # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 with: diff --git a/composer.json b/composer.json index 49d9d08..6124951 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.0", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" diff --git a/lib/MC/Google/Visualization.php b/lib/MC/Google/Visualization.php index fa269d7..9e42828 100644 --- a/lib/MC/Google/Visualization.php +++ b/lib/MC/Google/Visualization.php @@ -14,13 +14,14 @@ use MC\Parser\Token; use PDO; use PDOException; +use Tests\VisualizationTest; /** * Provide a working implementation of the Google Visualization Query data source that works with a database * (or any other custom backend). The documentation for the query language itself and how to use it with Google * Visualizations can be found here: http://code.google.com/apis/visualization/documentation/querylanguage.html. * - * @see \Tests\VisualizationTest + * @see VisualizationTest * * @phpstan-type FieldSpec array{ * type: string, @@ -106,10 +107,8 @@ class Visualization * Create a new instance. This must be done before the library can be used. Pass in a PDO connection and * dialect if MC_Google_Visualization will handle the entire request cycle. * - * @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 + * @param null|PDO $db the database connection to use + * @param 'mysql'|'postgres'|'sqlite' $dialect the SQL dialect to use * * @throws Visualization_Error */ @@ -140,9 +139,7 @@ public function setDB(?PDO $db = null): void /** * Set the dialect to use when generating SQL statements. * - * @param string $dialect one of "mysql", "postgres", or "sqlite" - * - * @phpstan-param 'mysql'|'postgres'|'sqlite' $dialect + * @param 'mysql'|'postgres'|'sqlite' $dialect the SQL dialect to use * * @throws Visualization_Error */ @@ -158,11 +155,8 @@ 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" - * - * @phpstan-param 'date'|'datetime'|'time'|'boolean'|'number' $type - * - * @param string $format the format string to use for the data type + * @param string $format the format string to use for the data type + * @param 'boolean'|'date'|'datetime'|'number'|'time' $type the data type to change * * @throws Visualization_Error */ @@ -582,7 +576,7 @@ public function getRowValues(array $row, array $meta): string $time = strtotime($year.'0104 +'.$week.' weeks'); assert(false !== $time); $monday = strtotime('-'.((int) date('w', $time) - 1).' days', $time); - assert(false !== $monday); // @phpstan-ignore-line ; PHP < 8.0 + assert(false !== $monday); [$year, $month, $day] = explode('-', date('Y-m-d', $monday)); $formatted = date($format, $monday); } else { @@ -737,7 +731,7 @@ public function getGrammar(): Def * * @throws ParseError * @throws Visualization_QueryError - * @throws Parser\DefError + * @throws DefError */ public function parseQuery(string $str): array { @@ -887,7 +881,7 @@ public function parseQuery(string $str): array * * @return string the string to output that will cause the visualization client to detect an error */ - protected function handleError(int $reqid, string $detailMsg, string $handler = 'google.visualization.Query.setResponse', string $code = 'error', string $summaryMsg = null): string + protected function handleError(int $reqid, string $detailMsg, string $handler = 'google.visualization.Query.setResponse', string $code = 'error', ?string $summaryMsg = null): string { if (null === $summaryMsg) { $summaryMsg = $detailMsg; @@ -1013,7 +1007,7 @@ protected function generateSQL(array &$meta): string $stmt = $this->db->query($pivotSql); assert(false !== $stmt); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); - assert(false !== $rows); // @phpstan-ignore-line ; PHP < 8.0 + assert(false !== $rows); foreach ($rows as $row) { // Create a version of all function-ed fields for each unique combination of pivot values foreach ($funcFields as $field) { @@ -1292,7 +1286,7 @@ protected function getFieldQuote(): string * * @return string the SQL string for this field, with an op */ - protected function getFieldSQL(string $name, array $spec, bool $alias = false, string $func = null, array $pivot = null, array $pivotFields = null): string + protected function getFieldSQL(string $name, array $spec, bool $alias = false, ?string $func = null, ?array $pivot = null, ?array $pivotFields = null): string { $sql = $spec['field']; $q = $this->getFieldQuote(); @@ -1360,7 +1354,7 @@ protected function addDependantCallbackFields(array $fieldSpec, array $entity, a * @param Token $token the token or token group to recursively parse * @param null|array $fields the collector array reference to receive the flattened select field values */ - protected function parseFieldTokens(Token $token, array &$fields = null): void + protected function parseFieldTokens(Token $token, ?array &$fields = null): void { if ('*' === $token->value) { return; @@ -1392,7 +1386,7 @@ protected function parseFieldTokens(Token $token, array &$fields = null): void * @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 */ - protected function parseWhereTokens(Token $token, array &$where = null): void + protected function parseWhereTokens(Token $token, ?array &$where = null): void { if (!is_array($where)) { $where = []; diff --git a/lib/MC/Parser.php b/lib/MC/Parser.php index 291ad9b..d862c04 100644 --- a/lib/MC/Parser.php +++ b/lib/MC/Parser.php @@ -14,11 +14,12 @@ use MC\Parser\Def\Set; use MC\Parser\Def\Word; use MC\Parser\DefError; +use Tests\ParserTest; /** * Parser-generator class with an easy PHP-based API, similar to the pyparsing module in philosophy. * - * @see \Tests\ParserTest + * @see ParserTest */ class Parser { @@ -48,7 +49,7 @@ public function oneOf(Def ...$args): OneOf /** * Return a Word that matches a set of possible characters not separated by whitespace. */ - public function word(string $firstChars, string $restChars = null): Word + public function word(string $firstChars, ?string $restChars = null): Word { return new Word($firstChars, $restChars); } diff --git a/lib/MC/Parser/Def.php b/lib/MC/Parser/Def.php index fe94492..c1b9bf0 100644 --- a/lib/MC/Parser/Def.php +++ b/lib/MC/Parser/Def.php @@ -46,11 +46,7 @@ public function parsePart(string $str, int $loc): array { [$loc, $tok] = $this->_parse($str, $loc); - $char = @$str[$loc++]; - while (('' !== $char) && Parser::isWhitespace($char)) { - $char = @$str[$loc++]; - } - --$loc; + for (; ($loc < strlen($str)) && Parser::isWhitespace($str[$loc]); ++$loc); return [$loc, $tok]; } diff --git a/lib/MC/Parser/Def/Regex.php b/lib/MC/Parser/Def/Regex.php index 8a66e4e..c592eaa 100644 --- a/lib/MC/Parser/Def/Regex.php +++ b/lib/MC/Parser/Def/Regex.php @@ -29,7 +29,7 @@ class Regex extends Def /** @var int */ public $retgroup = 0; - public function __construct(string $regex = null, string $flags = null, string $errstr = null) + public function __construct(?string $regex = null, ?string $flags = null, ?string $errstr = null) { if (null !== $regex) { $this->regex = $regex; diff --git a/lib/MC/Parser/Def/Word.php b/lib/MC/Parser/Def/Word.php index e8c04ac..6eadbe9 100644 --- a/lib/MC/Parser/Def/Word.php +++ b/lib/MC/Parser/Def/Word.php @@ -13,7 +13,7 @@ class Word extends Regex * @param string $firstChars the characters allowed as the first character in the word * @param null|string $restChars the characters allowed as the rest of the word - defaults to same as $first_chars */ - public function __construct(string $firstChars, string $restChars = null) + public function __construct(string $firstChars, ?string $restChars = null) { parent::__construct(); diff --git a/lib/MC/Parser/DefError.php b/lib/MC/Parser/DefError.php index 7e2d5ed..8eb55b6 100644 --- a/lib/MC/Parser/DefError.php +++ b/lib/MC/Parser/DefError.php @@ -4,6 +4,4 @@ namespace MC\Parser; -class DefError extends Error -{ -} +class DefError extends Error {} diff --git a/lib/MC/Parser/Error.php b/lib/MC/Parser/Error.php index 84588e9..80f47fb 100644 --- a/lib/MC/Parser/Error.php +++ b/lib/MC/Parser/Error.php @@ -6,6 +6,4 @@ use Exception; -class Error extends Exception -{ -} +class Error extends Exception {} diff --git a/phpstan.neon b/phpstan.neon index 9f283d1..7550d46 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,5 +4,5 @@ includes: - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: checkMissingIterableValueType: false - reportUnmatchedIgnoredErrors: false + reportUnmatchedIgnoredErrors: true treatPhpDocTypesAsCertain: false \ No newline at end of file diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php index d787d34..c59c1c1 100644 --- a/tests/ExampleTest.php +++ b/tests/ExampleTest.php @@ -141,6 +141,10 @@ public function testQueryJoins(): void $output = $vis->handleRequest(false, $parameters); + // hack, different PHP version have different float rounding + $output = preg_replace('/69\.22149730656[0-9]+/', '69.22149730656', $output); + assert(is_string($output)); + // file_put_contents(__DIR__.'/result3.js', $output); self::assertStringEqualsFile(__DIR__.'/result3.js', $output); } diff --git a/tests/result3.js b/tests/result3.js index 86c87b6..5a0d0b8 100644 --- a/tests/result3.js +++ b/tests/result3.js @@ -1 +1 @@ -google.visualization.Query.setResponse({version:'0.5',reqId:'3',status:'ok',table:{cols: [{id:'max-life_male',label:"Life Expectancy (Male)",type:'number'},{id:'avg-life_female',label:"Life Expectancy (Female)",type:'number'},{id:'min-life_both',label:"Life Expectancy (Combined)",type:'number'}],rows: [{c:[{v:82.15,f:"82.15 years"},{v:69.2214973065625,f:"69.22 years"},{v:0,f:"0.00 years"}]}]}}); \ No newline at end of file +google.visualization.Query.setResponse({version:'0.5',reqId:'3',status:'ok',table:{cols: [{id:'max-life_male',label:"Life Expectancy (Male)",type:'number'},{id:'avg-life_female',label:"Life Expectancy (Female)",type:'number'},{id:'min-life_both',label:"Life Expectancy (Combined)",type:'number'}],rows: [{c:[{v:82.15,f:"82.15 years"},{v:69.22149730656,f:"69.22 years"},{v:0,f:"0.00 years"}]}]}}); \ No newline at end of file