3v4l.org

run code in 300+ PHP versions simultaneously
<?php exit; namespace Foobar; use Foobar\Packer\Internal; use PhpParser\ParserFactory; use PhpParser\Node\Stmt; class Packer { private $compactSourceFormat; function __construct($a = false) { $this->compactSourceFormat = $a; } function pack(string ...$a): string { $b = (new ParserFactory())->create(ParserFactory::ONLY_PHP7); $c = $this->compactSourceFormat ? new Internal\PrintCompact() : new Internal\PrintStandard(); $d = new Internal\Pruner(); $e = []; foreach ($a as $f) { $g = $b->parse($f); if (!$g) continue; if (!$g[0] instanceof Stmt\Namespace_) $g = [new Stmt\Namespace_(null, $g)]; array_push($e, ...$g); } $d->process($e); return $c->prettyPrint($e); } } namespace Foobar\Packer\Internal; class Utils { static function getSymbolGen($a, $b) { $c = 0; $d = strlen($a); return function () use (&$c, $a, $d, $b) { do { $e = ''; $f = $c; do { $e .= $a[$f % $d]; $f = (int) ($f / $d); } while ($f); $c++; } while (isset($b[$e])); return $e; }; } } namespace Foobar\Packer\Internal; use PhpParser\Node; class Traverser { const SKIP_CHILDREN = 1; const ABORT = 2; const REMOVE = 3; protected $before, $after; function __construct(?\Closure $a, ?\Closure $b = null) { $this->before = $a; $this->after = $b; } function traverse(&$a): void { $b = $this->before; $c = $this->after; $d = static function (&$e, $f) use ($b, $c, &$d, &$g) { $h = []; foreach ($e as $i => &$j) if (is_array($j)) $d($j, $f); elseif ($j instanceof Node) { $k = $g($j, $f); if ($k === self::REMOVE) $h[] = [$i, null]; elseif ($k) $h[] = [$i, $k]; } if ($h) do { [$i, $l] = array_pop($h); if ($l === null) \array_splice($e, $i, 1); elseif ($l instanceof Node) \array_splice($e, $i, 1, [$l]); else \array_splice($e, $i, 1, $l); } while ($h); }; $g = static function (Node &$m, $f) use ($b, $c, &$d, &$g) { $n = null; if ($b) $n = $b($m, $f); if ($n === self::ABORT) throw new TraverserAbort(); if ($n === null) foreach ($m->getSubNodeNames() as $o) { $p = &$m->{$o}; if (is_array($p)) $d($p, $m); elseif ($p instanceof Node) { $k = $g($p, $m); if ($k === self::ABORT) $p = null; elseif ($k instanceof Node) $p = $k; else if ($k) throw new \Exception('The node can\'t be replaced with a list of nodes, as it\'s not located in a list context.'); } } $q = null; if ($c) $q = $c($m, $f); if ($q === self::ABORT) throw new TraverserAbort(); if ($n || $q) { if ($q === self::REMOVE || $n === self::REMOVE) return self::REMOVE; if ($q instanceof Node || \is_array($q)) return $q; if ($n instanceof Node || \is_array($n)) return $n; if ($n !== self::SKIP_CHILDREN) throw new \Exception('Invalid return value from $before callback.'); if ($q !== self::SKIP_CHILDREN) throw new \Exception('Invalid return value from $after callback.'); } }; try { if (is_array($a)) $d($a, null); elseif ($a instanceof Node) $g($a, null); else throw new \TypeError('Unexpected format for $nodeOrNodes. Array or Node instance expected.'); } catch (TraverserAbort $e) {} } } namespace Foobar\Packer\Internal; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Stmt; use PhpParser\Comment; class Pruner { private const VAR_RESERVED = ['this' => true, '_ENV' => true, '_GET' => true, '_POST' => true, '_FILES' => true, '_SERVER' => true, '_COOKIE' => true, '_REQUEST' => true, '_SESSION' => true, 'GLOBALS' => true]; private const VAR_DICTIONARY = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; private $varGenMap, $varGenClosure; function process(&$a): void { $b = $this->getTraverser(); $b->traverse($a); } private function getTraverser() { $a = 0; $this->varGenReset(); $b = new Traverser( function (Node &$c) use (&$a) { if ($c instanceof Node\FunctionLike) $a++; }, function (Node &$c) use (&$a) { if ($c instanceof Node\FunctionLike) { $a--; if ($a == 0) $this->varGenReset(); } if ($a > 0 && $c instanceof Expr\Variable || $c instanceof Node\Param) if (is_string($c->name)) $c->name = $this->varGen($c->name); if ($a > 0 && $c instanceof Expr\ClosureUse) $c->var = $this->varGen($c->var); if ($c instanceof Stmt\Interface_) $c->stmts = []; if ($c instanceof Stmt\ClassMethod && $c->flags & Stmt\Class_::MODIFIER_ABSTRACT) return NodeTraverser::REMOVE_NODE; if ($c->getAttribute('comments')) $c->setAttribute('comments', null); if ($c instanceof Expr\FuncCall) if ($c->name instanceof Node\Name && $c->name->toString() === 'assert') return Traverser::REMOVE; if ($c instanceof Stmt\If_ && !$c->else && !$c->elseifs) { $d = $c->cond; if ($d instanceof Expr\BinaryOp\BooleanAnd) $d = $d->left; if ($d instanceof Expr\ConstFetch && $d->name->toString() === 'DEBUG') return Traverser::REMOVE; } if ($c instanceof Stmt\Class_) $c->flags &= ~(Stmt\Class_::MODIFIER_ABSTRACT | Stmt\Class_::MODIFIER_FINAL); if ($c instanceof Stmt\ClassMethod) $c->flags &= ~Stmt\Class_::MODIFIER_PUBLIC; }); return $b; } protected function varGenReset() { $this->varGenMap = []; $this->varGenClosure = Utils::getSymbolGen(self::VAR_DICTIONARY, self::VAR_RESERVED); } protected function varGen($a) { if (isset(self::VAR_RESERVED[$a])) return $a; if (!isset($this->varGenMap[$a])) { $b = ($this->varGenClosure)(); $this->varGenMap[$a] = $b; } return $this->varGenMap[$a]; } } namespace Foobar\Packer\Internal; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Stmt; class Compactor { private const CONST_USE = Stmt\Use_::TYPE_CONSTANT; private const FUNC_USE = Stmt\Use_::TYPE_FUNCTION; private const NORM_USE = Stmt\Use_::TYPE_NORMAL; function __construct($a) { $b = []; $c = []; foreach ($a as $d) { $e = $d->name === null ? 'global' : $d->name->toString(); $f = $this->getAliased($d); $g = $this->getMentioned($d); $h = []; $i = []; $j = []; foreach ([self::CONST_USE, self::FUNC_USE, self::NORM_USE] as $k) { $h[$k] = \array_diff_key($g[$k], $f[$k]); $f[$k] = \array_intersect_key($f[$k], $g[$k]); } $l = ['node' => &$d, 'aliased' => $f, 'original' => $h, 'mentioned' => $g]; $b[] = $l; $c[$e]['nodes'][] = &$d; $c[$e]['original'][Stmt\Use_::TYPE_NORMAL] += $h[Stmt\Use_::TYPE_NORMAL]; $c[$e]['original'][Stmt\Use_::TYPE_FUNCTION] += $h[Stmt\Use_::TYPE_FUNCTION]; $c[$e]['original'][Stmt\Use_::TYPE_CONSTANT] += $h[Stmt\Use_::TYPE_CONSTANT]; $c[$e]['mentioned'][Stmt\Use_::TYPE_NORMAL] += $g[Stmt\Use_::TYPE_NORMAL]; $c[$e]['mentioned'][Stmt\Use_::TYPE_FUNCTION] += $g[Stmt\Use_::TYPE_FUNCTION]; $c[$e]['mentioned'][Stmt\Use_::TYPE_CONSTANT] += $g[Stmt\Use_::TYPE_CONSTANT]; } } private function getAliased(Stmt\Namespace_ $a) { $b = []; foreach ($a->stmts as $c) { if (!$c instanceof Stmt\Use_ && !$c instanceof Stmt\GroupUse) break; $d = $c instanceof Stmt\GroupUse; $e = $d ? $c->prefix->toString() . '\\' : ''; foreach ($c->uses as $f) { $g = $e . $f->name->toString(); $h = $f->alias; $i = $d ? $f->type : $c->type; $j = $i !== Stmt\Use_::TYPE_CONSTANT; $k[$i][$j ? \strtolower($h) : $h] = $g; } } return $b; } private function getMentioned(Stmt\Namespace_ $a) { $b = []; $c = new Traverser( function ($d, $e) use (&$b) { if ($d instanceof Stmt\Const_) foreach ($d->consts as $f) $b[self::CONST_USE][$f->name] = true; if ($d instanceof Stmt\Function_) $b[self::FUNC_USE][\strtolower($d->name)] = true; if ($d instanceof Stmt\ClassLike) if ($d->name !== null) $b[self::NORM_USE][\strtolower($d->name)] = true; if ($d instanceof Node\Name) { $g = $d->toString(); if ($e instanceof Stmt\UseUse) ; elseif ($e instanceof Expr\ConstFetch) $b[self::CONST_USE][$g] = true; elseif ($e instanceof Expr\FuncCall) $b[self::FUNC_USE][\strtolower($g)] = true; else $b[self::NORM_USE][\strtolower($g)] = true; } }); $c->traverse($a); return $b; } } 135.6968879699707 Before: 21364 bytes; After: 8523 bytes Before: 566 lines; After: 244 lines // 135.6968879699707 ms // Before: 21364 bytes; After: 8523 bytes // Before: 566 lines; After: 244 lines
Output for 7.1.0 - 7.1.7
Parse error: syntax error, unexpected 'Before' (T_STRING) in /in/hd2hd on line 252
Process exited with code 255.
Output for 7.0.0 - 7.0.20
Parse error: syntax error, unexpected '?', expecting variable (T_VARIABLE) in /in/hd2hd on line 50
Process exited with code 255.

preferences:
147.61 ms | 1458 KiB | 7 Q