4b90c49a0ae65ea7a133730c17e48242007261ef
[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 ]);
42 }
43
44 private function extract(Phar $phar) {
45 $this->debug("Extracting %s ...\n", basename($phar->getPath()));
46 return (new Task\Extract($phar))->run($this->args->verbose);
47 }
48
49 private function hooks(SplObjectStorage $phars) {
50 $hooks = [];
51 foreach ($phars as $phar) {
52 if (isset($phar["pharext_install.php"])) {
53 $callable = include $phar["pharext_install.php"];
54 if (is_callable($callable)) {
55 $hooks[] = $callable($this);
56 }
57 }
58 }
59 return $hooks;
60 }
61
62 /**
63 * @inheritdoc
64 * @see \pharext\Command::run()
65 */
66 public function run($argc, array $argv) {
67 $list = new SplObjectStorage();
68 $phar = new Phar(Phar::running(false));
69 $temp = $this->extract($phar);
70
71 foreach ($phar as $entry) {
72 $dep_file = $entry->getBaseName();
73 if (fnmatch("*.ext.phar*", $dep_file)) {
74 $dep_phar = new Phar("$temp/$dep_file");
75 $list[$dep_phar] = $this->extract($dep_phar);
76 }
77 }
78 /* the actual ext.phar at last */
79 $list[$phar] = $temp;
80
81 /* installer hooks */
82 $hook = $this->hooks($list);
83
84 /* standard arg stuff */
85 $errs = [];
86 $prog = array_shift($argv);
87 foreach ($this->args->parse(--$argc, $argv) as $error) {
88 $errs[] = $error;
89 }
90
91 if ($this->args["help"]) {
92 $this->header();
93 $this->help($prog);
94 exit;
95 }
96
97 foreach ($this->args->validate() as $error) {
98 $errs[] = $error;
99 }
100
101 if ($errs) {
102 if (!$this->args["quiet"]) {
103 $this->header();
104 }
105 foreach ($errs as $err) {
106 $this->error("%s\n", $err);
107 }
108 if (!$this->args["quiet"]) {
109 $this->help($prog);
110 }
111 exit(1);
112 }
113
114 /* post process hooks */
115 foreach ($hook as $callback) {
116 if (is_callable($callback)) {
117 $callback($this);
118 }
119 }
120
121 /* install packages */
122 foreach ($list as $phar) {
123 $this->info("Installing %s ...\n", basename($phar->getPath()));
124 $this->install($list[$phar]);
125 $this->activate($list[$phar]);
126 $this->cleanup($list[$phar]);
127 $this->info("Successfully installed %s!\n", basename($phar->getPath()));
128 }
129 }
130
131 /**
132 * Phpize + trinity
133 */
134 private function install($temp) {
135 try {
136 // phpize
137 $this->info("Running phpize ...\n");
138 $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"});
139 $phpize->run($this->args->verbose);
140
141 // configure
142 $this->info("Running configure ...\n");
143 $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"});
144 $configure->run($this->args->verbose);
145
146 // make
147 $this->info("Running make ...\n");
148 $make = new Task\Make($temp);
149 $make->run($this->args->verbose);
150
151 // install
152 $this->info("Running make install ...\n");
153 $sudo = isset($this->args->sudo) ? $this->args->sudo : null;
154 $install = new Task\Make($temp, ["install"], $sudo);
155 $install->run($this->args->verbose);
156
157 } catch (\Exception $e) {
158 $this->error("%s\n", $e->getMessage());
159 exit(2);
160 }
161 }
162
163 private function cleanup($temp) {
164 if (is_dir($temp)) {
165 $this->rm($temp);
166 } elseif (file_exists($temp)) {
167 unlink($temp);
168 }
169 }
170
171 private function activate($temp) {
172 if ($this->args->ini) {
173 $files = [realpath($this->args->ini)];
174 } else {
175 $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files())));
176 $files[] = php_ini_loaded_file();
177 }
178
179 $sudo = isset($this->args->sudo) ? $this->args->sudo : null;
180
181 try {
182 $this->info("Running INI activation ...\n");
183 $activate = new Task\Activate($temp, $files, $sudo);
184 if (!$activate->run($this->args->verbose)) {
185 $this->info("Extension already activated ...\n");
186 }
187 } catch (\Exception $e) {
188 $this->error("%s\n", $e->getMessage());
189 $this->error("%s\n", $output);
190 exit(3);
191 }
192 }
193 }