Last active
May 31, 2018 15:21
-
-
Save dg/14a9f37f1c6ac9bd77f96d050e8d5eed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
require '/nette.phar'; | |
class TypeHints | |
{ | |
public $php71 = true; | |
public $ignoredTypes = [ | |
false => ['mixed', 'resource', 'object', 'iterable', 'void'], // PHP 7.0 | |
true => ['mixed', 'resource', 'object', 'static'], // PHP 7.1 | |
]; | |
public $ignoredMethods = [ | |
'offsetSet', 'offsetGet', 'offsetExists', 'offsetUnset', | |
//'__set', '__get', '__isset', '__unset', | |
]; | |
public function update($file) | |
{ | |
$s = file_get_contents($file); | |
$s = str_replace("\r\n", "\n", $s); | |
$s = preg_replace_callback('#((?>\n[ \t]*\/\*\*.+\*\/))?(\s+[\w ]*function &?)(\w+)\((.*)\)(:?+)#sU', | |
function ($m) { | |
list(, $doc, $inter, $name, $args, $hint) = $m; | |
$args = $args ? explode(', ', $args) : []; | |
$returnHint = ''; | |
$this->processReturn($doc, $returnHint); | |
if (!in_array($name, $this->ignoredMethods, true)) { | |
$this->processParams($doc, $args); | |
} | |
if (trim($doc, "/* \t\n") === '') { | |
$doc = ''; | |
} | |
return $doc . $inter . $name . '(' . implode(', ', | |
$args) . ')' . ($hint ?: ($returnHint ? ": $returnHint" : '')); | |
}, $s); | |
$s = str_replace("\n", "\r\n", $s); | |
file_put_contents($file, $s); | |
} | |
private function processReturn(string &$doc, string &$returnHint): void | |
{ | |
$returnHint = ''; | |
$doc = preg_replace_callback( | |
'#\n[ \t]*\* *@return +(\S+)( +.*)?()#', | |
function (array $m) use (&$returnHint) { | |
list($line, $type, $info) = $m; | |
$nnType = $this->php71 ? str_replace(['|null', '|NULL'], '', $type) : $type; | |
$nullable = $type !== $nnType ? '?' : ''; | |
if (in_array($nnType, $this->ignoredTypes[$this->php71], true)) { | |
} elseif ($nnType === 'static') { | |
$returnHint = $nullable . 'self'; | |
} elseif (preg_match('#^[\w\\\\]+\[\]\z#', $nnType)) { | |
$returnHint = $nullable . 'array'; | |
} elseif (preg_match('#^[\w\\\\]+\z#', $nnType)) { | |
$returnHint = $nullable . $nnType; | |
if (!$info) { | |
return ''; | |
} | |
} | |
return $line; | |
}, | |
$doc | |
); | |
} | |
private function processParams(string &$doc, array &$args): void | |
{ | |
$counter = 0; | |
$removed = false; | |
$doc = preg_replace_callback( | |
'#(\n[ \t]*\* *@param)(?: +(\S+)(?:\s+(\$\w+))?( +.*)?)?()#', | |
function (array $m) use (&$args, &$counter, &$removed, $doc) { | |
list(, $begin, $type, $name, $info) = $m; | |
if ($name) { | |
foreach ($args as $counter => &$arg) { | |
if (strpos($arg, $name) !== false) { | |
$counter++; | |
goto process; | |
} | |
} | |
return $m[0]; | |
} | |
if (!isset($args[$counter])) { | |
echo "Too much @param in $doc\n"; | |
return $m[0]; | |
} | |
$arg = &$args[$counter++]; | |
process: | |
if ($info && strpos($arg, trim($info)) !== false) { | |
$info = ''; | |
} | |
$defaultType = $this->getDefaultType($arg); | |
$nnType = str_replace(['|null', '|NULL'], '', $type); | |
$nullable = ($type !== $nnType) && $defaultType !== 'null'; | |
$hasHint = preg_match('#\S &?\$#', $arg); | |
if (preg_match('#^[\w\\\\]+\z#', $nnType) && !in_array($nnType, $this->ignoredTypes[$this->php71], true) && (!$nullable || $this->php71)) { | |
if (!$hasHint) { | |
$arg = ($nullable ? '?' : '') . $nnType . ' ' . $arg; | |
} | |
if (!$info) { | |
$removed = true; | |
return ''; | |
} | |
} elseif (!$hasHint && $defaultType && $defaultType !== 'null') { | |
$arg = ($nullable ? '?' : '') . $defaultType . ' ' . $arg; | |
} | |
if ($removed && !$name) { | |
preg_match('#\$\w+#', $arg, $m); | |
$name = $m[0]; | |
} | |
return rtrim($begin . ' ' . trim($type . ' ' . $name) . $info); | |
}, | |
$doc | |
); | |
for (; $counter < count($args); $counter++) { | |
$arg = &$args[$counter]; | |
$defaultType = $this->getDefaultType($arg); | |
$hasHint = preg_match('#\S &?\$#', $arg); | |
if (!$hasHint && $defaultType && $defaultType !== 'null') { | |
$arg = $defaultType . ' ' . $arg; | |
} | |
} | |
} | |
private function getDefaultType($arg) | |
{ | |
if (preg_match('#= (?:(true|false)|(\d)|(["\'])|(\[)|(null))#i', $arg, $m)) { | |
if ($m[1]) { | |
return 'bool'; | |
} elseif ($m[2] !== '') { | |
return 'int'; | |
} elseif ($m[3]) { | |
return 'string'; | |
} elseif ($m[4]) { | |
return 'array'; | |
} elseif ($m[5]) { | |
return 'null'; | |
} | |
} | |
} | |
} | |
$updater = new TypeHints; | |
foreach (Nette\Utils\Finder::findFiles('*.php')->from(__DIR__) as $file) { | |
echo $file, "\n"; | |
$updater->update((string) $file); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment