Skip to content

Commit

Permalink
Install phpstan via composer (GoogleForCreators#4540)
Browse files Browse the repository at this point in the history
  • Loading branch information
swissspidy committed Sep 28, 2020
1 parent bad3f1d commit e895876
Show file tree
Hide file tree
Showing 10 changed files with 373 additions and 13 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/continuous-integration-lint-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,13 @@ jobs:
- name: Detect coding standard violations (PHPCS)
run: vendor/bin/phpcs -q --report=checkstyle --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 | cs2pr --graceful-warnings

- name: Static Analysis (PHPStan)
run: vendor/bin/phpstan analyse --memory-limit=512M --error-format=checkstyle | cs2pr
if: always()

- name: Normalize composer.json
# composer-normalize requires PHP 7.1+. Ignore version conflicts.
run: |
composer require --no-interaction --dev ergebnis/composer-normalize --ignore-platform-reqs
composer --no-interaction normalize --dry-run
- name: Static Analysis (PHPStan)
# phpstan requires PHP 7.1+. Ignore version conflicts.
run: |
composer require --no-interaction --dev szepeviktor/phpstan-wordpress --ignore-platform-reqs
vendor/bin/phpstan analyse --memory-limit=512M --error-format=checkstyle | cs2pr
if: always()
20 changes: 18 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
},
"require-dev": {
"automattic/vipwpcs": "^2.1",
"civicrm/composer-downloads-plugin": "^2.1",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"php-stubs/wordpress-stubs": "^5.5",
"phpcompatibility/phpcompatibility-wp": "^2.1",
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5",
"roave/security-advisories": "dev-master",
Expand All @@ -26,15 +28,29 @@
},
"sort-packages": true
},
"extra": {
"downloads": {
"phpstan": {
"url": "https://github.com/phpstan/phpstan/releases/latest/download/phpstan.phar",
"path": "vendor/bin/phpstan",
"type": "phar"
}
}
},
"autoload": {
"psr-4": {
"Google\\Web_Stories\\": "includes"
}
},
"autoload-dev": {
"psr-4": {
"Google\\Web_Stories\\Tests\\": "tests/phpunit/includes"
"Google\\Web_Stories\\Tests\\": "tests/phpunit/includes",
"PHPStan\\WordPress\\": "tests/phpstan/src"
}
},
"prefer-stable": true
"prefer-stable": true,
"scripts": {
"phpcs": "phpcs",
"phpstan": "phpstan analyse --memory-limit=512M"
}
}
126 changes: 125 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,9 @@
"lint:js": "eslint --ext .js,.cjs,.md .",
"lint:js:fix": "npm run lint:js -- --fix",
"lint:js:report": "eslint --ext .js,.cjs,.md --output-file build/lint-js-report.json --format json .",
"lint:php": "vendor/bin/phpcs",
"lint:php": "composer phpcs",
"lint:php:fix": "vendor/bin/phpcbf",
"lint:phpstan": "composer phpstan",
"lint:md": "markdownlint .",
"postinstall": "npx patch-package",
"storybook": "start-storybook --quiet",
Expand Down
2 changes: 1 addition & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@
<exclude-pattern>*/node_modules/*</exclude-pattern>
<exclude-pattern>*/vendor/*</exclude-pattern>
<exclude-pattern>assets/js/*.asset.php</exclude-pattern>
<exclude-pattern>tests/phpstan/bootstrap.php</exclude-pattern>
<exclude-pattern>tests/phpstan/*</exclude-pattern>
</ruleset>
18 changes: 16 additions & 2 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
includes:
- vendor/szepeviktor/phpstan-wordpress/extension.neon
services:
-
class: PHPStan\WordPress\IsWpErrorFunctionTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
-
class: PHPStan\WordPress\GetPostDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: PHPStan\WordPress\GetPostsDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

parameters:
level: 8
inferPrivatePropertyTypeFromConstructor: true
checkMissingIterableValueType: false
paths:
- includes/
bootstrapFiles:
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
- tests/phpstan/stubs/wordpress-defines.php
- tests/phpstan/bootstrap.php
- includes/namespace.php
dynamicConstantNames:
Expand Down
48 changes: 48 additions & 0 deletions tests/phpstan/src/GetPostDynamicFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* Copied from szepeviktor/phpstan-wordpress
*/

/**
* Set return type of get_post().
*/

namespace PHPStan\WordPress;

use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Type;
use PHPStan\Type\ArrayType;
use PHPStan\Type\StringType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\NullType;
use PHPStan\Type\TypeCombinator;

class GetPostDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
{
public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return in_array($functionReflection->getName(), ['get_post', 'get_page_by_path'], true);
}

// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
{
$output = 'OBJECT';
if (count($functionCall->args) >= 2 && $functionCall->args[1]->value instanceof ConstFetch) {
$output = $functionCall->args[1]->value->name->getLast();
}
if ($output === 'ARRAY_A') {
return TypeCombinator::union(new ArrayType(new StringType(), new MixedType()), new NullType());
}
if ($output === 'ARRAY_N') {
return TypeCombinator::union(new ArrayType(new IntegerType(), new MixedType()), new NullType());
}

return TypeCombinator::union(new ObjectType('WP_Post'), new NullType());
}
}
81 changes: 81 additions & 0 deletions tests/phpstan/src/GetPostsDynamicFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php
/**
* Copied from szepeviktor/phpstan-wordpress
*/

/**
* Set return type of get_post().
*/

namespace PHPStan\WordPress;

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Type;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantStringType;

class GetPostsDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
{
public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return in_array($functionReflection->getName(), ['get_posts'], true);
}

/**
* @see https://developer.wordpress.org/reference/classes/wp_query/#return-fields-parameter
*/
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
{
// Called without arguments
if (count($functionCall->args) === 0) {
return new ArrayType(new IntegerType(), new ObjectType('WP_Post'));
}

$argumentType = $scope->getType($functionCall->args[0]->value);

// Called with an array argument
if ($argumentType instanceof ConstantArrayType) {
foreach ($argumentType->getKeyTypes() as $index => $key) {
if (! $key instanceof ConstantStringType || $key->getValue() !== 'fields') {
continue;
}

$fieldsType = $argumentType->getValueTypes()[$index];
if ($fieldsType instanceof ConstantStringType) {
$fields = $fieldsType->getValue();
}
break;
}
}
// Called with a string argument
if ($argumentType instanceof ConstantStringType) {
parse_str($argumentType->getValue(), $variables);
$fields = $variables['fields'] ?? 'all';
}

// Without constant argument return default return type
if (! isset($fields)) {
return ParametersAcceptorSelector::selectFromArgs(
$scope,
$functionCall->args,
$functionReflection->getVariants()
)->getReturnType();
}

switch ($fields) {
case 'ids':
return new ArrayType(new IntegerType(), new IntegerType());
case 'id=>parent':
return new ArrayType(new IntegerType(), new ObjectType('stdClass'));
case 'all':
default:
return new ArrayType(new IntegerType(), new ObjectType('WP_Post'));
}
}
}
Loading

0 comments on commit e895876

Please sign in to comment.