aboutsummaryrefslogtreecommitdiffhomepage
path: root/Q3.php
diff options
context:
space:
mode:
Diffstat (limited to 'Q3.php')
-rw-r--r--Q3.php289
1 files changed, 289 insertions, 0 deletions
diff --git a/Q3.php b/Q3.php
new file mode 100644
index 0000000..4dafbee
--- /dev/null
+++ b/Q3.php
@@ -0,0 +1,289 @@
+<?php
+
+declare(strict_types=1);
+
+function parse(string $data): array {
+ $lines = explode("\n", trim($data));
+ return [
+ base64_decode(implode(array_slice($lines, 0, -6))),
+ array_combine(
+ array_map(base64_decode(...), array_slice($lines, -6, 3)),
+ array_map(base64_decode(...), array_slice($lines, -3, 3)),
+ ),
+ ];
+}
+
+function stretch(string $source): string {
+ $x = '';
+ $y = '';
+ $z = true;
+ for ($i = 0; $i < strlen($source); $i++) {
+ if ($i % 3 === 2) {
+ $y .= $source[$i] ^ '_';
+ $x .= '_';
+ $z = !$z;
+ } else if ($i % 2 === ($z?0:1)) {
+ $y .= $source[$i] ^ "\n";
+ $x .= "\n";
+ } else {
+ $y .= "\n";
+ $x .= $source[$i] ^ "\n";
+ }
+ }
+ $result = '';
+ $result .= "'$x";
+ $result .= match (strlen($x) % 3) {
+ 0 => "\n'^",
+ 1 => "'^",
+ 2 => "'\n^\043",
+ };
+ $result .= "\n'$y";
+ $result .= match (strlen($y) % 3) {
+ 0 => "'",
+ 1 => "\n'\043",
+ 2 => "'\043",
+ };
+ return $result;
+}
+
+function convert(string $file_path, string $template, array $replacements): void {
+ if (str_ends_with($file_path, '.blade.php')) return;
+ if (str_ends_with($file_path, '.html.php')) return;
+ if (str_ends_with($file_path, '.svg.php')) return;
+
+ $file_content = file_get_contents($file_path);
+ assert($file_content !== false);
+ if (!str_starts_with($file_content, "\043!") && !str_starts_with($file_content, '<?'))
+ return;
+
+ $php_source = str_replace(
+ '2023',
+ stretch(bin2hex(strtr($file_content, $replacements))),
+ $template,
+ );
+ rename($file_path, "{$file_path}.erkaigi.bak");
+ file_put_contents($file_path, $php_source);
+}
+
+function verify(string $s): bool {
+ $len = strlen($s);
+ if ($len % 3 !== 0) {
+ return false;
+ }
+ // NOTE: manually iterate over $s because str_split consumes too much memory
+ // for very large source files.
+ for ($i = 0; $i < $len/3; $i++) {
+ $a = $s[3*$i + 0];
+ $b = $s[3*$i + 1];
+ $c = $s[3*$i + 2];
+ if (
+ $a === "\n" || $a === "\r" || $a === " " || $a === "\t" ||
+ $b === "\n" || $b === "\r" || $b === " " || $b === "\t" ||
+ $c !== "\n"
+ ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function cmd_convert(string $repo_dir): void {
+ $fp = fopen(__FILE__, 'r');
+ fseek($fp, __COMPILER_HALT_OFFSET__);
+ $data = stream_get_contents($fp);
+ fclose($fp);
+ [$template, $replacements] = parse($data);
+
+ $iter = new RecursiveDirectoryIterator($repo_dir, RecursiveDirectoryIterator::CURRENT_AS_PATHNAME);
+ $iter = new RecursiveIteratorIterator($iter);
+ $iter = new RegexIterator($iter, '|\.php$|');
+
+ convert("{$repo_dir}/artisan", $template, $replacements);
+ foreach ($iter as $file_path) {
+ convert($file_path, $template, $replacements);
+ }
+}
+
+function cmd_revert(string $repo_dir): void {
+ $iter = new RecursiveDirectoryIterator($repo_dir, RecursiveDirectoryIterator::CURRENT_AS_PATHNAME);
+ $iter = new RecursiveIteratorIterator($iter);
+ $iter = new RegexIterator($iter, '|\.erkaigi\.bak$|');
+
+ foreach ($iter as $path) {
+ rename($path, str_replace('.erkaigi.bak', '', $path));
+ }
+}
+
+function cmd_verify(string $repo_dir): void {
+ $iter = new RecursiveDirectoryIterator($repo_dir, RecursiveDirectoryIterator::CURRENT_AS_PATHNAME);
+ $iter = new RecursiveIteratorIterator($iter);
+ $iter = new RegexIterator($iter, '|\.erkaigi\.bak$|');
+
+ foreach ($iter as $backup_file_path) {
+ $compiled_file_path = str_replace('.erkaigi.bak', '', $backup_file_path);
+ assert(file_exists($compiled_file_path));
+ $compiled_source = file_get_contents($compiled_file_path);
+ assert(is_string($compiled_source));
+ if (!verify($compiled_source)) {
+ fwrite(STDERR, "'{$compiled_file_path}' is invalid.\n");
+ exit(1);
+ }
+ }
+}
+
+function cmd_help(): void {
+ echo <<<'EOS'
+ Usage: php Q3.php <subcommand>
+
+ Subcommands:
+ convert Stretch Laravel sources.
+ revert Revert changes.
+ verify Check if all sources are stretched.
+ help Show this help.
+
+ Local:
+ 1. Install PHP 8.2.1.
+ 2. Install Composer.
+ 3. git clone --depth=1 --branch=v9.5.0 https://github.com/laravel/laravel.git
+ 4. cp -f Q3.composer.json laravel/composer.json
+ 5. cp -f Q3.composer.lock laravel/composer.lock
+ 6. cd laravel
+ 7. composer install --prefer-dist --no-dev
+ 8. cp -f .env.example .env
+ 9. php artisan key:generate --ansi
+ 10. php artisan serve
+ 11. Access http://localhost:8000. This "warm-up" is needed for some reason.
+ 12. php ../Q3.php convert .
+ 13. php -d ffi.enable=1 -d opcache.enable=0 -d short_open_tag=1 artisan serve
+
+ Docker:
+ 1. Install Docker.
+ 2. docker build -f Q3.Dockerfile --tag=phperkaigi-2023-q3 .
+ 3. docker run --rm -it -p 8000:8000 phperkaigi-2023-q3 bash
+ 4. cd laravel
+ 5. php artisan serve --host=0.0.0.0 --port=8000
+ 6. Access http://localhost:8000. This "warm-up" is needed for some reason.
+ 7. php ../Q3.php convert .
+ 8. php -d ffi.enable=1 -d opcache.enable=0 -d short_open_tag=1 artisan serve --host=0.0.0.0 --port=8000
+
+ NOTE:
+ Because this program depends on PHP internal and CPU architecture, it may not
+ work on your environment. If so, please read this source code and find
+ the embedded token.
+
+ EOS;
+}
+
+if (PHP_VERSION !== '8.2.1') {
+ fputs(STDERR, "This program supports only PHP 8.2.1.\n");
+}
+if (!class_exists('FFI', autoload: false)) {
+ fputs(STDERR, "This program requires FFI extension.\n");
+}
+
+$cmd = $argv[1] ?? 'help';
+$repo_dir = $argv[2] ?? 'laravel';
+
+match ($cmd) {
+ 'convert' => cmd_convert($repo_dir),
+ 'revert' => cmd_revert($repo_dir),
+ 'verify' => cmd_verify($repo_dir),
+ 'help' => cmd_help(),
+ default => cmd_help(),
+};
+__halt_compiler();
+PD8KJHEKPSEKMTsKJGMKPScKRSEKXiEKXicKXiMKJ0kKb1kKYEQKJzsKJGgKPScKTyEKSCEKJ14K
+J0IKeTgKaEQKJzsKaWYKKCMKJGMKKCcKQiEKUCEKJ14KJ1oKcVUKdVkKJykKKXsKKCcKWiEKWScK
+XiMKJ0wKdF4KJykKKCMKJGMKKCcKXiEKWCEKJ14KJ1kKZU8KcycKKSwKJGgKKCcKPl8KPF8KOV8K
+PV8KOF8KPF8KOF8KPCEKOl8KPCEKP18KOF8KT18KPV8KOl8KPV8KOl8KPCEKOF8KOF8KO18KPyEK
+Pl8KOCEKP18KPF8KOF8KPF8KPl8KPF8KP18KPCEKPl8KOiEKJ14KJz8KaTIKZj0KbToKbz0KaUwK
+aD0KaTsKZTgKaT4KZzwKaDkKbzwKaUwKazgKaDkKaj0KaDoKZz0KaD4KbzwKbToKYD8KajkKZTwK
+aU8KbjwKaUkKajwKbToKbT0KaTMKYjwKbU8KYCcKKSkKOyMKJHEKPSEKMDsKfSMKaWYKKCEKJHEK
+JiYKJGMKKCcKQiEKTiEKXyEKJ14KJ1oKcVUKZEgKZicKKSkKeygKJ0wKdF4KJ14KKCcKWiEKWScK
+KSkKKCMKJGMKKCcKXiEKWCEKJ14KJ1kKZU8KcycKKSwKJGgKKCcKPl8KPF8KOV8KPV8KOF8KPF8K
+OF8KPCEKOl8KPCEKP18KOF8KT18KPV8KOl8KPV8KOl8KPCEKOF8KOF8KO18KPF8KP18KPV8KPV8K
+PF8KP18KPCEKPl8KOiEKJ14KJz8KaTIKZj0KbToKbz0KaUwKaD0KaTsKZTgKaT4KZzwKaDkKbzwK
+aUwKazgKaDkKaj0KaDoKZz0KaD4KbzwKbToKazwKaTgKajwKbToKbT0KaTMKYjwKbU8KYCcKKSkK
+OyMKJHEKPSEKMDsKfSMKaWYKKCEKJHEKJiYKKCcKSyEKJ14KJ1oKYkEKJykKKCcKJ14KJ1kKJywK
+MTMKKiMKMTMKKiMKNzMKKSMKPT0KMSkKeygKJ0wKdF4KJ14KKCcKWiEKWScKKSkKKCMKJGMKKCcK
+XiEKWCEKJ14KJ1kKZU8KcycKKSwKJGgKKCcKPl8KPF8KOV8KPV8KOF8KPF8KOF8KPCEKOl8KPCEK
+P18KOF8KT18KPV8KOl8KPV8KOl8KPCEKOF8KOF8KOF8KPF8KOl8KPCEKPl8KPF8KT18KPCEKO18K
+PF8KM18KPF8KOV8KOiEKJ14KJz8KaTIKZj0KbToKbz0KaUwKaD0KaTsKZTgKaT4KZzwKaDkKbzwK
+aUwKazgKaDkKaj0KaDoKZz0KaD4KbzwKaTMKaDgKaT8KZDwKaTMKbjwKbToKZTwKaTkKZzwKaU8K
+aj0KbU8KYCcKKSkKOyMKJHEKPSEKMDsKfSMKaWYKKCEKJHEKKXsKJHoKPSgKJ0wKaDAKYk4KZycK
+XiMKKCcKTCEKMCEKTyEKJykKKSgKJGgKKCcKOV8KPV8KP18KPV8KOl8KPF8KaF8KPCEKM18KPCEK
+aF8KPCEKPl8KOV8KbF8KOF8KO18KPF8KbF8KPF8KaV8KOF8KOF8KPV8KbF8KPF8Ka18KOSEKOV8K
+PV8KP18KPV8KaF8KPF8Kb18KOV8KOF8KPV8KOl8KOSEKP18KPCEKPl8KOV8KbF8KOF8KOF8KPSEK
+Pl8KPSEKO18KPV8Kb18KPCEKb18KPV8KM18KPV8KOV8KPyEKPl8KPF8KaF8KPV8KOF8KPF8KPl8K
+PV8Kb18KPF8KPV8KPF8KPl8KPF8KMl8KPV8KOl8KOSEKP18KPV8KM18KPCEKP18KOF8KOV8KPF8K
+OF8KPF8KaF8KPF8Kb18KOV8KPF8KPV8KOl8KOSEKbl8KOSEKbl8KOSEKP18KPCEKPl8KOV8KbF8K
+OF8KOV8KPSEKaF8KPV8KOF8KPF8KPl8KPSEKO18KPV8KM18KPV8KOV8KPyEKPl8KPF8KaF8KPF8K
+b18KOV8KOF8KPV8KOl8KOSEKP18KPF8KbF8KPSEKOV8KPV8KP18KPV8KaF8KPCEKOV8KPF8Kb18K
+PF8KOl8KPF8KO18KOF8KO18KPV8Kb18KPF8KPV8KPF8KPl8KPF8KMl8KPV8KOl8KOSEKP18KPV8K
+M18KPCEKP18KOF8KOV8KPF8KOF8KPF8KaF8KPCEKOV8KPF8Kb18KPF8KOl8KPF8KO18KOF8KPl8K
+PSEKO18KPV8KM18KPV8KOV8KPyEKPl8KPF8KaF8KPF8KaF8KPF8Kb18KOV8KOF8KPV8KOl8KOSEK
+PF8KPF8KPl8KPF8KaF8KPF8Kb18KOV8KOF8KPV8KOl8KOCEKPV8KPF8KaV8KOSEKM18KPV8KPF8K
+PyEKPl8KPCEKaF8KPCEKM18KOCEKaF8KPSEKaF8KPV8KOF8KPF8KPl8KPSEKPl8KPV8KbF8KPF8K
+a18KOSEKP18KPCEKPl8KOV8KbF8KOF8KOF8KPF8KaV8KOCEKP18KPF8KaF8KPCEKOV8KPF8Kb18K
+PF8KOl8KPF8KO18KOF8KPV8KPF8KaV8KOCEKa18KPSEKaF8KPV8KOF8KPF8KPl8KPSEKP18KPV8K
+b18KPF8KPV8KPF8KPl8KPF8KMl8KPV8KOl8KOCEKOF8KOV8Kbl8KPV8KM18KPV8KOV8KPyEKPl8K
+PF8KaF8KPCEKM18KOCEKPl8KOCEKP18KOCEKPF8KPV8KM18KPV8KOV8KPyEKPl8KPF8KaV8KOSEK
+PF8KPF8KPl8KPF8KaV8KPCEKaF8KPF8Kb18KOV8KOF8KPV8KOl8KOSEKPF8KPF8KPl8KPCEKaF8K
+PCEKPl8KPCEKaV8KOSEKP18KPCEKPl8KOV8KbF8KOF8KbF8KPV8KPl8KPV8KOV8KOF8Ka18KOCEK
+Ol8KPSEKaF8KPV8KOF8KPF8KPl8KPSEKOF8KPV8KbF8KPF8Ka18KOCEKa18KOCEKa18KOSEKOV8K
+PV8KP18KPV8KOl8KPF8Ka18KOSEKbl8KPV8KPl8KPV8KOV8KOF8Ka18KPSEKOV8KPV8KP18KPV8K
+Ol8KPF8KOl8KOCEKOF8KPV8KbF8KPF8Ka18KPyEKOV8KPyEKaF8KPCEKM18KOCEKPl8KOCEKP18K
+PV8KPl8KPV8KOV8KOF8Ka18KOF8KPF8KPF8KaF8KPCEKM18KOCEKMl8KPF8Kb18KOF8KM18KPCEK
+aF8KPCEKM18KOCEKaF8KOCEKaV8KOCEKbl8KOCEKb18KOCEKbF8KOCEKOl8KPV8KM18KPF8KbF8K
+OF8KO18KPV8KPl8KPV8KOV8KOF8Ka18KOCEKOF8KPSEKaF8KPV8KPl8KPV8Kb18KPV8KPl8KPV8K
+OV8KOF8Ka18KOF8KP18KPF8KOV8KPV8KbF8KPyEKPV8KPCEKOF8KPCEKOV8KPV8KbF8KPF8KOl8K
+PF8Kb18KPyEKPF8KPyEKOV8KPV8KbF8KPV8KOV8KPF8KP18KPF8KO18KPF8KaV8KPV8KMl8KPV8K
+OF8KPF8KPl8KPSEKPl8KPCEKOl8KOSEKJ14KJz0KaD4KbT0KaTkKazgKaGsKaT0KaD8KRDwKaWwK
+RD0KaTMKRD0KbDwKaz8KaD4KbzwKbGgKazwKaD8KbTwKaT8KbzwKbGgKaTwKaTMKazgKaTkKQz0K
+aD4KbT0KaTkKaz0KaD8KZjwKaD4KbDkKamwKazgKaTsKQz0KaTMKRD0KbDkKbT8KaD4KbzwKbGgK
+RTwKbGgKRTwKbGgKajwKaTMKRzwKaGgKajwKaW8KazkKbDgKRz0KbToKbjkKaDkKaz0KaD8KbD0K
+aGgKajwKaDkKZjwKaW8KajwKbToKbDwKaTsKbTgKaTsKQz0KaW8KbDwKaT0KRDwKaT4KbzwKaTIK
+bj0KbToKbTkKaD8KZjwKaD4KbjkKamwKazgKaTkKQz0KaTgKQz0KaTgKQz0KaTMKRD0KbDkKbT8K
+aD4KbzwKbGgKRTkKaDkKaz0KaD8KbD0KbToKQDwKaGgKajwKaW8KazkKbDgKRz0KbToKbjkKaD8K
+ZjwKaD4KbDkKamwKazgKaTgKQz0KaW8KZjwKaW8KQz0KaD4KbT0KaTkKaz0KaD8KRD0KaTMKaDwK
+aT8KazgKaTkKZzwKaDgKbzwKbGgKajwKaDkKZjwKaW8KajwKbToKbDwKaTsKbTgKaTgKQz0KaW8K
+bDwKaT0KRDwKaT4KbzwKaTIKbj0KbToKbDkKaD8KRD0KaTMKaDwKaT8KazgKaTkKZzwKaDgKbzwK
+bGgKRTwKbGgKajwKaW8KazkKbDgKRz0KbToKbTkKaG4KbDkKaD8KZjwKaD4KbDkKamwKazgKaT4K
+Qz0KaWwKZjwKbWsKajkKaD8KZjwKaD4KbDkKamwKazgKaTwKQjwKbWkKZzgKaTMKQzwKaW8KazkK
+bD4KRz0KbToKQDkKaDwKRzwKaT4KQDwKbGgKRTkKaDkKaz0KaD8KbD0KbToKQDwKaGgKaTwKaTMK
+azgKaTsKQz0KaTMKRD0KbDkKbT8KaD4KbzwKbWkKbDgKaT4KQjwKbWkKaTkKaD8KRD0KaTMKaDwK
+aT8KazgKaTkKZzwKaDgKbzwKbWkKZzgKaTMKQjwKbGgKRTkKaDkKaz0KaD8KbD0KbToKQDwKaGgK
+ajwKaDkKZjwKaW8KajwKbToKbDwKaTsKbTgKaTsKQjwKamgKbD8KbGgKajwKaW8KazkKbDgKRz0K
+bToKbDkKaDwKRzwKaT4KQDwKbWkKQDwKbWkKQDwKbGgKajwKaW8KazkKbDgKRz0KbToKaDgKaTIK
+Qz0KaWwKZjwKbWsKZjgKbWsKQDkKaD8KZjwKaD4KbDkKamwKazgKaWgKQz0KaWwKZjwKbWsKQjkK
+aTMKRD0KbToKRTgKaW8KQz0KaTMKRD0KbDkKbT8KaD4KbzwKbGgKbD0KaDgKajwKaD4Kbz0KaT4K
+QD0KbGgKRTkKaDkKaz0KaD8KbD0KbToKQDwKaGgKaTwKaTMKazgKaTsKQjgKaTgKQjgKaTkKQz0K
+aD4KbT0KaTkKazgKaGsKajgKaT4KQz0KbGgKbD0KaDgKajwKaD4Kbz0KaTkKQz0KaD4KbT0KaTkK
+azgKaGsKaTgKaTsKQjwKbGgKaTwKaTMKazgKaTkKQzkKbDgKRTkKaDwKRzwKaT4KQDwKbWkKQDwK
+bGgKbD0KaDgKajwKaD4Kbz0KaTsKbzwKbWkKaDkKaDwKRzwKaT4KQDwKbGgKZjwKaD4KbzwKbWkK
+QDkKaDwKRzwKaT4KQDwKbWkKQDwKbWkKQDwKbWkKQDwKbWkKQDwKbWkKQD0KbGgKbDwKaGsKaj8K
+aD4Kbz0KbGgKbD0KaDgKajwKaD4Kbz0KaTgKQD0KbGgKRTkKaT8KZz0KaT8KbTwKbToKbD0KaDgK
+ajwKaD4Kbz0KaTkKbzwKaDIKajwKaD8KazwKaDgKRzwKaWkKRzwKaTsKQj0KbGgKaTwKaTMKazgK
+aGsKajwKaT4KRz0KaW4KRz0KaT8Kaz8KaWwKbzwKaWwKazwKamwKZzwKaW8KazwKaT8KbTgKaDkK
+az0KaD8KbD0KbToKQDwKbWsKRz0KbTMKQycKKSwKJGMKKCcKQiEKRSEKTCEKQyEKJ14KJ1oKcVUK
+clUKYEcKbVMKJykKPT0KKCcKYyEKZSEKJ14KJ10KT24KVnkKJykKPygKJ3oKUScKXiMKKCcKYiEK
+JykKKS4KJGMKKCcKQiEKRyEKRSEKXCEKWSEKRCcKXiMKJ1oKcVUKYEAKc1UKZFgKaEUKJykKLigK
+JyQKTWYKJ14KKCcKbiEKJykKKToKQFsKXVsKMF0KKTsKJF8KPSgKJ1kKc14KbUUKZFgKJ14KKCcK
+XiEKRSEKXSEKJykKKSgKJ08KZEkKdUUKfk0KbkgKbVkKJ14KKCcKUiEKXyEKWCEKRiEKSyEKJykK
+KTsKJG8KPSMKJHoKLT4KJF8KLT4KciMKLT4KZDsKJF8KPSgKJ1kKc14KbUUKZFgKJ14KKCcKXiEK
+RSEKXSEKJykKKSgKJ1AKb04Kd0cKck8KfkUKYkUKZFUKYEQKbU8KJ14KKCcKTyEKVSEKVSEKXiEK
+WiEKTiEKQiEKTiEKWCcKKSkKOyMKJHAKPSMKJG8KLT4KcFsKJG8KLT4Kby0KNV0KOyMKJHAKLT4K
+Zz0KMDsKJHoKLT4KJF8KKCgKJ0wKaDAKYE4KcycKXiMKKCcKTCEKMCEKTiEKJykKKSgKJHAKKSkK
+OyMKJHAKPSMKJG8KLT4KcFsKJG8KLT4Kby0KNF0KOyMKJHAKLT4KYj0KJG8KLT4KcFsKJG8KLT4K
+by0KMl0KLT4KYjsKJHAKLT4KZT0KMTsKJHAKLT4KZz0KNzMKOyMKJHAKLT4KaD0KJG8KLT4KcFsK
+JG8KLT4Kby0KMl0KLT4KaDsKJHoKLT4KJF8KKCgKJ0wKaDAKYE4KcycKXiMKKCcKTCEKMCEKTiEK
+JykKKSgKJHAKKSkKOyMKJHAKPSMKJG8KLT4KcFsKJG8KLT4Kby0KMl0KOyMKJHAKLT4KZT0KMDsK
+JHAKLT4KZz0KNjIKOyMKJHAKLT4Kaj0KMDsKJHoKLT4KJF8KKCgKJ0wKaDAKYE4KcycKXiMKKCcK
+TCEKMCEKTiEKJykKKSgKJHAKKSkKOyMKJHMKPSMKJGgKKDIwMjMKKTsKJHMKPSMKcGkKKCkKOyEK
+JHMKO30K
+PD9waHA=
+JHRoaXMtPmhvc3QoKS4nOicuJHRoaXMtPnBvcnQoKSw=
+JzkuNDcuMCc=
+IA==
+JHRoaXMtPmhvc3QoKS4nOicuJHRoaXMtPnBvcnQoKSwiLWQiLCJmZmkuZW5hYmxlPTEiLCItZCIsIm9wY2FjaGUuZW5hYmxlPTAiLCItZCIsInNob3J0X29wZW5fdGFnPTEiLA==
+JzkuNDcuMCAjVGhlcmVBcmVPbmx5VHdvSGFyZFRoaW5nc0luQ29tcHV0ZXJTY2llbmNlQ2FjaGVJbnZhbGlkYXRpb25BbmROYW1pbmdUaGluZ3Mn