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