change nullable types to union types in attributes

This commit is contained in:
Ivan Fedorov 2021-06-30 20:05:44 +03:00 committed by Ivan Fedorov
parent 1b352c474f
commit 835466f1d5
11 changed files with 84 additions and 72 deletions

View File

@ -15,7 +15,7 @@ use JetBrains\PhpStorm\Pure;
*/
class PDOException extends RuntimeException
{
#[LanguageLevelTypeAware(['8.1' => '?array'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'array|null'], default: '')]
public $errorInfo;
}

View File

@ -808,7 +808,7 @@ class Phar extends RecursiveDirectoryIterator implements RecursiveIterator, Seek
final public static function webPhar(
?string $alias = null,
?string $index = "index.php",
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $fileNotFoundScript = null,
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $fileNotFoundScript = null,
array $mimeTypes = null,
?callable $rewrite = null
) {}

View File

@ -1075,7 +1075,7 @@ class RegexIterator extends FilterIterator
public const INVERT_MATCH = 2;
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $replacement;
/**

View File

@ -23,7 +23,7 @@ class DOMNode
* The value of this node, depending on its type
* @link https://php.net/manual/en/class.domnode.php#domnode.props.nodevalue
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $nodeValue;
/**
@ -40,7 +40,7 @@ class DOMNode
* The parent of this node. If there is no such node, this returns NULL.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.parentnode
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMNode'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNode|null'], default: '')]
public $parentNode;
/**
@ -56,7 +56,7 @@ class DOMNode
* The first child of this node. If there is no such node, this returns NULL.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.firstchild
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMNode'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNode|null'], default: '')]
public $firstChild;
/**
@ -64,7 +64,7 @@ class DOMNode
* The last child of this node. If there is no such node, this returns NULL.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.lastchild
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMNode'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNode|null'], default: '')]
public $lastChild;
/**
@ -72,7 +72,7 @@ class DOMNode
* The node immediately preceding this node. If there is no such node, this returns NULL.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.previoussibling
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMNode'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNode|null'], default: '')]
public $previousSibling;
/**
@ -80,7 +80,7 @@ class DOMNode
* The node immediately following this node. If there is no such node, this returns NULL.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.nextsibling
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMNode'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNode|null'], default: '')]
public $nextSibling;
/**
@ -88,7 +88,7 @@ class DOMNode
* A <classname>DOMNamedNodeMap</classname> containing the attributes of this node (if it is a <classname>DOMElement</classname>) or NULL otherwise.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.attributes
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMNamedNodeMap'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNamedNodeMap|null'], default: '')]
public $attributes;
/**
@ -96,7 +96,7 @@ class DOMNode
* The <classname>DOMDocument</classname> object associated with this node, or NULL if this node is a <classname>DOMDocument</classname>.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.ownerdocument
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMDocument'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMDocument|null'], default: '')]
public $ownerDocument;
/**
@ -104,7 +104,7 @@ class DOMNode
* The namespace URI of this node, or NULL if it is unspecified.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.namespaceuri
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $namespaceURI;
/**
@ -120,7 +120,7 @@ class DOMNode
* Returns the local part of the qualified name of this node.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.localname
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $localName;
/**
@ -128,7 +128,7 @@ class DOMNode
* The absolute base URI of this node or NULL if the implementation wasn't able to obtain an absolute URI.
* @link https://php.net/manual/en/class.domnode.php#domnode.props.baseuri
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $baseURI;
/**
@ -491,19 +491,19 @@ class DOMImplementation
class DOMNameSpaceNode
{
#[LanguageLevelTypeAware(['8.1' => '?DOMNode'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMNode|null'], default: '')]
public $parentNode;
#[LanguageLevelTypeAware(['8.1' => '?DOMDocument'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMDocument|null'], default: '')]
public $ownerDocument;
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $namespaceURI;
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $localName;
#[LanguageLevelTypeAware(['8.1' => 'string'], default: '')]
public $prefix;
#[LanguageLevelTypeAware(['8.1' => 'int'], default: '')]
public $nodeType;
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $nodeValue;
#[LanguageLevelTypeAware(['8.1' => 'string'], default: '')]
public $nodeName;
@ -518,10 +518,10 @@ class DOMDocumentFragment extends DOMNode implements DOMParentNode
#[LanguageLevelTypeAware(['8.1' => 'int'], default: '')]
public $childElementCount;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $lastElementChild;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $firstElementChild;
public function __construct() {}
@ -559,7 +559,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.actualencoding
*/
#[Deprecated("Actual encoding of the document, is a readonly equivalent to encoding.")]
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $actualEncoding;
/**
@ -576,7 +576,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* The Document Type Declaration associated with this document.
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.doctype
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMDocumentType'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMDocumentType|null'], default: '')]
public $doctype;
/**
@ -585,7 +585,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* that is the document element of the document.
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.documentelement
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $documentElement;
/**
@ -593,7 +593,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* The location of the document or NULL if undefined.
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.documenturi
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $documentURI;
/**
@ -603,7 +603,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* encoding in this implementation.
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.encoding
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $encoding;
/**
@ -686,7 +686,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.version
*/
#[Deprecated('Version of XML, corresponds to xmlVersion')]
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $version;
/**
@ -695,7 +695,7 @@ class DOMDocument extends DOMNode implements DOMParentNode
* unspecified or when it is not known, such as when the Document was created in memory.
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.xmlencoding
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $xmlEncoding;
/**
@ -713,16 +713,16 @@ class DOMDocument extends DOMNode implements DOMParentNode
* declaration and if this document supports the "XML" feature, the value is "1.0".
* @link https://php.net/manual/en/class.domdocument.php#domdocument.props.xmlversion
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $xmlVersion;
#[LanguageLevelTypeAware(['8.1' => 'int'], default: '')]
public $childElementCount;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $lastElementChild;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $firstElementChild;
/**
@ -1267,10 +1267,10 @@ class DOMCharacterData extends DOMNode implements DOMChildNode
#[LanguageLevelTypeAware(['8.1' => 'int'], default: '')]
public $length;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $nextElementSibling;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $previousElementSibling;
/**
@ -1386,7 +1386,7 @@ class DOMAttr extends DOMNode
* The element which contains the attribute
* @link https://php.net/manual/en/class.domattr.php#domattr.props.ownerelement
*/
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $ownerElement;
/**
@ -1489,19 +1489,19 @@ class DOMElement extends DOMNode implements DOMParentNode, DOMChildNode
#[LanguageLevelTypeAware(['8.1' => 'string'], default: '')]
public $tagName;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $firstElementChild;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $lastElementChild;
#[LanguageLevelTypeAware(['8.1' => 'int'], default: '')]
public $childElementCount;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $previousElementSibling;
#[LanguageLevelTypeAware(['8.1' => '?DOMElement'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'DOMElement|null'], default: '')]
public $nextElementSibling;
/**
@ -1953,7 +1953,7 @@ class DOMDocumentType extends DOMNode
* The internal subset as a string, or null if there is none. This is does not contain the delimiting square brackets.
* @link https://php.net/manual/en/class.domdocumenttype.php#domdocumenttype.props.internalsubset
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $internalSubset;
}
@ -1991,7 +1991,7 @@ class DOMEntity extends DOMNode
* The public identifier associated with the entity if specified, and NULL otherwise.
* @link https://php.net/manual/en/class.domentity.php#domentity.props.publicid
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $publicId;
/**
@ -2000,7 +2000,7 @@ class DOMEntity extends DOMNode
* absolute URI or not.
* @link https://php.net/manual/en/class.domentity.php#domentity.props.systemid
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $systemId;
/**
@ -2008,7 +2008,7 @@ class DOMEntity extends DOMNode
* For unparsed entities, the name of the notation for the entity. For parsed entities, this is NULL.
* @link https://php.net/manual/en/class.domentity.php#domentity.props.notationname
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $notationName;
/**

View File

@ -80,7 +80,7 @@ function dcgettext(string $domain, string $message, int $category): string {}
* </p>
* @return string|false The full pathname for the <i>domain</i> currently being set.
*/
function bindtextdomain(string $domain, #[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $directory): string|false {}
function bindtextdomain(string $domain, #[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $directory): string|false {}
/**
* Plural version of gettext
@ -135,6 +135,6 @@ function dcngettext(string $domain, string $singular, string $plural, int $count
* </p>
* @return string|false A string on success.
*/
function bind_textdomain_codeset(string $domain, #[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $codeset): string|false {}
function bind_textdomain_codeset(string $domain, #[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $codeset): string|false {}
// End of gettext v.

View File

@ -3611,7 +3611,7 @@ function collator_get_sort_key(Collator $object, string $string): string|false {
* @return NumberFormatter|false|null <b>NumberFormatter</b> object or <b>FALSE</b> on error.
*/
#[Pure]
function numfmt_create(string $locale, int $style, #[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $pattern = null): ?NumberFormatter {}
function numfmt_create(string $locale, int $style, #[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $pattern = null): ?NumberFormatter {}
/**
* (PHP 5 &gt;= 5.3.0, PECL intl &gt;= 1.0.0)<br/>
@ -4299,7 +4299,7 @@ function datefmt_create(
int $timeType,
$timezone = null,
IntlCalendar|int|null $calendar = null,
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $pattern = null
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $pattern = null
): ?IntlDateFormatter {}
/**

View File

@ -95,7 +95,7 @@ class mysqli
/**
* @var string
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $connect_error;
/**
* @var int
@ -120,7 +120,7 @@ class mysqli
/**
* @var string
*/
#[LanguageLevelTypeAware(['8.1' => '?string'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'string|null'], default: '')]
public $info;
/**
* @var int|string
@ -848,7 +848,7 @@ class mysqli_result implements IteratorAggregate
/**
* @var array
*/
#[LanguageLevelTypeAware(['8.1' => '?array'], default: '')]
#[LanguageLevelTypeAware(['8.1' => 'array|null'], default: '')]
public $lengths;
/**
* @var int
@ -2450,11 +2450,11 @@ function mysqli_stat(mysqli $mysql): string|false {}
*/
function mysqli_ssl_set(
mysqli $mysql,
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $key,
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $certificate,
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $ca_certificate,
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $ca_path,
#[LanguageLevelTypeAware(['8.0' => '?string'], default: 'string')] $cipher_algos
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $key,
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $certificate,
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $ca_certificate,
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $ca_path,
#[LanguageLevelTypeAware(['8.0' => 'string|null'], default: 'string')] $cipher_algos
): bool {}
/**

View File

@ -410,7 +410,7 @@ class SoapClient
* @return void No value is returned.
* @since 5.0.4
*/
public function __setCookie($name, #[LanguageLevelTypeAware(["8.0" => "?string"], default: "string")] $value) {}
public function __setCookie($name, #[LanguageLevelTypeAware(["8.0" => "string|null"], default: "string")] $value) {}
/**
* Sets the location of the Web service to use
@ -466,7 +466,7 @@ class SoapVar
* </p>
* @since 5.0.1
*/
public function __construct($data, #[LanguageLevelTypeAware(["7.1" => "?int"], default: "int")] $encoding, #[LanguageLevelTypeAware(["8.0" => "?string"], default: "string")] $typeName, $typeNamespace = '', $nodeName = '', $nodeNamespace = '') {}
public function __construct($data, #[LanguageLevelTypeAware(["7.1" => "int|null"], default: "int")] $encoding, #[LanguageLevelTypeAware(["8.0" => "string|null"], default: "string")] $typeName, $typeNamespace = '', $nodeName = '', $nodeNamespace = '') {}
/**
* SoapVar constructor

View File

@ -37,4 +37,15 @@ abstract class BaseStubsTest extends TestCase
}
return $resultString;
}
public static function convertNullableTypesToUnion($typesToProcess, array &$resultArray)
{
array_walk($typesToProcess, function (string $type) use (&$resultArray) {
if (str_contains($type, '?')) {
array_push($resultArray, 'null', ltrim($type, '?'));
} else {
array_push($resultArray, $type);
}
});
}
}

View File

@ -502,13 +502,25 @@ class StubsTest extends BaseStubsTest
$className = $class->name;
$stubProperty = PhpStormStubsSingleton::getPhpStormStubs()->getClass($class->name)->properties[$property->name];
$propertyName = $stubProperty->name;
$conditionToCompareWithSignature = self::ifReflectionTypesExistInSignature($property->typesFromSignature, $stubProperty->typesFromSignature);
$conditionToCompareWithAttribute = self::ifReflectionTypesExistInAttributes($property->typesFromSignature, $stubProperty->typesFromAttribute);
$unifiedStubsPropertyTypes = [];
$unifiedStubsAttributesPropertyTypes = [];
$unifiedReflectionPropertyTypes = [];
self::convertNullableTypesToUnion($property->typesFromSignature, $unifiedReflectionPropertyTypes);
if (!empty($stubProperty->typesFromSignature)) {
self::convertNullableTypesToUnion($stubProperty->typesFromSignature, $unifiedStubsPropertyTypes);
} else {
foreach ($stubProperty->typesFromAttribute as $languageVersion => $listOfTypes) {
$unifiedStubsAttributesPropertyTypes[$languageVersion] = [];
self::convertNullableTypesToUnion($listOfTypes, $unifiedStubsAttributesPropertyTypes[$languageVersion]);
}
}
$conditionToCompareWithSignature = self::ifReflectionTypesExistInSignature($unifiedReflectionPropertyTypes, $unifiedStubsPropertyTypes);
$conditionToCompareWithAttribute = self::ifReflectionTypesExistInAttributes($unifiedReflectionPropertyTypes, $unifiedStubsAttributesPropertyTypes);
$testCondition = $conditionToCompareWithSignature || $conditionToCompareWithAttribute;
self::assertTrue($testCondition, "Property $className::$propertyName has invalid typehint.
Reflection property has type " . implode('|', $property->typesFromSignature) . ' but stubs has type ' .
implode('|', $stubProperty->typesFromSignature) . ' in signature and attribute has types ' .
self::getStringRepresentationOfTypeHintsFromAttributes($stubProperty->typesFromAttribute));
Reflection property has type " . implode('|', $unifiedReflectionPropertyTypes) . ' but stubs has type ' .
implode('|', $unifiedStubsPropertyTypes) . ' in signature and attribute has types ' .
self::getStringRepresentationOfTypeHintsFromAttributes($unifiedStubsAttributesPropertyTypes));
}
/**

View File

@ -351,15 +351,4 @@ class StubsTypeHintsTest extends BaseStubsTest
"' but stub parameter has type '" . implode('|', $unifiedStubsParameterTypes) . "' in signature and " .
BaseStubsTest::getStringRepresentationOfTypeHintsFromAttributes($unifiedStubsAttributesParameterTypes) . ' in attribute');
}
private static function convertNullableTypesToUnion($typesToProcess, array &$resultArray)
{
array_walk($typesToProcess, function (string $type) use (&$resultArray) {
if (str_contains($type, '?')) {
array_push($resultArray, 'null', ltrim($type, '?'));
} else {
array_push($resultArray, $type);
}
});
}
}