Skip to content

Commit

Permalink
improved parameter generation (both signature and doc-block)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kharhamel committed Sep 6, 2019
1 parent 277af72 commit 51d47ad
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 39 deletions.
2 changes: 1 addition & 1 deletion generator/src/Method.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ private function getDocBlock(): string

$i=1;
foreach ($this->getParams() as $parameter) {
$str .= '@param '.$parameter->getBestType().' $'.$parameter->getParameter().' ';
$str .= '@param '.$parameter->getDocBlockType().' $'.$parameter->getParameter().' ';
$str .= $this->getStringForXPath("(//docbook:refsect1[@role='parameters']//docbook:varlistentry)[$i]//docbook:para")."\n";
$i++;
}
Expand Down
69 changes: 39 additions & 30 deletions generator/src/Parameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,63 @@ public function __construct(\SimpleXMLElement $parameter, ?PhpStanFunction $phpS
}

/**
* Returns the type as declared in the doc.
* @return string
* Tries to identify the typehint from the doc-block parameter
*/
public function getType(): string
public function getSignatureType(): string
{
$type = $this->parameter->type->__toString();
$strType = Type::toRootNamespace($type);
if ($strType !== 'mixed' && $strType !== 'resource' && $this->phpStanFunction !== null) {
$phpStanParameter = $this->phpStanFunction->getParameter($this->getParameter());
if ($phpStanParameter) {
// Let's make the parameter nullable if it is by reference and is used only for writing.
if ($phpStanParameter->isWriteOnly()) {
$strType = '?'.$strType;
}
}
$coreType = $this->getDocBlockType();
//list all types in the doc-block
$types = explode('|', $coreType);
//no typehint exists for thoses cases
if (in_array('resource', $types) || in_array('mixed', $types)) {
return '';
}
//remove 'null' from the list to identify if the signature type should be nullable
$nullablePosition = array_search('null', $types);
if ($nullablePosition !== false) {
array_splice($types, $nullablePosition, 1);
}
if (count($types) === 0) {
throw new \RuntimeException('Error when trying to extract parameter type');
}
//if there is still several types, no typehint
if (count($types) > 1) {
return '';
}

$finalType = $types[0];
if (strpos($finalType, '[]') !== false) {
$finalType = 'array'; //generics cannot be typehinted and have to be turned into array
}
return $strType;
return ($nullablePosition !== false ? '?' : '').$finalType;
}

/**
* Returns the type as declared in the doc.
* @return string
* Try to fetch the complete type used in the doc_block, first from phpstan, then from the regular documentation
*/
public function getBestType(): string
public function getDocBlockType(): string
{
// Get the type from PhpStan database first, then from the php doc.
if ($this->phpStanFunction !== null) {
$phpStanParameter = $this->phpStanFunction->getParameter($this->getParameter());
if ($phpStanParameter) {
try {
return $phpStanParameter->getType();
$type = $phpStanParameter->getType();
// Let's make the parameter nullable if it is by reference and is used only for writing.
if ($phpStanParameter->isWriteOnly() && $type !== 'resource' && $type !== 'mixed') {
$type = $type.'|null';
}
return $type;
} catch (EmptyTypeException $e) {
// If the type is empty in PHPStan, let's fallback to documentation.
return $this->getType();
// If the type is empty in PHPStan, we fallback to documentation.
// @ignoreException
}
}
}
return $this->getType();

$type = $this->parameter->type->__toString();
return Type::toRootNamespace($type);
}

/*
* @return string
*/
public function getParameter(): string
{
if ($this->isVariadic()) {
Expand Down Expand Up @@ -157,9 +171,4 @@ private function getInnerXml(\SimpleXMLElement $SimpleXMLElement): string
$inner_xml = trim($inner_xml);
return $inner_xml;
}

public function isTypeable(): bool
{
return $this->getType() !== 'mixed' && $this->getType() !== 'resource' && \count(\explode("|", $this->getType())) < 2;
}
}
8 changes: 1 addition & 7 deletions generator/src/WritePhpFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,7 @@ private function displayParamsWithType(array $params): string
$optDetected = false;

foreach ($params as $param) {
$paramAsString = '';
if ($param->isTypeable()) {
if ($param->isNullable()) {
$paramAsString .= '?';
}
$paramAsString .= $param->getType().' ';
}
$paramAsString = $param->getSignatureType().' ';

$paramName = $param->getParameter();
if ($param->isVariadic()) {
Expand Down
11 changes: 10 additions & 1 deletion generator/tests/MethodTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,19 @@ public function testGetFunctionParam()
$xmlObject = $docPage->getMethodSynopsis();
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader(), Method::FALSY_TYPE);
$params = $method->getParams();
$this->assertEquals('string', $params[0]->getType());
$this->assertEquals('string', $params[0]->getSignatureType());
$this->assertEquals('pattern', $params[0]->getParameter());
}

public function testGetTypeHintFromRessource()
{
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/filesystem/functions/file-get-contents.xml');
$xmlObject = $docPage->getMethodSynopsis();
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader(), Method::FALSY_TYPE);
$params = $method->getParams();
$this->assertEquals('', $params[2]->getSignatureType());
}

public function testGetInitializer()
{
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/apc/functions/apc-cache-info.xml');
Expand Down

0 comments on commit 51d47ad

Please sign in to comment.