@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class JavelinPeastLibrary extends Phobject {
4
5 /**
6 * The expected Peast version
7 *
8 * This is the version that would be obtained by downloading and including an
9 * up-to-date Peast. The //actual// Peast version may vary.
10 */
11 const EXPECTED_VERSION = '1.17.4';
12
13 const REPO = 'https://github.com/mck89/peast';
14
15 /**
16 * The expected md5 hash of the PHP-parser packages listed above.
17 */
18 private static $hashes = array(
19 // v1.17.4.tar.gz
20 'b6fe4a3345ec7653adae94cedb1b5405',
21 // v1.17.4.zip
22 'e8056c15c52de1dc2a2318462a0a4a61',
23 );
24
25 private static $version;
26
27 public static function build() {
28 $root = phutil_get_library_root('phorge');
29 $path = Filesystem::resolvePath($root.'/../support/peast');
30 $target = self::getPath();
31 $version = self::EXPECTED_VERSION;
32
33 if (extension_loaded('zip')) {
34 $target_path = $path.'/peast-'.$version.'.zip';
35
36 id(new PhutilGitHubReleaseDownloader(self::REPO, $target_path))
37 ->setDownloadFormat('zip')
38 ->setVersion($version)
39 ->validateDownload(self::$hashes)
40 ->download();
41
42 $zip = new ZipArchive();
43 $result = $zip->open($target_path);
44 if (!$result) {
45 throw new Exception(
46 pht(
47 'Opening %s failed! %s.',
48 $target_path,
49 $result === false ? 'Unknown Error' : (string)$result));
50 }
51
52 $zip->extractTo($target);
53
54 // Renames fail if the target directory exists.
55 Filesystem::remove("{$target}/Peast");
56
57 Filesystem::rename(
58 "{$target}/peast-{$version}/lib/Peast",
59 "{$target}/Peast");
60
61 Filesystem::remove("{$target}/peast-{$version}");
62 } else if (
63 extension_loaded('phar') &&
64 extension_loaded('zlib')) {
65
66 $target_path = $path.'/peast-'.$version.'.tar.gz';
67
68 id(new PhutilGitHubReleaseDownloader(self::REPO, $target_path))
69 ->setDownloadFormat('tar.gz')
70 ->setVersion($version)
71 ->validateDownload(self::$hashes)
72 ->download();
73
74 id(new PharData($target_path))->extractTo($target, null, true);
75
76 // Renames fail if the target directory exists.
77 Filesystem::remove("{$target}/Peast");
78
79 Filesystem::rename(
80 "{$target}/peast-{$version}/lib/Peast",
81 "{$target}/Peast");
82
83 Filesystem::remove("{$target}/peast-{$version}");
84 } else if (Filesystem::binaryExists('git')) {
85 execx(
86 'git clone --single-branch --depth 1 --branch %s %s %s',
87 'v'.self::EXPECTED_VERSION,
88 self::REPO,
89 $path.'/git');
90
91 // Renames fail if the target directory exists.
92 Filesystem::remove("{$path}/Peast");
93
94 Filesystem::rename(
95 "{$path}/git/lib/Peast",
96 "{$path}/Peast");
97
98 Filesystem::remove($path.'/git');
99 } else {
100 throw new Exception(
101 pht('No viable means to download Peast is available.'));
102 }
103
104 Filesystem::writeFile($target.'/version', $version);
105 }
106
107 /**
108 * Returns human-readable instructions for building PHP-parser.
109 *
110 * @return string
111 */
112 public static function getBuildInstructions() {
113 $root = phutil_get_library_root('phorge');
114 $script = Filesystem::resolvePath(
115 $root.'/../support/peast/build-peast.php');
116
117 return phutil_console_format(
118 "%s:\n\n \$ %s\n",
119 pht(
120 "Your version of '%s' is unbuilt or out of date. Run this ".
121 "script to build it.",
122 'peast'),
123 $script);
124 }
125
126 private static function peastAutoloader($classname) {
127 $lib = self::getPath();
128
129 if (strncmp($classname, 'Peast', 5)) {
130 return false;
131 }
132
133 $path = $lib.'/'.str_replace('\\', '/', $classname).'.php';
134
135 if (!Filesystem::pathExists($path)) {
136 return false;
137 }
138
139 require $path;
140
141 return true;
142 }
143
144 public static function loadLibrary() {
145 static $loaded = false;
146
147 if (!$loaded) {
148 if (!self::isAvailable()) {
149 try {
150 self::build();
151 } catch (Throwable $ex) {
152 throw new Exception(self::getBuildInstructions(), 0, $ex);
153 }
154 }
155
156 spl_autoload_register('JavelinPeastLibrary::peastAutoloader');
157 }
158 }
159
160 /**
161 * Returns the path to the Peast library.
162 *
163 * @return string
164 */
165 public static function getPath() {
166 static $path = null;
167
168 if (!$path) {
169 $root = phutil_get_library_root('phorge');
170 $path = Filesystem::resolvePath($root.'/../support/peast/lib');
171 }
172
173 return $path;
174 }
175
176 /**
177 * Returns the Peast version.
178 *
179 * @return string
180 */
181 public static function getVersion() {
182 if (self::$version === null) {
183 $lib = self::getPath();
184
185 if (Filesystem::pathExists($lib.'/version')) {
186 self::$version = trim(Filesystem::readFile($lib.'/version'));
187 }
188 }
189
190 return self::$version;
191 }
192
193 /**
194 * Checks if PHP-parser is built and up-to-date.
195 *
196 * @return bool
197 */
198 public static function isAvailable() {
199 $version = self::getVersion();
200 return $version === self::EXPECTED_VERSION;
201 }
202
203}