Skip to content

Commit d843f71

Browse files
authored
implement type inference on rex_request::* (#193)
1 parent c072959 commit d843f71

6 files changed

Lines changed: 89 additions & 12 deletions

File tree

default-config.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ services:
6262
class: rexstan\RexFunctionsDynamicReturnTypeExtension
6363
tags:
6464
- phpstan.broker.dynamicFunctionReturnTypeExtension
65+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension

lib/extension/RexFunctionsDynamicReturnTypeExtension.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,53 @@
44

55
namespace rexstan;
66

7+
use PhpParser\Node\Arg;
78
use PhpParser\Node\Expr\FuncCall;
9+
use PhpParser\Node\Expr\StaticCall;
810
use PHPStan\Analyser\Scope;
911
use PHPStan\Reflection\FunctionReflection;
12+
use PHPStan\Reflection\MethodReflection;
1013
use PHPStan\ShouldNotHappenException;
1114
use PHPStan\Type\ArrayType;
1215
use PHPStan\Type\BooleanType;
1316
use PHPStan\Type\Constant\ConstantStringType;
1417
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
18+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
1519
use PHPStan\Type\FloatType;
1620
use PHPStan\Type\IntegerType;
1721
use PHPStan\Type\MixedType;
1822
use PHPStan\Type\ObjectWithoutClassType;
1923
use PHPStan\Type\StringType;
2024
use PHPStan\Type\Type;
2125
use PHPStan\Type\TypeCombinator;
26+
use rex_request;
2227
use function count;
2328
use function in_array;
2429

25-
final class RexFunctionsDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
30+
final class RexFunctionsDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension, DynamicStaticMethodReturnTypeExtension
2631
{
32+
public function getClass(): string
33+
{
34+
return rex_request::class;
35+
}
36+
37+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
38+
{
39+
return in_array(
40+
strtolower($methodReflection->getName()),
41+
['get', 'post', 'request', 'server', 'session', 'cookie', 'files', 'env'],
42+
true
43+
);
44+
}
45+
46+
public function getTypeFromStaticMethodCall(
47+
MethodReflection $methodReflection,
48+
StaticCall $methodCall,
49+
Scope $scope
50+
): ?Type {
51+
return $this->getType($methodCall->getArgs(), $scope);
52+
}
53+
2754
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2855
{
2956
return in_array($functionReflection->getName(), ['rex_get', 'rex_post', 'rex_request', 'rex_server', 'rex_session', 'rex_cookie', 'rex_files', 'rex_env'], true);
@@ -34,8 +61,14 @@ public function getTypeFromFunctionCall(
3461
FuncCall $functionCall,
3562
Scope $scope
3663
): ?Type {
37-
$args = $functionCall->getArgs();
64+
return $this->getType($functionCall->getArgs(), $scope);
65+
}
3866

67+
/**
68+
* @param Arg[] $args
69+
*/
70+
private function getType(array $args, Scope $scope): ?Type
71+
{
3972
if (count($args) < 2) {
4073
return null;
4174
}

tests/data/rex-sapi.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace RexSapi;
4+
5+
use rex_request;
6+
use function PHPStan\Testing\assertType;
7+
8+
function doFoo()
9+
{
10+
assertType('string', rex_request::get('HTTP_REFERER', 'string', ''));
11+
assertType('string', rex_request::post('HTTP_REFERER', 'string', ''));
12+
assertType('string', rex_request::env('HTTP_REFERER', 'string', ''));
13+
assertType('string', rex_request::request('HTTP_REFERER', 'string', ''));
14+
assertType('string', rex_request::server('HTTP_REFERER', 'string', ''));
15+
assertType('string', rex_request::session('HTTP_REFERER', 'string', ''));
16+
assertType('string', rex_request::files('HTTP_REFERER', 'string', ''));
17+
}
18+
19+
function doBar()
20+
{
21+
assertType('string', rex_get('HTTP_REFERER', 'string', ''));
22+
assertType('string', rex_post('HTTP_REFERER', 'string', ''));
23+
assertType('string', rex_env('HTTP_REFERER', 'string', ''));
24+
assertType('string', rex_request('HTTP_REFERER', 'string', ''));
25+
assertType('string', rex_server('HTTP_REFERER', 'string', ''));
26+
assertType('string', rex_session('HTTP_REFERER', 'string', ''));
27+
assertType('string', rex_files('HTTP_REFERER', 'string', ''));
28+
}
29+
30+
function doFooBar()
31+
{
32+
assertType('string', rex_get('HTTP_REFERER', 'string', ''));
33+
assertType("''|int", rex_get('HTTP_REFERER', 'int', ''));
34+
assertType("''|int", rex_get('HTTP_REFERER', 'integer', ''));
35+
assertType("''|float", rex_get('HTTP_REFERER', 'float', ''));
36+
assertType("''|float", rex_get('HTTP_REFERER', 'double', ''));
37+
assertType("''|float", rex_get('HTTP_REFERER', 'real', ''));
38+
assertType("''|bool", rex_get('HTTP_REFERER', 'bool', ''));
39+
assertType("''|bool", rex_get('HTTP_REFERER', 'boolean', ''));
40+
}

tests/data/superglobals.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
namespace SuperGlobals;
4+
35
static function ($value) {
46
$_SESSION['foo'] = 'bar';
57
$x = $_GET['foo'];

tests/expected.out

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
return (string) $post->id;
2929
}, $mixed).
3030
/tests/data/sql-injection.php:67:Possible SQL-injection in expression str_replace(['_', '%'], ['\\_', '\\%'], $prefix).
31-
/tests/data/superglobals.php:4:Using $_SESSION is forbidden, use rex_request::session(), rex_request::setSession() or rex_session(), rex_set_session() instead for proper per instance scoping.
32-
/tests/data/superglobals.php:5:Using $_GET is forbidden, use rex_request::get() or rex_get() instead.
33-
/tests/data/superglobals.php:6:Using $_POST is forbidden, use rex_request::post() or rex_post() instead.
34-
/tests/data/superglobals.php:7:Using $_REQUEST is forbidden, use rex_request::request() or rex_request() instead.
35-
/tests/data/superglobals.php:8:Using $_COOKIE is forbidden, use rex_request::cookie() or rex_cookie() instead.
36-
/tests/data/superglobals.php:9:Using $_SERVER is forbidden, use rex_request::server() or rex_server() instead.
37-
/tests/data/superglobals.php:10:Using $_ENV is forbidden, use rex_request::env() or rex_env() instead.
38-
/tests/data/superglobals.php:11:Using $_FILES is forbidden, use rex_request::files() or rex_files() instead.
39-
/tests/data/superglobals.php:13:Calling setcookie() is forbidden, use rex_response::sendCookie() or rex_response::clearCookie() instead.
31+
/tests/data/superglobals.php:6:Using $_SESSION is forbidden, use rex_request::session(), rex_request::setSession() or rex_session(), rex_set_session() instead for proper per instance scoping.
32+
/tests/data/superglobals.php:7:Using $_GET is forbidden, use rex_request::get() or rex_get() instead.
33+
/tests/data/superglobals.php:8:Using $_POST is forbidden, use rex_request::post() or rex_post() instead.
34+
/tests/data/superglobals.php:9:Using $_REQUEST is forbidden, use rex_request::request() or rex_request() instead.
35+
/tests/data/superglobals.php:10:Using $_COOKIE is forbidden, use rex_request::cookie() or rex_cookie() instead.
36+
/tests/data/superglobals.php:11:Using $_SERVER is forbidden, use rex_request::server() or rex_server() instead.
37+
/tests/data/superglobals.php:12:Using $_ENV is forbidden, use rex_request::env() or rex_env() instead.
38+
/tests/data/superglobals.php:13:Using $_FILES is forbidden, use rex_request::files() or rex_files() instead.
39+
/tests/data/superglobals.php:15:Calling setcookie() is forbidden, use rex_response::sendCookie() or rex_response::clearCookie() instead.

tests/run.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
$output = preg_replace('/\s*$/', '', $output);
99
$expected = trim((string) file_get_contents(__DIR__.'/expected.out'));
1010

11-
function relativePath(string $path):string {
11+
function relativePath(string $path): string
12+
{
1213
$projectRoot = realpath(__DIR__.'/../');
1314

1415
$projectRoot = str_replace('\\', '/', $projectRoot);

0 commit comments

Comments
 (0)