forked from leproxy/leproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompile.php
157 lines (136 loc) · 6.46 KB
/
compile.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?php
// explicitly give VERSION via ENV or ask git for current version
$version = getenv('VERSION');
if ($version === false) {
$version = ltrim(exec('git describe --always --dirty', $unused, $code), 'v');
if ($code !== 0) {
fwrite(STDERR, 'Error: Unable to get version info from git. Try passing VERSION via ENV' . PHP_EOL);
exit(1);
}
}
// use first argument as output file or use "leproxy-{version}.php"
$out = isset($argv[1]) ? $argv[1] : ('leproxy-' . $version . '.php');
system('composer install --no-dev --classmap-authoritative');
$classes = require __DIR__ . '/vendor/composer/autoload_classmap.php';
$includes = require __DIR__ . '/vendor/composer/autoload_files.php';
system('composer install');
echo 'Loading ' . count($classes) . ' classes to determine load order...';
// register autoloader which remembers load order
$ordered = array();
spl_autoload_register(function ($name) use ($classes, &$ordered) {
require $classes[$name];
$ordered[$name] = $classes[$name];
});
// use actual include file instead of include wrapper
foreach ($includes as $i => $path) {
$includes[$i] = str_replace('_include.php', '.php', $path);
require $path;
}
// load each class (and its superclasses once) into memory
foreach ($classes as $class => $path) {
class_exists($class, true);
}
echo ' DONE' . PHP_EOL;
// resulting list of all includes and ordered class list
$files = array_merge($includes, $ordered);
echo 'Concatenating ' . count($files) . ' files into ' . $out . '...';
system('sed -n "/\#\!/,/^$/p" leproxy.php | sed -e "s/version dev/version ' . $version . '/" > ' . escapeshellarg($out));
foreach ($files as $file) {
$file = substr($file, strlen(__DIR__) + 1);
system('(echo "# ' . $file . '"; grep -v "<?php" ' . escapeshellarg($file) . ') >> ' . escapeshellarg($out));
}
$file = 'leproxy.php';
system('(echo "# ' . $file . '"; egrep -v "^<\?php|^require " ' . escapeshellarg($file) . ') | sed -e "s/development/release/;/define(\'VERSION\'/c const VERSION=\"' . $version . '\";" >> ' . escapeshellarg($out));
chmod($out, 0755);
echo ' DONE (' . filesize($out) . ' bytes)' . PHP_EOL;
echo 'Optimizing resulting file...';
$small = '';
$all = token_get_all(file_get_contents($out));
// search next non-whitespace/non-comment token
$next = function ($i) use (&$all) {
for ($i = $i + 1; !isset($all[$i]) || is_array($all[$i]) && ($all[$i][0] === T_COMMENT || $all[$i][0] === T_DOC_COMMENT || $all[$i][0] === T_WHITESPACE); ++$i);
return $i;
};
// search previous non-whitespace/non-comment token
$prev = function ($i) use (&$all) {
for ($i = $i -1; $i >= 0 && (!isset($all[$i]) || (is_array($all[$i]) && ($all[$i][0] === T_COMMENT || $all[$i][0] === T_DOC_COMMENT || $all[$i][0] === T_WHITESPACE))); --$i);
return $i;
};
$first = true;
foreach ($all as $i => $token) {
if (is_array($token) && ($token[0] === T_COMMENT || $token[0] === T_DOC_COMMENT)) {
// remove all comments except first
if ($first === true) {
$first = false;
continue;
}
unset($all[$i]);
} elseif (is_array($token) && $token[0] === T_PUBLIC) {
// get next non-whitespace token after `public` visibility
$token = $all[$next($i)];
if (is_array($token) && $token[0] === T_VARIABLE) {
// use shorter variable notation `public $a` => `var $a`
$all[$i] = array(T_VAR, 'var');
} else {
// remove unneeded public identifier `public static function a()` => `static function a()`
unset($all[$i]);
}
} elseif (is_array($token) && $token[0] === T_LNUMBER) {
// Use shorter integer notation `0x0F` => `15` and `011` => `9`.
// Technically, hex codes may be shorter for very large ints, but adding
// another 2 leading chars is rarely worth it.
// Optimizing floats is not really worth it, as they have many special
// cases, such as e-notation and we would lose types for `0.0` => `0`.
$all[$i][1] = (string)intval($token[1], 0);
} elseif (is_array($token) && $token[0] === T_NEW) {
// remove unneeded parenthesis for constructors without args `new a();` => `new a;`
// jump over next token (class name), then next must be open parenthesis, followed by closing
$open = $next($next($i));
$close = $next($open);
if ($all[$open] === '(' && $all[$close] === ')') {
unset($all[$open], $all[$close]);
}
} elseif (is_array($token) && $token[0] === T_STRING) {
// replace certain functions with their shorter alias function name
// http://php.net/manual/en/aliases.php
static $replace = array(
'implode' => 'join',
'fwrite' => 'fputs',
'array_key_exists' => 'key_exists',
'current' => 'pos',
);
// check this has a replacement and "looks like" a function call
// this works on a number of assumptions, such as not being aliased/namespaced
if (isset($replace[$token[1]])) {
$p = $all[$prev($i)];
if ($all[$next($i)] === '(' && (!is_array($p) || !in_array($p[0], array(T_FUNCTION, T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_NEW)))) {
$all[$i][1] = $replace[$all[$i][1]];
}
}
} elseif (is_array($token) && $token[0] === T_EXIT) {
// replace `exit` with shorter alias `die`
// it's a language construct, not a function (see above)
$all[$i][1] = 'die';
} elseif (is_array($token) && $token[0] === T_RETURN) {
// replace `return null;` with `return;`
$t = $next($i);
if (is_array($all[$t]) && $all[$t][0] === T_STRING && $all[$t][1] === 'null' && $all[$next($t)] === ';') {
unset($all[$t]);
}
}
}
$all = array_values($all);
foreach ($all as $i => $token) {
if (is_array($token) && $token[0] === T_WHITESPACE) {
if (strpos($token[1], "\n") !== false) {
$token = strpos("()[]<>=+-*/%|,.:?!'\"\n", substr($small, -1)) === false ? "\n" : '';
} else {
$last = substr($small, -1);
$next = isset($all[$i + 1]) ? substr(is_array($all[$i + 1]) ? $all[$i + 1][1] : $all[$i + 1], 0, 1) : ' ';
$token = (strpos('()[]{}<>;=+-*/%&|,.:?!@\'"' . "\r\n", $last) !== false || strpos('()[]{}<>;=+-*/%&|,.:?!@\'"' . '\\$', $next) !== false) ? '' : ' ';
}
}
$small .= isset($token[1]) ? $token[1] : $token;
}
file_put_contents($out, $small);
echo ' DONE (' . strlen($small) . ' bytes)' . PHP_EOL;