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