support packages that have been pickle'd
[pharext/pharext] / src / pharext / SourceDir / Pecl.php
1 <?php
2
3 namespace pharext\SourceDir;
4
5 use pharext\Command;
6 use pharext\SourceDir;
7
8 /**
9 * A PECL extension source directory containing a v2 package.xml
10 */
11 class Pecl implements \IteratorAggregate, SourceDir
12 {
13 /**
14 * The Packager command
15 * @var pharext\Packager
16 */
17 private $cmd;
18
19 /**
20 * The package.xml
21 * @var SimpleXmlElement
22 */
23 private $sxe;
24
25 /**
26 * The base directory
27 * @var string
28 */
29 private $path;
30
31 /**
32 * @inheritdoc
33 * @see \pharext\SourceDir::__construct()
34 */
35 public function __construct(Command $cmd, $path) {
36 if (realpath("$path/package2.xml")) {
37 $sxe = simplexml_load_file("$path/package2.xml");
38 } elseif (realpath("$path/package.xml")) {
39 $sxe = simplexml_load_file("$path/package.xml");
40 } else {
41 throw new \Exception("Missing package.xml in $path");
42 }
43 $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]);
44
45 $args = $cmd->getArgs();
46 if (!isset($args->name)) {
47 $name = (string) $sxe->xpath("/pecl:package/pecl:name")[0];
48 foreach ($args->parse(2, ["--name", $name]) as $error) {
49 $cmd->error("%s\n", $error);
50 }
51 }
52
53 if (!isset($args->release)) {
54 $release = (string) $sxe->xpath("/pecl:package/pecl:version/pecl:release")[0];
55 foreach ($args->parse(2, ["--release", $release]) as $error) {
56 $cmd->error("%s\n", $error);
57 }
58 }
59
60 $this->cmd = $cmd;
61 $this->sxe = $sxe;
62 $this->path = $path;
63 }
64
65 /**
66 * @inheritdoc
67 * @see \pharext\SourceDir::getBaseDir()
68 */
69 public function getBaseDir() {
70 return $this->path;
71 }
72
73 /**
74 * Compute the path of a file by parent dir nodes
75 * @param \SimpleXMLElement $ele
76 * @return string
77 */
78 private function dirOf($ele) {
79 $path = "";
80 while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") {
81 $path = trim($ele["name"], "/") ."/". $path ;
82 }
83 return trim($path, "/");
84 }
85
86 /**
87 * Render installer hook
88 * @param array $configure
89 * @return string
90 */
91 private static function loadHook($configure, $dependencies) {
92 return include __DIR__."/../../pharext_install.tpl.php";
93 }
94
95 /**
96 * Create installer hook
97 * @return \Generator
98 */
99 private function generateHooks() {
100 $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package");
101 foreach ($dependencies as $key => $dep) {
102 if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) {
103 usort($glob, function($a, $b) {
104 return version_compare(
105 substr($a, strpos(".ext.phar", $a)),
106 substr($b, strpos(".ext.phar", $b))
107 );
108 });
109 yield realpath($this->path."/".end($glob));
110 } else {
111 unset($dependencies[$key]);
112 }
113 }
114 $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption");
115 if ($configure) {
116 $fd = tmpfile();
117 ob_start(function($s) use($fd){
118 fwrite($fd, $s);
119 return null;
120 });
121 self::loadHook($configure, $dependencies);
122 ob_end_flush();
123 rewind($fd);
124 yield "pharext_install.php" => $fd;
125 }
126 }
127
128 /**
129 * Generate a list of files from the package.xml
130 * @return Generator
131 */
132 private function generateFiles() {
133 foreach ($this->generateHooks() as $file => $hook) {
134 if ($this->cmd->getArgs()->verbose) {
135 $this->cmd->info("Packaging %s\n", is_string($hook) ? $hook : $file);
136 }
137 yield $file => $hook;
138 }
139 foreach ($this->sxe->xpath("//pecl:file") as $file) {
140 $path = $this->path ."/". $this->dirOf($file) ."/". $file["name"];
141 if ($this->cmd->getArgs()->verbose) {
142 $this->cmd->info("Packaging %s\n", $path);
143 }
144 if (!($realpath = realpath($path))) {
145 $this->cmd->error("File %s does not exist", $path);
146 }
147 yield $realpath;
148 }
149 }
150
151 /**
152 * Implements IteratorAggregate
153 * @see IteratorAggregate::getIterator()
154 */
155 public function getIterator() {
156 return $this->generateFiles();
157 }
158 }