From e92bfcf7f7727d2443b67c493e80218fc73f17ef Mon Sep 17 00:00:00 2001 From: flashwave Date: Wed, 23 Dec 2020 01:44:45 +0000 Subject: [PATCH] import --- .gitattributes | 1 + .gitignore | 1 + lib/FWIF/FWIF.php | 162 ++++++++++++++++++++++ lib/FWIF/FWIFDecodeStream.php | 31 +++++ lib/FWIF/FWIFException.php | 4 + lib/FWIF/FWIFSerializable.php | 6 + lib/FWIF/FWIFUnsupportedTypeException.php | 4 + patchouli.txt | 28 ++++ public/index.php | 55 ++++++++ src/Dummy/DummyPackage.php | 55 ++++++++ src/Http/HttpRequest.php | 82 +++++++++++ src/IPackage.php | 10 ++ src/Patchouli.php | 18 +++ src/SingleInstance.php | 28 ++++ src/Version.php | 19 +++ startup.php | 21 +++ 16 files changed, 525 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 lib/FWIF/FWIF.php create mode 100644 lib/FWIF/FWIFDecodeStream.php create mode 100644 lib/FWIF/FWIFException.php create mode 100644 lib/FWIF/FWIFSerializable.php create mode 100644 lib/FWIF/FWIFUnsupportedTypeException.php create mode 100644 patchouli.txt create mode 100644 public/index.php create mode 100644 src/Dummy/DummyPackage.php create mode 100644 src/Http/HttpRequest.php create mode 100644 src/IPackage.php create mode 100644 src/Patchouli.php create mode 100644 src/SingleInstance.php create mode 100644 src/Version.php create mode 100644 startup.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd76df5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.debug diff --git a/lib/FWIF/FWIF.php b/lib/FWIF/FWIF.php new file mode 100644 index 0000000..bb97148 --- /dev/null +++ b/lib/FWIF/FWIF.php @@ -0,0 +1,162 @@ + 'Null', + self::TYPE_INTEGER => 'Integer', + self::TYPE_FLOAT => 'Float', + self::TYPE_STRING => 'String', + self::TYPE_ARRAY => 'Array', + self::TYPE_OBJECT => 'Object', + ]; + + private static function isAssocArray($array): bool { + if(!is_array($array) || $array === []) + return false; + return array_keys($array) !== range(0, count($array) - 1); + } + + private static function detectType($data): int { + if(is_null($data)) + return self::TYPE_NULL; + if(is_int($data)) + return self::TYPE_INTEGER; + if(is_float($data)) + return self::TYPE_FLOAT; + if(is_string($data)) // Should this check if a string is valid UTF-8 and swap over to TYPE_BUFFER? + return self::TYPE_STRING; + if(is_object($data) || self::isAssocArray($data)) + return self::TYPE_OBJECT; + if(is_array($data)) + return self::TYPE_ARRAY; + throw new FWIFUnsupportedTypeException(gettype($data)); + } + + public static function encode($data): string { + if($data instanceof FWIFSerializable) + $data = $data->fwifSerialize(); + $type = self::detectType($data); + return chr($type) . self::{'encode' . self::CODECS[$type]}($data); + } + + public static function decode(string $data) { + return self::decodeInternal(new FWIFDecodeStream($data)); + } + + private static function decodeInternal(FWIFDecodeStream $data) { + $type = $data->readByte(); + if(!array_key_exists($type, self::CODECS)) { + $hexType = dechex($type); $hexPos = dechex($data->getPosition()); + throw new FWIFUnsupportedTypeException("Unsupported type {$type} (0x{$hexType}) at position {$data->getPosition()} (0x{$hexPos})"); + } + return self::{'decode' . self::CODECS[$type]}($data); + } + + private static function encodeNull($data): string { return ''; } + private static function decodeNull(FWIFDecodeStream $data) { return null; } + + private static function encodeInteger(int $number): string { + $packed = ''; $more = 1; $negative = $number < 0; $size = PHP_INT_SIZE * 8; + while($more) { + $byte = $number & 0x7F; + $number >>= 7; + if($negative) + $number |= (~0 << ($size - 7)); + if((!$number && !($byte & 0x40)) || ($number === -1 && ($byte & 0x40))) + $more = 0; + else + $byte |= 0x80; + $packed .= chr($byte); + } + return $packed; + } + private static function decodeInteger(FWIFDecodeStream $data): int { + $number = 0; $shift = 0; $o = 0; $size = PHP_INT_SIZE * 8; + do { + $byte = $data->readByte(); + $number |= ($byte & 0x7F) << $shift; + $shift += 7; + } while($byte & 0x80); + if(($shift < $size) && ($byte & 0x40)) + $number |= (~0 << $shift); + return $number; + } + + private static function encodeFloat(float $number): string { + return pack('E', $number); + } + private static function decodeFloat(FWIFDecodeStream $data): float { + $packed = ''; for($i = 0; $i < 8; ++$i) $packed .= chr($data->readByte()); + return unpack('E', $packed)[1]; + } + + private static function encodeString(string $string): string { + $packed = ''; $string = unpack('C*', mb_convert_encoding($string, 'utf-8')); + foreach($string as $char) + $packed .= chr($char); + return $packed . chr(self::TYPE_TRAILER); + } + private static function decodeAsciiString(FWIFDecodeStream $data): string { + $string = ''; + for(;;) { + $byte = $data->readByte(); + if($byte === self::TYPE_TRAILER) + break; + $string .= chr($byte); + } + return $string; + } + private static function decodeString(FWIFDecodeStream $data): string { // This should decode based on the utf-8 spec rather than just + return mb_convert_encoding(self::decodeAsciiString($data), 'utf-8'); // grabbing the FF terminated string representation. + } + + private static function encodeArray(array $array): string { + $packed = ''; + foreach($array as $value) + $packed .= self::encode($value); + return $packed . chr(self::TYPE_TRAILER); + } + private static function decodeArray(FWIFDecodeStream $data): array { + $array = []; + for(;;) { + if($data->readByte() === self::TYPE_TRAILER) + break; + $data->stepBack(); + $array[] = self::decodeInternal($data); + } + return $array; + } + + private static function encodeObject($object): string { + $packed = ''; $array = (array)$object; + foreach($array as $name => $value) + $packed .= $name . chr(self::TYPE_TRAILER) . self::encode($value); + return $packed . chr(self::TYPE_TRAILER); + } + private static function decodeObject(FWIFDecodeStream $data): object { + $array = []; + for(;;) { + if($data->readByte() === self::TYPE_TRAILER) + break; + $data->stepBack(); + $name = self::decodeAsciiString($data); + $array[$name] = self::decodeInternal($data); + } + return (object)$array; + } +} diff --git a/lib/FWIF/FWIFDecodeStream.php b/lib/FWIF/FWIFDecodeStream.php new file mode 100644 index 0000000..3608976 --- /dev/null +++ b/lib/FWIF/FWIFDecodeStream.php @@ -0,0 +1,31 @@ +body = $body; + $this->length = strlen($body); + } + + public function getLength(): int { + return $this->length; + } + + public function getPosition(): int { + return $this->position; + } + + public function stepBack(): void { + $this->position = max(0, $this->position - 1); + } + + public function readByte(): int { + if($this->position + 1 >= $this->length) + return 0xFF; + return ord($this->body[$this->position++]); + } +} diff --git a/lib/FWIF/FWIFException.php b/lib/FWIF/FWIFException.php new file mode 100644 index 0000000..4f0c487 --- /dev/null +++ b/lib/FWIF/FWIFException.php @@ -0,0 +1,4 @@ +リV゙´匕j 〉  !ヽ ヽ、 +          , へヽ;;;;;`ヾミ`ヽ、⊥_   l    /_ノ>-─┐、 +          〉   l;;;;l;\;;;`ヾ三、ミ`ヽー/ 二三‐二二ニ⊥、\ +          ⌒T´::l;;;;l;;;;;;;`'';;ヽ、;;;`ヽ〉//二 -─..''.. ̄ ̄;//  `'' ー-、 +         ,,, -─'''>、.l;;;;l;;;;;;;;;;;;;;;;;;;`;;ヾニィ´;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;//\_  、 \ +      /,   /く ィュ|::l;;;;l;;;;;;;;;;;;;;;;;;;;;;;;;;|;;|:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;// ノ  `> ヾ ヽ + __〈∠l _//| `::ノ.::l;;;;l;;;;;;;;;;;;;;;;;l\〈\;;;;∩;;;;;;;r─-、;;;;//'' ィ、 |  l |、 | +´         ̄ `Y .:::::l;;;;l;;;;;;;;;;;;;;;;;ト、ヽ〉 ヽノ |´∨:::::r‐ノ;;//  \r''  | l ∨ +       ............... | ::::::l;;;;:ー-.、;;;;;;;;\    l::::_ノl/ 〈;;;;//l >| ̄丿 ノ!/ +           :::::\.. :: ̄ >、;;\;;;;;|\    〉! 、  `ヾ、ヽ:ヽ_ 〈 +           ::::::::: ̄ ̄へ  `ヽイ:::::_>、ノノヽ ゞ    !:ノ::::::.  \ヽ_ _ +..                _::二-‐''/ ̄ハ:::`ーr-、 `´ィ:::.....    〉:::::::::::..   ̄\:::`ヽ +:::.      ,, ‐''´/.. ̄:::::,::-‐:/:::::::ノ:::::ヽ::::::|:::l:::..:::::::::─-ノ .:::|::.::::::::::      ヽ:::: +::::::;;: ‐''゙´  /:::::::::::::/ ::/::::::;ィ´: :: ::::::::::!::::\:::::::::..../::: l:::::.::::::        :: +/    /ノ7:::::::/ /.:::::/ l:: :::::::::::::/:l ..:::: ̄ ̄`ー‐''´::::::\ _ +     //´ └-/  / .::::/     ..::-─/  \ .....:::::::::::::.   ::    \:: ̄`ヽ +  .::. : /     /  /..:::::/   _ 二 -─/    ノー-、::::::::::::..        `ヽ::::::::::. + .::::::._|    /   \_/      ::::::::/    /  .::::\- 、:::...       \:::::::.. +::::::/      !:: l\::  :. .   ..::::::::::/   ..::::/  .:::::::!:::::ト、 `ヽ;:..        \ー-、 +/      |::/ ::::7 :: :::...::::::::::::: 〈  ::: /: .::::::::/::/ \  :.        ヽ::::: \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..79ad9ef --- /dev/null +++ b/public/index.php @@ -0,0 +1,55 @@ +match('GET', '/packages')) { + $tags = explode(';', (string)$request->getQueryParam('tags', FILTER_SANITIZE_STRING)); + $packages = empty($tags) ? Patchouli::getPackages() : Patchouli::getPackagesWithTags($tags); + + $encoded = FWIF::encode($packages); + echo strlen($encoded) . ' ' . $encoded; + + echo "\r\n\r\n--------------------\r\n\r\n"; + + $jsonEncoded = json_encode($packages); + echo strlen($jsonEncoded) . ' ' . $jsonEncoded; + + echo "\r\n\r\n--------------------\r\n\r\n"; + + $hexdump = bin2hex($encoded); $hexdumpSect = 8; $hexdumpSize = 32; + for($i = 0; $i < strlen($hexdump) / $hexdumpSize; ++$i) { + $line = substr($hexdump, $i * $hexdumpSize, $hexdumpSize); + echo str_pad(dechex($i * $hexdumpSize), 4, '0', STR_PAD_LEFT) . ' '; + for($j = 0; $j < strlen($line) / $hexdumpSect; ++$j) + echo substr($line, $j * $hexdumpSect, $hexdumpSect) . ' '; + echo "\r\n"; + } + + echo "\r\n--------------------\r\n\r\n"; + + var_dump([(object)$packages[0]->fwifSerialize()]); + + echo "\r\n--------------------\r\n\r\n"; + + $decoded = FWIF::decode($encoded); + var_dump($decoded); + return; +} + +if($request->match('GET', '/')) { + header('Content-Type: text/html; charset=utf-8'); + echo 'Patchouli
';
+    readfile(PAT_ROOT . '/patchouli.txt');
+    echo '
'; + return; +} + +http_response_code(404); +echo '{"code":404,"message":"Path not found."}'; diff --git a/src/Dummy/DummyPackage.php b/src/Dummy/DummyPackage.php new file mode 100644 index 0000000..fcdbf17 --- /dev/null +++ b/src/Dummy/DummyPackage.php @@ -0,0 +1,55 @@ + null, + 'zero' => 0, + 'u8' => 0x42, + 'u16' => 0x4344, + 'u24' => 0x454647, + 'u32' => 0x58596061, + 'u40' => 0x6263646566, + 'u48' => 0x676869707172, + 'u56' => 0x73747576777879, + 'u64' => 0x7481828384858687, + 'neg32' => -12345678, + 'neg64' => -1234567890987654, + 'float' => 12345.6789, + 'array' => ['e', 'a', 0x55], + 'object' => new \stdClass, + 'misaka' => '御坂 美琴', + 'id' => $this->getId(), + 'name' => $this->getName(), + 'version' => $this->getVersion(), + 'deps' => [], + ]; + foreach($this->getDependencies() as $dependency) + $data['deps'][] = $dependency->getName(); + return $data; + } + + public function jsonSerialize() { + return $this->fwifSerialize(); + } +} diff --git a/src/Http/HttpRequest.php b/src/Http/HttpRequest.php new file mode 100644 index 0000000..e3d46b7 --- /dev/null +++ b/src/Http/HttpRequest.php @@ -0,0 +1,82 @@ +method = $server['REQUEST_METHOD']; + $this->path = '/' . trim(parse_url($server['REQUEST_URI'], PHP_URL_PATH), '/'); + $this->serverParams = $server; + $this->queryParams = $query; + $this->postParams = $post; + } + + public static function create(): self { + return new static($_SERVER, $_GET, $_POST); + } + + public function getMethod(): string { + return $this->method; + } + public function isMethod(string|array $method): bool { + if(is_string($method)) + return $this->method === $method; + return in_array($this->method, $method); + } + + public function getPath(): string { + return $this->path; + } + public function isPath(string $path): bool { + return $this->path === $path; + } + public function matchPath(string $regex, ?array &$args = null): bool { + if(!preg_match($regex, $this->path, $matches)) + return false; + $args = array_slice($matches, 1); + return true; + } + + public function match(string|array $method, string $path, ?array &$args = null): bool { + if(!$this->isMethod($method)) + return false; + if($path[0] === '/') + return $this->isPath($path); + return $this->matchPath($path, $args); + } + + public function getHeader($name): string { + $name = strtr(strtoupper($name), '-', '_'); + if($name === 'CONTENT_LENGTH' || $name === 'CONTENT_LENGTH') + return $this->serverParams[$name]; + return $this->serverParams['HTTP_' . $name] ?? ''; + } + + public function getServerParam(string $name): string { + return $this->serverParams[$name] ?? ''; + } + + public function getBody(): string { + return file_get_contents('php://input'); + } + + public function getQueryParams(): array { + return $this->queryParams; + } + public function getQueryParam(string $name, int $filter = FILTER_DEFAULT, mixed $options = null): mixed { + return filter_var($this->queryParams[$name] ?? null, $filter, $options); + } + + public function getPostParams(): array { + return $this->postParams; + } + public function getPostParam(string $name, int $filter = FILTER_DEFAULT, mixed $options = null): mixed { + return filter_var($this->postParams[$name] ?? null, $filter, $options); + } +} diff --git a/src/IPackage.php b/src/IPackage.php new file mode 100644 index 0000000..340966d --- /dev/null +++ b/src/IPackage.php @@ -0,0 +1,10 @@ +{'_' . $name}(...$args); + } + + public static function __callStatic(string $name, array $args) { + if($name[0] === '_') { + trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR); + return; + } + return self::getInstance()->{'_' . $name}(...$args); + } +} diff --git a/src/Version.php b/src/Version.php new file mode 100644 index 0000000..bf0caed --- /dev/null +++ b/src/Version.php @@ -0,0 +1,19 @@ +