0171eaeb6ce695bd7211ce8faa998ca6f2e17f67
[pharext/pharext] / src / pharext / Installer.php
1 <?php
2
3 namespace pharext;
4
5 use pharext\Cli\Args as CliArgs;
6 use pharext\Cli\Command as CliCommand;
7
8 use Phar;
9 use SplObjectStorage;
10
11 /**
12 * The extension install command executed by the extension phar
13 */
14 class Installer implements Command
15 {
16 use CliCommand;
17
18 /**
19 * Cleanups
20 * @var array
21 */
22 private $cleanup = [];
23
24 /**
25 * Create the command
26 */
27 public function __construct() {
28 $this->args = new CliArgs([
29 ["h", "help", "Display help",
30 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
31 ["v", "verbose", "More output",
32 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG],
33 ["q", "quiet", "Less output",
34 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG],
35 ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7",
36 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG],
37 ["n", "common-name", "PHP common program name, e.g. php5 or zts-php",
38 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG,
39 "php"],
40 ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag",
41 CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG],
42 ["s", "sudo", "Installation might need increased privileges",
43 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG,
44 "sudo -S %s"],
45 ["i", "ini", "Activate in this php.ini instead of loaded default php.ini",
46 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG],
47 [null, "signature", "Show package signature",
48 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
49 [null, "license", "Show package license",
50 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
51 [null, "name", "Show package name",
52 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
53 [null, "date", "Show package release date",
54 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
55 [null, "release", "Show package release version",
56 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
57 [null, "version", "Show pharext version",
58 CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
59 ]);
60 }
61
62 /**
63 * Perform cleaniup
64 */
65 function __destruct() {
66 foreach ($this->cleanup as $cleanup) {
67 $cleanup->run();
68 }
69 }
70
71 private function extract($phar) {
72 $temp = (new Task\Extract($phar))->run($this->verbosity());
73 $this->cleanup[] = new Task\Cleanup($temp);
74 return $temp;
75 }
76
77 private function hooks(SplObjectStorage $phars) {
78 $hook = [];
79 foreach ($phars as $phar) {
80 if (isset($phar["pharext_package.php"])) {
81 $sdir = include $phar["pharext_package.php"];
82 if ($sdir instanceof SourceDir) {
83 $this->args->compile($sdir->getArgs());
84 $hook[] = $sdir;
85 }
86 }
87 }
88 return $hook;
89 }
90
91 private function load() {
92 $list = new SplObjectStorage();
93 $phar = extension_loaded("Phar")
94 ? new Phar(Phar::running(false))
95 : new Archive(PHAREXT_PHAR);
96 $temp = $this->extract($phar);
97
98 foreach ($phar as $entry) {
99 $dep_file = $entry->getBaseName();
100 if (fnmatch("*.ext.phar*", $dep_file)) {
101 $dep_phar = extension_loaded("Phar")
102 ? new Phar("$temp/$dep_file")
103 : new Archive("$temp/$dep_file");
104 $list[$dep_phar] = $this->extract($dep_phar);
105 }
106 }
107
108 /* the actual ext.phar at last */
109 $list[$phar] = $temp;
110 return $list;
111 }
112
113 /**
114 * @inheritdoc
115 * @see \pharext\Command::run()
116 */
117 public function run($argc, array $argv) {
118 try {
119 /* load the phar(s) */
120 $list = $this->load();
121 /* installer hooks */
122 $hook = $this->hooks($list);
123 } catch (\Exception $e) {
124 $this->error("%s\n", $e->getMessage());
125 exit(self::EEXTRACT);
126 }
127
128 /* standard arg stuff */
129 $errs = [];
130 $prog = array_shift($argv);
131 foreach ($this->args->parse(--$argc, $argv) as $error) {
132 $errs[] = $error;
133 }
134
135 if ($this->args["help"]) {
136 $this->header();
137 $this->help($prog);
138 exit;
139 }
140 try {
141 foreach (["signature", "name", "date", "license", "release", "version"] as $opt) {
142 if ($this->args[$opt]) {
143 printf("%s\n", $this->metadata($opt));
144 exit;
145 }
146 }
147 } catch (\Exception $e) {
148 $this->error("%s\n", $e->getMessage());
149 exit(self::EARGS);
150 }
151
152 foreach ($this->args->validate() as $error) {
153 $errs[] = $error;
154 }
155
156 if ($errs) {
157 if (!$this->args["quiet"]) {
158 $this->header();
159 }
160 foreach ($errs as $err) {
161 $this->error("%s\n", $err);
162 }
163 if (!$this->args["quiet"]) {
164 $this->help($prog);
165 }
166 exit(self::EARGS);
167 }
168
169 try {
170 /* post process hooks */
171 foreach ($hook as $sdir) {
172 $sdir->setArgs($this->args);
173 }
174 } catch (\Exception $e) {
175 $this->error("%s\n", $e->getMessage());
176 exit(self::EARGS);
177 }
178
179 /* install packages */
180 try {
181 foreach ($list as $phar) {
182 $this->info("Installing %s ...\n", basename($phar->getPath()));
183 $this->install($list[$phar]);
184 $this->activate($list[$phar]);
185 $this->info("Successfully installed %s!\n", basename($phar->getPath()));
186 }
187 } catch (\Exception $e) {
188 $this->error("%s\n", $e->getMessage());
189 exit(self::EINSTALL);
190 }
191 }
192
193 /**
194 * Phpize + trinity
195 */
196 private function install($temp) {
197 // phpize
198 $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"});
199 $phpize->run($this->verbosity());
200
201 // configure
202 $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"});
203 $configure->run($this->verbosity());
204
205 // make
206 $make = new Task\Make($temp);
207 $make->run($this->verbosity());
208
209 // install
210 $sudo = isset($this->args->sudo) ? $this->args->sudo : null;
211 $install = new Task\Make($temp, ["install"], $sudo);
212 $install->run($this->verbosity());
213 }
214
215 private function activate($temp) {
216 if ($this->args->ini) {
217 $files = [$this->args->ini];
218 } else {
219 $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files())));
220 $files[] = php_ini_loaded_file();
221 }
222
223 $sudo = isset($this->args->sudo) ? $this->args->sudo : null;
224 $type = $this->metadata("type") ?: "extension";
225
226 $activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo);
227 if (!$activate->run($this->verbosity())) {
228 $this->info("Extension already activated ...\n");
229 }
230 }
231 }