13declare(strict_types=1);
 
   15namespace pocketmine\network\mcpe\protocol\tools\generate_create_static_methods;
 
   17use 
function array_map;
 
   18use 
function array_slice;
 
   20use 
function class_exists;
 
   23use 
function file_get_contents;
 
   24use 
function file_put_contents;
 
   27use 
function preg_match;
 
   28use 
function preg_split;
 
   30use 
function str_repeat;
 
   35require dirname(__DIR__) . 
'/vendor/autoload.php';
 
   37function generateCreateFunction(\ReflectionClass $reflect, 
int $indentLevel, 
int $modifiers, 
string $methodName) : array{
 
   40    $longestParamType = 0;
 
   41    $phpstanParamTags = [];
 
   42    $longestPhpstanParamType = 0;
 
   43    foreach($reflect->getProperties() as $property){
 
   44        if($property->getDeclaringClass()->getName() !== $reflect->getName()){
 
   47        $properties[$property->getName()] = $property;
 
   48        if(($phpDoc = $property->getDocComment()) !== 
false && preg_match(
'/@var ([A-Za-z\d\[\]\\\\]+)/', $phpDoc, $matches) === 1){
 
   49            $paramTags[] = [$matches[1], $property->getName()];
 
   50            $longestParamType = max($longestParamType, strlen($matches[1]));
 
   52        if(($phpDoc = $property->getDocComment()) !== 
false && preg_match(
'/@phpstan-var ([A-Za-z\d\[\]\\\\<>:\*\(\)\$ ,]+)/', substr($phpDoc, 3, -2), $matches) === 1){
 
   53            $matches[1] = trim($matches[1]);
 
   54            $phpstanParamTags[] = [$matches[1], $property->getName()];
 
   55            $longestPhpstanParamType = max($longestPhpstanParamType, strlen($matches[1]));
 
   61    $lines[] = 
" * @generate-create-func";
 
   62    foreach($paramTags as $paramTag){
 
   63        $lines[] = 
" * @param " . str_pad($paramTag[0], $longestParamType, 
" ", STR_PAD_RIGHT) . 
" $" . $paramTag[1];
 
   65    foreach($phpstanParamTags as $paramTag){
 
   66        $lines[] = 
" * @phpstan-param " . str_pad($paramTag[0], $longestPhpstanParamType, 
" ", STR_PAD_RIGHT) . 
" $" . $paramTag[1];
 
   70    $visibilityStr = match(
true){
 
   71        ($modifiers & \ReflectionMethod::IS_PUBLIC) !== 0 => 
"public",
 
   72        ($modifiers & \ReflectionMethod::IS_PRIVATE) !== 0 => 
"private",
 
   73        ($modifiers & \ReflectionMethod::IS_PROTECTED) !== 0 => 
"protected",
 
   74        default => 
throw new \InvalidArgumentException(
"Visibility must be a ReflectionMethod visibility constant")
 
   76    $funcStart = 
"$visibilityStr static function $methodName(";
 
   77    $funcEnd = 
") : self{";
 
   79    foreach($properties as $name => $reflectProperty){
 
   81        $propertyType = $reflectProperty->getType();
 
   84        if($propertyType instanceof \ReflectionNamedType){
 
   85            $stringType = ($propertyType->allowsNull() ? 
"?" : 
"") . ($propertyType->isBuiltin() ? 
"" : 
"\\") . $propertyType->getName();
 
   86        }elseif($propertyType instanceof \ReflectionUnionType){
 
   87            $stringType = implode(
"|", array_map(fn(\ReflectionNamedType $subType) => ($subType->isBuiltin() ? 
"" : 
"\\") . $subType->getName(), $propertyType->getTypes()));
 
   90        $params[] = ($stringType !== 
"" ? 
"$stringType " : 
"") . 
"\$$name";
 
   92    if(count($params) <= 6){
 
   93        $lines[] = $funcStart . implode(
", ", $params) . $funcEnd;
 
   95        $lines[] = $funcStart;
 
   96        foreach($params as $param){
 
   97            $lines[] = 
"\t$param,";
 
  101    if(count($params) > 0){
 
  102        $lines[] = 
"\t\$result = new self;";
 
  103        foreach($properties as $name => $reflectProperty){
 
  104            $lines[] = 
"\t\$result->$name = \$$name;";
 
  106        $lines[] = 
"\treturn \$result;";
 
  108        $lines[] = 
"\treturn new self;";
 
  112    return array_map(fn(
string $line) => str_repeat(
"\t", $indentLevel) . $line, $lines);
 
  115foreach(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(dirname(__DIR__) . 
'/src', \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
 
  116    if(substr($file, -4) !== 
".php"){
 
  119    $contents = file_get_contents($file);
 
  120    if($contents === 
false){
 
  121        throw new \RuntimeException(
"Failed to get contents of $file");
 
  124    if(preg_match(
"/(*ANYCRLF)^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match(
'/(*ANYCRLF)^((final|abstract)\s+)?class /m', $contents) !== 1){
 
  127    $shortClassName = basename($file, 
".php");
 
  128    $className = $matches[1] . 
"\\" . $shortClassName;
 
  129    if(!class_exists($className)){
 
  132    $reflect = new \ReflectionClass($className);
 
  133    $newContents = $contents;
 
  135    foreach($reflect->getMethods(\ReflectionMethod::IS_STATIC) as $method){
 
  136        if($method->getDeclaringClass()->getName() !== $reflect->getName() || $method->isAbstract()){
 
  140        $docComment = $method->getDocComment();
 
  141        if($docComment === 
false || preg_match(
'/@generate-create-func\s/', $docComment) !== 1){
 
  145        $lines = preg_split(
"/(*ANYCRLF)\n/", $newContents);
 
  146        $docCommentLines = preg_split(
"/(*ANYCRLF)\n/", $docComment);
 
  147        $beforeLines = array_slice($lines, 0, $method->getStartLine() - 1 - count($docCommentLines));
 
  148        $afterLines = array_slice($lines, $method->getEndLine());
 
  149        $newContents = implode(
"\n", $beforeLines) . 
"\n" . implode(
"\n", generateCreateFunction($reflect, 1, $method->getModifiers(), $method->getName())) . 
"\n" . implode(
"\n", $afterLines);
 
  151        $modified[] = $method->getName();
 
  154    $shortName = substr($reflect->getName(), strlen(
"pocketmine\\network\\mcpe\\protocol\\"));
 
  155    if($newContents !== $contents){
 
  156        file_put_contents($file, $newContents);
 
  157        echo 
"Successfully patched class $shortName: " . implode(
", ", $modified) . 
"\n";
 
  158    }elseif(count($modified) > 0){
 
  159        echo 
"Already up to date class $shortName: " . implode(
", ", $modified) . 
"\n";
 
  161        echo 
"No functions found with @generate-create-func tag in class $shortName\n";