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