Skip to content

Commit d6bf1f3

Browse files
committed
added support for 'null on error' functions
1 parent 84fd1be commit d6bf1f3

File tree

6 files changed

+83
-21
lines changed

6 files changed

+83
-21
lines changed

generator/src/DocPage.php

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,39 @@ public function __construct(string $_path)
2121
$this->path = $_path;
2222
}
2323

24-
/*
25-
* Detect function which didn't return FALSE on error.
26-
*
27-
* @return bool
28-
*/
29-
public function detectFalsyFunction(): bool
24+
// Ignore function if it was removed before PHP 7.1
25+
private function getIsDeprecated(string $file): bool
3026
{
31-
$file = file_get_contents($this->path);
32-
3327
if (preg_match('/&warn\.deprecated\.function-(\d+-\d+-\d+)\.removed-(\d+-\d+-\d+)/', $file, $matches)) {
3428
$removedVersion = $matches[2];
3529
[$major, $minor] = explode('-', $removedVersion);
3630
if ($major < 7 || ($major == 7 && $minor == 0)) {
37-
// Ignore function if it was removed before PHP 7.1
38-
return false;
31+
return true;
3932
}
4033
}
34+
4135
if (preg_match('/&warn\.removed\.function-(\d+-\d+-\d+)/', $file, $matches) && isset($matches[2])) {
4236
$removedVersion = $matches[2];
4337
[$major, $minor] = explode('-', $removedVersion);
4438
if ($major < 7 || ($major == 7 && $minor == 0)) {
45-
// Ignore function if it was removed before PHP 7.1
46-
return false;
39+
return true;
4740
}
4841
}
4942

43+
return false;
44+
}
45+
46+
/*
47+
* Detect function which return FALSE on error.
48+
*/
49+
public function detectFalsyFunction(): bool
50+
{
51+
$file = file_get_contents($this->path);
52+
53+
if ($this->getIsDeprecated($file)) {
54+
return false;
55+
}
56+
5057
if (preg_match('/&false;\s+on\s+error/m', $file)) {
5158
return true;
5259
}
@@ -90,6 +97,24 @@ public function detectFalsyFunction(): bool
9097
return false;
9198
}
9299

100+
/*
101+
* Detect function which return NULL on error.
102+
*/
103+
public function detectNullsyFunction(): bool
104+
{
105+
$file = \file_get_contents($this->path);
106+
107+
if ($this->getIsDeprecated($file)) {
108+
return false;
109+
}
110+
111+
if (preg_match('/&null;\s+on\s+failure/', $file)) {
112+
return true;
113+
}
114+
115+
return false;
116+
}
117+
93118

94119
/**
95120
* @return \SimpleXMLElement[]

generator/src/Method.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
class Method
99
{
10+
const FALSY_TYPE = 1;
11+
const NULLSY_TYPE = 2;
1012
/**
1113
* @var \SimpleXMLElement
1214
*/
@@ -27,20 +29,30 @@ class Method
2729
* @var PhpStanFunctionMapReader
2830
*/
2931
private $phpStanFunctionMapReader;
32+
/**
33+
* @var int
34+
*/
35+
private $errorType;
3036

31-
public function __construct(\SimpleXMLElement $_functionObject, \SimpleXMLElement $rootEntity, string $moduleName, PhpStanFunctionMapReader $phpStanFunctionMapReader)
37+
public function __construct(\SimpleXMLElement $_functionObject, \SimpleXMLElement $rootEntity, string $moduleName, PhpStanFunctionMapReader $phpStanFunctionMapReader, int $errorType)
3238
{
3339
$this->functionObject = $_functionObject;
3440
$this->rootEntity = $rootEntity;
3541
$this->moduleName = $moduleName;
3642
$this->phpStanFunctionMapReader = $phpStanFunctionMapReader;
43+
$this->errorType = $errorType;
3744
}
3845

3946
public function getFunctionName(): string
4047
{
4148
return $this->functionObject->methodname->__toString();
4249
}
4350

51+
public function getErrorType(): int
52+
{
53+
return $this->errorType;
54+
}
55+
4456
public function getReturnType(): string
4557
{
4658
// If the function returns a boolean, since false is for error, true is for success.

generator/src/Scanner.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ public function getMethods(array $paths): array
9494
}
9595

9696
$docPage = new DocPage($path);
97-
if ($docPage->detectFalsyFunction()) {
97+
$isFalsy = $docPage->detectFalsyFunction();
98+
$isNullsy = $docPage->detectNullsyFunction();
99+
if ($isFalsy || $isNullsy) {
100+
$errorType = $isFalsy ? Method::FALSY_TYPE : Method::NULLSY_TYPE;
101+
98102
$functionObjects = $docPage->getMethodSynopsis();
99103
if (count($functionObjects) > 1) {
100104
$overloadedFunctions = array_merge($overloadedFunctions, \array_map(function ($functionObject) {
@@ -107,7 +111,7 @@ public function getMethods(array $paths): array
107111
}
108112
$rootEntity = $docPage->loadAndResolveFile();
109113
foreach ($functionObjects as $functionObject) {
110-
$function = new Method($functionObject, $rootEntity, $docPage->getModule(), $phpStanFunctionMapReader);
114+
$function = new Method($functionObject, $rootEntity, $docPage->getModule(), $phpStanFunctionMapReader, $errorType);
111115
if (isset($ignoredFunctions[$function->getFunctionName()])) {
112116
continue;
113117
}

generator/src/WritePhpFunction.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,24 @@ private function writePhpFunction(): string
9999

100100
private function generateExceptionCode(string $moduleName, Method $method) : string
101101
{
102+
$errorValue = null;
103+
switch ($method->getErrorType()) {
104+
case Method::FALSY_TYPE:
105+
$errorValue = 'false';
106+
break;
107+
case Method::NULLSY_TYPE:
108+
$errorValue = 'null';
109+
break;
110+
default:
111+
throw new \LogicException("Method doesn't have an error type");
112+
}
113+
102114
// Special case for CURL: we need the first argument of the method if this is a resource.
103115
if ($moduleName === 'Curl') {
104116
$params = $method->getParams();
105117
if (\count($params) > 0 && $params[0]->getParameter() === 'ch') {
106118
return "
107-
if (\$result === false) {
119+
if (\$result === $errorValue) {
108120
throw CurlException::createFromCurlResource(\$ch);
109121
}
110122
";
@@ -113,7 +125,7 @@ private function generateExceptionCode(string $moduleName, Method $method) : str
113125

114126
$exceptionName = FileCreator::toExceptionName($moduleName);
115127
return "
116-
if (\$result === false) {
128+
if (\$result === $errorValue) {
117129
throw {$exceptionName}::createFromPhpError();
118130
}
119131
";

generator/tests/DocPageTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,13 @@ public function testDetectFalsyFunction()
2626
$this->assertTrue($mcryptDecrypt->detectFalsyFunction());
2727
$this->assertTrue($fsockopen->detectFalsyFunction());
2828
}
29+
30+
public function testDetectNullsyFunction()
31+
{
32+
$pregMatch = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/array/functions/array-flip.xml');
33+
$implode = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/strings/functions/implode.xml');
34+
35+
$this->assertTrue($pregMatch->detectNullsyFunction());
36+
$this->assertFalse($implode->detectNullsyFunction());
37+
}
2938
}

generator/tests/MethodTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public function testGetFunctionName()
1111
{
1212
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
1313
$xmlObject = $docPage->getMethodSynopsis();
14-
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
14+
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader(), Method::FALSY_TYPE);
1515
$name = $method->getFunctionName();
1616
$this->assertEquals('preg_match', $name);
1717
}
@@ -20,7 +20,7 @@ public function testGetFunctionType()
2020
{
2121
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
2222
$xmlObject = $docPage->getMethodSynopsis();
23-
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
23+
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader(), Method::FALSY_TYPE);
2424
$type = $method->getReturnType();
2525
$this->assertEquals('int', $type);
2626
}
@@ -29,7 +29,7 @@ public function testGetFunctionParam()
2929
{
3030
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
3131
$xmlObject = $docPage->getMethodSynopsis();
32-
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
32+
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader(), Method::FALSY_TYPE);
3333
$params = $method->getParams();
3434
$this->assertEquals('string', $params[0]->getType());
3535
$this->assertEquals('pattern', $params[0]->getParameter());
@@ -39,7 +39,7 @@ public function testGetInitializer()
3939
{
4040
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/apc/functions/apc-cache-info.xml');
4141
$xmlObject = $docPage->getMethodSynopsis();
42-
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
42+
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader(), Method::FALSY_TYPE);
4343

4444
$params = $method->getParams();
4545
$this->assertEquals('', $params[0]->getDefaultValue());

0 commit comments

Comments
 (0)