SOF3 / pharynx

A tool to recompile PHP sources into a phar in PSR-0
Apache License 2.0
17 stars 4 forks source link

Parsing problems using strangely formatted but correct code #24

Open Xd-pro opened 8 months ago

Xd-pro commented 8 months ago

Xd-pro/safe_sql#2

Error: Syntax error: syntax error, unexpected token "public" on dev-plugins\dev/src\Finnbar\FFA\database\Transaction.php:2

As you can see the error is incorrect. There is no token "public" on line 2 of the file. The code works correctly when formatted with intelephense. The PHP CLI and intelephense both treat this code as valid, but pharynx does not. It doesn't contain any top-level functionality or ifs

Here is an example of the code:

<?php

namespace Finnbar\FFA\database;

use Exception;
use pocketmine\thread\Thread;
use pmmp\thread\ThreadSafe;
use pmmp\thread\ThreadSafeArray;
use pocketmine\plugin\PluginBase;
use pocketmine\scheduler\ClosureTask;
use Throwable;

/*

Hi future developer. This file is used by safe_sql.exe for code generation. Don't modify it manually.

*/

abstract class TransactionBase
{

    public function __construct(public \PDO $db)
    {
        $db->beginTransaction();
    }

    public function commit(): bool
    {
        return $this->db->commit();
    }

    public function rollBack(): bool
    {
        return $this->db->rollBack();
    }
}

abstract class AsyncTransaction
{

    abstract public function run(Transaction $t);
}

class DatabaseThread extends Thread
{

    /** @var ThreadSafeArray<int, DataEntry> */
    public $data;

    public int $active = 0;

    public function __construct(private string $databaseConnector)
    {
        $this->data = new ThreadSafeArray;
    }

    public function tick()
    {
        if ($this->isTerminated()) throw new Exception("Thread died?");
        /** @var DataEntry[] */
        $toProcess = [];
        $this->synchronized(function () use (&$toProcess) {
            $toPutBack = [];
            foreach ($this->data as $data) {
                if (!$data->query) {
                    $toProcess[] = $data;
                } else {
                    $toPutBack[] = $data;
                }
            }
            $this->data = ThreadSafeArray::fromArray($toPutBack);
        });
        foreach ($toProcess as $data) {
            if ($data->callbackId !== null) {
                $deser = unserialize($data->data);
                if ($deser instanceof Throwable) {
                    throw $deser;
                }
                ClosureStore::$closures[$data->callbackId]($deser);
                unset(ClosureStore::$closures[$data->callbackId]);
            }
        }
    }

    public bool $stop = false;

    public function onRun(): void
    {
        $conn = new \PDO($this->databaseConnector);
        while (true) {
            $t = false;
            /** @var DataEntry[] */
            $toProcess = [];
            $this->synchronized(function () use (&$toProcess, &$t) {
                $toPutBack = [];
                foreach ($this->data as $data) {
                    if ($data->query) {
                        $toProcess[] = $data;
                    } else {
                        $toPutBack[] = $data;
                    }
                }
                $this->data = ThreadSafeArray::fromArray($toPutBack);
                $this->active = count($toProcess);
                if ($this->stop && $this->active === 0) {
                    $t = true;
                }
            });

            if ($t) break;

            /** @var DataEntry[] */
            $toAdd = [];

            foreach ($toProcess as $data) {
                /** @var AsyncTransaction */
                $at = unserialize($data->data);
                try {
                    $toAdd[] = new DataEntry(false, serialize($at->run(new Transaction($conn))), $data->callbackId);
                    if ($conn->inTransaction()) $conn->commit();
                } catch (Exception $e) {
                    throw $e;
                    exit;
                    $toAdd[] = new DataEntry(false, serialize($e), $data->callbackId);
                    if ($conn->inTransaction()) $conn->rollBack();
                }
                $this->synchronized(function () {
                    $this->active--;
                });
            }
            $this->synchronized(function () use (&$toAdd) {
                foreach ($toAdd as $ta) {
                    $this->data[] = $ta;
                }
            });
            usleep(100);
        }
    }
}

class DatabasePool
{

    /** @var DatabaseThread[] $threads */
    private array $threads = [];

    public function run(AsyncTransaction $query, \Closure $onDone = null)
    {
        if ($onDone === null) {
            $onDone = function (mixed $data) {
                if ($data instanceof Exception) throw $data;
            };
        }
        $id = 0;
        while (isset(ClosureStore::$closures[$id])) {
            $id++;
        }
        ClosureStore::$closures[$id] = $onDone;
        $thread = $this->strongest_thread();
        $thread->synchronized(function () use (&$query, &$id, &$thread) {
            $thread->data[] = new DataEntry(true, \serialize($query), $id);
        });
    }

    public function stopThreads() {
        foreach ($this->threads as $thread) {
            $thread->synchronized(function () use (&$thread) {
                $thread->stop = true;
            });
            if ($thread->isRunning()) {
                $thread->join();
            }
        }
    }

    public function tick()
    {
        foreach ($this->threads as $thread) {
            $thread->tick();
        }
    }

    private function strongest_thread(): DatabaseThread
    {
        $lowest = \PHP_INT_MAX;
        $lowestThread = null;
        foreach ($this->threads as $thread) {
            if ($thread->active < $lowest) {
                $lowest = $thread->active;
                $lowestThread = $thread;
            }
        }
        if ($lowestThread === null) throw new \Exception("No threads available to process asynchronous query");
        return $lowestThread;
    }

    public function __construct(string $connectionString, int $workers = 1)
    {
        while ($workers > 0) {
            $workers--;
            $thread = new DatabaseThread($connectionString);
            $thread->start();
            $this->threads[] = $thread;
        }
    }
}

class DataEntry extends ThreadSafe
{

    public function __construct(public bool $query, public $data, public ?string $callbackId = null)
    {
    }
}

class ClosureStore
{
    public static array $closures = [];
}

class SafeSql
{
    private function __construct()
    {
    }

    public static function bootstrapPocketmine(PluginBase $plugin, string $connectionString, int $pollTicks = 4, int $workers = 1): DatabasePool
    {
        $p = new DatabasePool($connectionString, $workers);
        $plugin->getScheduler()->scheduleRepeatingTask(new ClosureTask(function () use (&$p) {
            $p->tick();
        }), $pollTicks);
        return $p;
    }
}
class Transaction extends TransactionBase {/** @return int */public function kit_create_table() {$statement = $this->db->prepare("create table if not exists PracticeKits (    OwnerUUID VARCHAR(36),    FOREIGN KEY (OwnerUUID) REFERENCES PracticePlayers(UUID),    KitType VARCHAR(25) NOT NULL,    PRIMARY KEY (OwnerUUID, KitType),    Inventory JSON NOT NULL) ");$statement->execute([]);return $statement->rowCount();}/** @return int */public function practice_set_player(string $uuid,string $name,int $kills,int $deaths,int $elo,int $cps,int $ownDeathMessages,int $otherDeathMessages,int $arenaRespawn,int $scoreboard,int $autosprint,int $seenRules,) {$statement = $this->db->prepare("insert into PracticePlayers (UUID, LastName, Kills, Deaths, Elo, CPS, OwnDeathMessages, OtherDeathMessages, ArenaRespawn, Scoreboard, Autosprint, SeenRules)values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) on duplicate key update    LastName = ?,    Kills = ?,     Deaths = ?,     Elo = ?,     CPS = ?,     OwnDeathMessages = ?,     OtherDeathMessages = ?,     ArenaRespawn = ?,     Scoreboard = ?,    Autosprint = ?,    SeenRules = ? ");$statement->execute([$uuid,$name,$kills,$deaths,$elo,$cps,$ownDeathMessages,$otherDeathMessages,$arenaRespawn,$scoreboard,$autosprint,$seenRules,$name,$kills,$deaths,$elo,$cps,$ownDeathMessages,$otherDeathMessages,$arenaRespawn,$scoreboard,$autosprint,$seenRules,]);return $statement->rowCount();}/** @return practice_top_kdr[]|\Generator */public function practice_top_kdr() {$statement = $this->db->prepare("SELECT LastName,       Deaths,       (Kills / NULLIF(Deaths, 0)) AS Kdr FROM PracticePlayers WHERE Deaths >= 20 ORDER BY kdr DESC LIMIT 20 "); $statement->execute([]); while ($res = $statement->fetch(\PDO::FETCH_NUM)) { yield new practice_top_kdr(...$res);}}/** @return int */public function kit_set_kit(string $OwnerUUID,string $KitType,string $Inventory,) {$statement = $this->db->prepare("insert into PracticeKits (OwnerUUID, KitType, Inventory) values (?, ?, ?) on duplicate key update Inventory = ? ");$statement->execute([$OwnerUUID,$KitType,$Inventory,$Inventory,]);return $statement->rowCount();}/** @return kit_get_kit[]|\Generator */public function kit_get_kit(string $OwnerUUID,) {$statement = $this->db->prepare("select Inventory, KitType from PracticeKits where OwnerUUID = ? "); $statement->execute([$OwnerUUID,]); while ($res = $statement->fetch(\PDO::FETCH_NUM)) { yield new kit_get_kit(...$res);}}/** @return practice_top_deaths[]|\Generator */public function practice_top_deaths() {$statement = $this->db->prepare("select LastName, Deaths from PracticePlayers order by Deaths desc limit 0,20 "); $statement->execute([]); while ($res = $statement->fetch(\PDO::FETCH_NUM)) { yield new practice_top_deaths(...$res);}}/** @return practice_get_player[]|\Generator */public function practice_get_player(string $uuid,) {$statement = $this->db->prepare("select UUID, LastName, Kills, Deaths, Elo, CPS, OwnDeathMessages, OtherDeathMessages, ArenaRespawn, Scoreboard, Autosprint, SeenRules from PracticePlayers where UUID = ? "); $statement->execute([$uuid,]); while ($res = $statement->fetch(\PDO::FETCH_NUM)) { yield new practice_get_player(...$res);}}/** @return int */public function practice_create_table_players() {$statement = $this->db->prepare("create table if not exists PracticePlayers (    UUID varchar(36) primary key,    LastName varchar(16) not null,    Kills int unsigned not null default 0,    Deaths int unsigned not null default 0,    Elo int unsigned not null default 1000,    CPS boolean not null default true,    OwnDeathMessages boolean not null default true,    OtherDeathMessages boolean not null default true,    ArenaRespawn boolean not null default false,    Scoreboard boolean not null default true,    Autosprint boolean not null default false,    SeenRules boolean not null default false) ");$statement->execute([]);return $statement->rowCount();}/** @return practice_top_elo[]|\Generator */public function practice_top_elo() {$statement = $this->db->prepare("select LastName, Elo from PracticePlayers order by Elo desc limit 0,20 "); $statement->execute([]); while ($res = $statement->fetch(\PDO::FETCH_NUM)) { yield new practice_top_elo(...$res);}}/** @return practice_top_kills[]|\Generator */public function practice_top_kills() {$statement = $this->db->prepare("select LastName, Kills from PracticePlayers order by Kills desc limit 0,20 "); $statement->execute([]); while ($res = $statement->fetch(\PDO::FETCH_NUM)) { yield new practice_top_kills(...$res);}}}class practice_top_kdr {public function __construct(public string $LastName,public float $Kdr,) {}}class kit_get_kit {public function __construct(public string $Inventory,public string $KitType,) {}}class practice_top_deaths {public function __construct(public string $LastName,public int $Deaths,) {}}class practice_get_player {public function __construct(public string $UUID,public string $LastName,public int $Kills,public int $Deaths,public int $Elo,public bool $CPS,public bool $OwnDeathMessages,public bool $OtherDeathMessages,public bool $ArenaRespawn,public bool $Scoreboard,public bool $Autosprint,public bool $SeenRules,) {}}class practice_top_elo {public function __construct(public string $LastName,public int $Elo,) {}}class practice_top_kills {public function __construct(public string $LastName,public int $Kills,) {}}class AT_kit_create_table extends AsyncTransaction {public function __construct() {}public function run(Transaction $t,) {$out = $t->kit_create_table();return $out;}}class AT_practice_set_player extends AsyncTransaction {public function __construct(private string $uuid,private string $name,private int $kills,private int $deaths,private int $elo,private int $cps,private int $ownDeathMessages,private int $otherDeathMessages,private int $arenaRespawn,private int $scoreboard,private int $autosprint,private int $seenRules,) {}public function run(Transaction $t,) {$out = $t->practice_set_player($this->uuid,$this->name,$this->kills,$this->deaths,$this->elo,$this->cps,$this->ownDeathMessages,$this->otherDeathMessages,$this->arenaRespawn,$this->scoreboard,$this->autosprint,$this->seenRules,);return $out;}}class AT_practice_top_kdr extends AsyncTransaction {public function __construct() {}public function run(Transaction $t,) {$out = $t->practice_top_kdr();
            $rv = [];
            foreach ($out as $out) {
                $rv[]=$out;
            }
            return $rv;}}class AT_kit_set_kit extends AsyncTransaction {public function __construct(private string $OwnerUUID,private string $KitType,private string $Inventory,) {}public function run(Transaction $t,) {$out = $t->kit_set_kit($this->OwnerUUID,$this->KitType,$this->Inventory,);return $out;}}class AT_kit_get_kit extends AsyncTransaction {public function __construct(private string $OwnerUUID,) {}public function run(Transaction $t,) {$out = $t->kit_get_kit($this->OwnerUUID,);
            $rv = [];
            foreach ($out as $out) {
                $rv[]=$out;
            }
            return $rv;}}class AT_practice_top_deaths extends AsyncTransaction {public function __construct() {}public function run(Transaction $t,) {$out = $t->practice_top_deaths();
            $rv = [];
            foreach ($out as $out) {
                $rv[]=$out;
            }
            return $rv;}}class AT_practice_get_player extends AsyncTransaction {public function __construct(private string $uuid,) {}public function run(Transaction $t,) {$out = $t->practice_get_player($this->uuid,);
            $rv = [];
            foreach ($out as $out) {
                $rv[]=$out;
            }
            return $rv;}}class AT_practice_create_table_players extends AsyncTransaction {public function __construct() {}public function run(Transaction $t,) {$out = $t->practice_create_table_players();return $out;}}class AT_practice_top_elo extends AsyncTransaction {public function __construct() {}public function run(Transaction $t,) {$out = $t->practice_top_elo();
            $rv = [];
            foreach ($out as $out) {
                $rv[]=$out;
            }
            return $rv;}}class AT_practice_top_kills extends AsyncTransaction {public function __construct() {}public function run(Transaction $t,) {$out = $t->practice_top_kills();
            $rv = [];
            foreach ($out as $out) {
                $rv[]=$out;
            }
            return $rv;}}