Skip to content

Commit 8e8d99e

Browse files
authored
Merge pull request #122 from Kharhamel/nullsyFunctions
Nullsy functions
2 parents c97371a + 6df6b5a commit 8e8d99e

File tree

12 files changed

+212
-22
lines changed

12 files changed

+212
-22
lines changed

generated/array.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,36 @@ function array_combine(array $keys, array $values): array
2828
}
2929

3030

31+
/**
32+
* array_flip returns an array in flip
33+
* order, i.e. keys from array become values and values
34+
* from array become keys.
35+
*
36+
* Note that the values of array need to be valid
37+
* keys, i.e. they need to be either integer or
38+
* string. A warning will be emitted if a value has the wrong
39+
* type, and the key/value pair in question will not be included
40+
* in the result.
41+
*
42+
* If a value has several occurrences, the latest key will be
43+
* used as its value, and all others will be lost.
44+
*
45+
* @param array $array An array of key/value pairs to be flipped.
46+
* @return array Returns the flipped array on success and NULL on failure.
47+
* @throws ArrayException
48+
*
49+
*/
50+
function array_flip(array $array): array
51+
{
52+
error_clear_last();
53+
$result = \array_flip($array);
54+
if ($result === null) {
55+
throw ArrayException::createFromPhpError();
56+
}
57+
return $result;
58+
}
59+
60+
3161
/**
3262
* array_multisort can be used to sort several
3363
* arrays at once, or a multi-dimensional array by one or more

generated/functionsList.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
'apcu_inc',
2727
'apcu_sma_info',
2828
'array_combine',
29+
'array_flip',
2930
'array_multisort',
3031
'array_walk_recursive',
3132
'arsort',
@@ -475,6 +476,7 @@
475476
'define',
476477
'highlight_file',
477478
'highlight_string',
479+
'sapi_windows_cp_conv',
478480
'sapi_windows_cp_set',
479481
'sapi_windows_vt100_support',
480482
'sleep',
@@ -900,6 +902,8 @@
900902
'simplexml_load_file',
901903
'simplexml_load_string',
902904
'socket_accept',
905+
'socket_addrinfo_bind',
906+
'socket_addrinfo_connect',
903907
'socket_bind',
904908
'socket_connect',
905909
'socket_create_listen',
@@ -909,6 +913,7 @@
909913
'socket_get_option',
910914
'socket_getpeername',
911915
'socket_getsockname',
916+
'socket_import_stream',
912917
'socket_listen',
913918
'socket_read',
914919
'socket_send',

generated/misc.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,30 @@ function highlight_string(string $str, bool $return = false)
9292
}
9393

9494

95+
/**
96+
* Convert string from one codepage to another.
97+
*
98+
* @param int|string $in_codepage The codepage of the subject string.
99+
* Either the codepage name or identifier.
100+
* @param int|string $out_codepage The codepage to convert the subject string to.
101+
* Either the codepage name or identifier.
102+
* @param string $subject The string to convert.
103+
* @return string The subject string converted to
104+
* out_codepage, or NULL on failure.
105+
* @throws MiscException
106+
*
107+
*/
108+
function sapi_windows_cp_conv($in_codepage, $out_codepage, string $subject): string
109+
{
110+
error_clear_last();
111+
$result = \sapi_windows_cp_conv($in_codepage, $out_codepage, $subject);
112+
if ($result === null) {
113+
throw MiscException::createFromPhpError();
114+
}
115+
return $result;
116+
}
117+
118+
95119
/**
96120
* Set the codepage of the current process.
97121
*

generated/sockets.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,46 @@ function socket_accept($socket)
4545
}
4646

4747

48+
/**
49+
* Create a Socket resource, and bind it to the provided AddrInfo resource. The return
50+
* value of this function may be used with socket_listen.
51+
*
52+
* @param resource $addr Resource created from socket_addrinfo_lookup().
53+
* @return resource|null Returns a Socket resource on success or NULL on failure.
54+
* @throws SocketsException
55+
*
56+
*/
57+
function socket_addrinfo_bind($addr)
58+
{
59+
error_clear_last();
60+
$result = \socket_addrinfo_bind($addr);
61+
if ($result === null) {
62+
throw SocketsException::createFromPhpError();
63+
}
64+
return $result;
65+
}
66+
67+
68+
/**
69+
* Create a Socket resource, and connect it to the provided AddrInfo resource. The return
70+
* value of this function may be used with the rest of the socket functions.
71+
*
72+
* @param resource $addr Resource created from socket_addrinfo_lookup()
73+
* @return resource|null Returns a Socket resource on success or NULL on failure.
74+
* @throws SocketsException
75+
*
76+
*/
77+
function socket_addrinfo_connect($addr)
78+
{
79+
error_clear_last();
80+
$result = \socket_addrinfo_connect($addr);
81+
if ($result === null) {
82+
throw SocketsException::createFromPhpError();
83+
}
84+
return $result;
85+
}
86+
87+
4888
/**
4989
* Binds the name given in address to the socket
5090
* described by socket. This has to be done before
@@ -336,6 +376,25 @@ function socket_getsockname($socket, ?string &$addr, ?int &$port = null): void
336376
}
337377

338378

379+
/**
380+
* Imports a stream that encapsulates a socket into a socket extension resource.
381+
*
382+
* @param resource $stream The stream resource to import.
383+
* @return resource Returns FALSE or NULL on failure.
384+
* @throws SocketsException
385+
*
386+
*/
387+
function socket_import_stream($stream)
388+
{
389+
error_clear_last();
390+
$result = \socket_import_stream($stream);
391+
if ($result === null) {
392+
throw SocketsException::createFromPhpError();
393+
}
394+
return $result;
395+
}
396+
397+
339398
/**
340399
* After the socket socket has been created
341400
* using socket_create and bound to a name with

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/Parameter.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,9 @@ private function getInnerXml(\SimpleXMLElement $SimpleXMLElement): string
157157
$inner_xml = trim($inner_xml);
158158
return $inner_xml;
159159
}
160+
161+
public function isTypeable(): bool
162+
{
163+
return $this->getType() !== 'mixed' && $this->getType() !== 'resource' && \count(\explode("|", $this->getType())) < 2;
164+
}
160165
}

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: 15 additions & 3 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
";
@@ -130,7 +142,7 @@ private function displayParamsWithType(array $params): string
130142

131143
foreach ($params as $param) {
132144
$paramAsString = '';
133-
if ($param->getType() !== 'mixed' && $param->getType() !== 'resource') {
145+
if ($param->isTypeable()) {
134146
if ($param->isNullable()) {
135147
$paramAsString .= '?';
136148
}

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
}

0 commit comments

Comments
 (0)