PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/PhpBrew/Extension/ExtensionFactory.php

http://github.com/c9s/phpbrew
PHP | 346 lines | 251 code | 29 blank | 66 comment | 29 complexity | 3a56002a6bc9bdeed6f5b2ce4a575115 MD5 | raw file
  1. <?php
  2. namespace PhpBrew\Extension;
  3. use Exception;
  4. use PEARX\PackageXml\Parser as PackageXmlParser;
  5. use PhpBrew\Config;
  6. use RecursiveDirectoryIterator;
  7. use RecursiveIteratorIterator;
  8. /**
  9. * This factory class handles the extension information.
  10. */
  11. class ExtensionFactory
  12. {
  13. /**
  14. * One extension directory might contains multiple config*.m4 file, like
  15. * memcache extension.
  16. */
  17. public static function configM4Exists($extensionDir)
  18. {
  19. $files = array();
  20. $configM4Path = $extensionDir . DIRECTORY_SEPARATOR . 'config.m4';
  21. if (file_exists($configM4Path)) {
  22. $files[] = $configM4Path;
  23. }
  24. for ($i = 0; $i < 10; ++$i) {
  25. $configM4Path = $extensionDir . DIRECTORY_SEPARATOR . "config{$i}.m4";
  26. if (file_exists($configM4Path)) {
  27. $files[] = $configM4Path;
  28. }
  29. }
  30. return $files;
  31. }
  32. public static function createFromDirectory($packageName, $extensionDir)
  33. {
  34. $packageXmlPath = $extensionDir . DIRECTORY_SEPARATOR . 'package.xml';
  35. // If the package.xml exists, we may get the configureoptions for configuring the Makefile
  36. // and use the provided extension name to enable the extension.
  37. //
  38. // Currently only PECL extensions have package.xml, however It's the
  39. // best strategy to install the extension.
  40. if (file_exists($packageXmlPath)) {
  41. // $this->logger->warning("===> Using xml extension meta");
  42. if ($ext = self::createPeclExtension($packageName, $packageXmlPath)) {
  43. return $ext;
  44. }
  45. }
  46. // If the config.m4 or config0.m4 exists, it requires us to run `phpize` to
  47. // initialize the `configure` script.
  48. //
  49. // It's basically a fallback for extensions that don't have package.xml.
  50. // Generlly, The possible extensions using this strategy are usually
  51. // PHP's core extensions, which are shipped in the distribution file.
  52. // quote:
  53. // the 0 there makes sure it gets into another stage of the buildprocess, the
  54. // top IIRC, it was added @ 12th May 2001, 12:09am (10 months ago).
  55. //
  56. // http://grokbase.com/t/php/php-dev/023cpdc9k6/quick-summary-of-changes
  57. //
  58. // When config[0-9].m4 found, it might be an extension that can't be
  59. // installed as a shared extension. We will need to raise a warning
  60. // message for users.
  61. $configM4Paths = self::configM4Exists($extensionDir);
  62. foreach ($configM4Paths as $m4path) {
  63. if (file_exists($m4path)) {
  64. try {
  65. $ext = self::createM4Extension($packageName, $m4path);
  66. if ($ext) {
  67. return $ext;
  68. }
  69. } catch (Exception $e) {
  70. // Can't parse the content, ignore the error and continue the parsing...
  71. }
  72. }
  73. }
  74. }
  75. public static function lookupRecursive($packageName, array $lookupDirs = array(), $fallback = true)
  76. {
  77. if ($fallback) {
  78. // Always push the PHP source directory to the end of the list for the fallback.
  79. $lookupDirs[] = Config::getBuildDir()
  80. . DIRECTORY_SEPARATOR . Config::getCurrentPhpName()
  81. . DIRECTORY_SEPARATOR . 'ext'
  82. . DIRECTORY_SEPARATOR . $packageName;
  83. }
  84. foreach ($lookupDirs as $lookupDir) {
  85. if (!file_exists($lookupDir)) {
  86. continue;
  87. }
  88. if ($ext = self::createFromDirectory($packageName, $lookupDir)) {
  89. return $ext;
  90. }
  91. /*
  92. * FOLLOW_SYMLINKS is available from 5.2.11, 5.3.1
  93. */
  94. $di = new RecursiveDirectoryIterator($lookupDir, RecursiveDirectoryIterator::SKIP_DOTS);
  95. $it = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
  96. /*
  97. * Search for config.m4 or config0.m4 and use them to determine
  98. * the directory of the extension's source, because it's not always
  99. * the root directory in the ext archive (example xhprof)
  100. */
  101. foreach ($it as $fileinfo) {
  102. if (!$fileinfo->isDir()) {
  103. continue;
  104. }
  105. if ($ext = self::createFromDirectory($packageName, $fileinfo->getPathName())) {
  106. return $ext;
  107. }
  108. }
  109. }
  110. }
  111. public static function lookup($packageName, array $lookupDirectories = array(), $fallback = true)
  112. {
  113. if ($fallback) {
  114. // Always push the PHP source directory to the end of the list for the fallback.
  115. $lookupDirectories[] = Config::getBuildDir() . '/' . Config::getCurrentPhpName() . '/ext';
  116. }
  117. foreach ($lookupDirectories as $lookupDir) {
  118. if (!file_exists($lookupDir)) {
  119. continue;
  120. }
  121. $extensionDir = $lookupDir . DIRECTORY_SEPARATOR . $packageName;
  122. if ($ext = self::createFromDirectory($packageName, $extensionDir)) {
  123. return $ext;
  124. }
  125. }
  126. return new Extension($packageName);
  127. }
  128. public static function createM4Extension($packageName, $m4Path)
  129. {
  130. if (!file_exists($m4Path)) {
  131. return;
  132. }
  133. $m4 = file_get_contents($m4Path);
  134. // PHP_NEW_EXTENSION(extname, sources [, shared [, sapi_class [, extra-cflags [, cxx [, zend_ext]]]]])
  135. if (
  136. preg_match('/PHP_NEW_EXTENSION \( \s*
  137. \[?
  138. (\w+) # The extension name
  139. \]?
  140. \s*,\s*
  141. \[?
  142. ([^,]*) # Source files
  143. \]?
  144. (?:
  145. \s*,\s* ([^,\)]*) # Ext Shared
  146. (?:
  147. \s*,\s* ([^,\)]*) # SAPI class
  148. (?:
  149. \s*,\s* ([^,\)]*) # Extra cflags
  150. (?:
  151. \s*,\s* ([^,\)]*) # CXX
  152. \s*,\s* ([^,\)]*) # zend extension
  153. )?
  154. )?
  155. )?
  156. )?
  157. /x', $m4, $matches)
  158. ) {
  159. array_shift($matches);
  160. $ext = new M4Extension($packageName);
  161. $ext->setExtensionName($matches[0]);
  162. $ext->setSharedLibraryName($matches[0] . '.so');
  163. if (isset($matches[6]) && strpos($matches[6], 'yes') !== false) {
  164. $ext->setZend(true);
  165. }
  166. $ext->setSourceDirectory(dirname($m4Path));
  167. /*
  168. PHP_ARG_ENABLE(calendar,whether to enable calendar conversion support,
  169. [ --enable-calendar Enable support for calendar conversion])
  170. */
  171. if (
  172. preg_match_all('/
  173. PHP_ARG_ENABLE\(
  174. \s*([^,]*)
  175. (?:
  176. \s*,\s*
  177. (
  178. [^,\)]*
  179. )
  180. (?:
  181. \s*,\s*
  182. \[
  183. \s*
  184. ([^\s]+)
  185. \s+
  186. ([^,\)]*)
  187. \s*
  188. \]
  189. )?
  190. )?/x', $m4, $allMatches)
  191. ) {
  192. for ($i = 0; $i < count($allMatches[0]); ++$i) {
  193. $name = $allMatches[1][$i];
  194. $desc = $allMatches[2][$i];
  195. $option = $allMatches[3][$i];
  196. $optionDesc = $allMatches[4][$i];
  197. $ext->addConfigureOption(new ConfigureOption($option ?: '--enable-' . $name, $desc ?: $optionDesc));
  198. }
  199. }
  200. /*
  201. PHP_ARG_WITH(gd, for GD support,
  202. [ --with-gd[=DIR] Include GD support. DIR is the GD library base
  203. install directory [BUNDLED]])
  204. Possible option formats:
  205. --with-libxml-dir=DIR
  206. --with-recode[=DIR]
  207. --with-yaml[[=DIR]]
  208. --with-mysql-sock[=SOCKPATH]
  209. */
  210. if (
  211. preg_match_all('/
  212. PHP_ARG_WITH\(
  213. \s*
  214. ([^,]*)
  215. (?:
  216. \s*,\s*
  217. \[?
  218. ([^,\)]*)
  219. \]?
  220. (?:
  221. \s*,\s*
  222. \[
  223. \s*
  224. # simple match (\S+)
  225. ([a-zA-Z0-9-]+) # option
  226. (?:
  227. =?
  228. \[?
  229. =?([^\s\]]*?)
  230. \]?
  231. )? # option value hint
  232. \s+
  233. ([^,\)]*) # option description
  234. \s*
  235. \]
  236. (?:
  237. \s*,\s*
  238. ([^,\)]*)
  239. (?:
  240. \s*,\s*
  241. ([^,\)]*)
  242. )?
  243. )?
  244. )?
  245. )?/x', $m4, $allMatches)
  246. ) {
  247. // Parsing the M4 statement:
  248. //
  249. // dnl PHP_ARG_WITH(arg-name, check message, help text[, default-val[, extension-or-not]])
  250. //
  251. for ($i = 0; $i < count($allMatches[0]); ++$i) {
  252. $name = $allMatches[1][$i];
  253. $desc = $allMatches[2][$i];
  254. $option = $allMatches[3][$i];
  255. $optionValueHint = $allMatches[4][$i];
  256. $optionDesc = $allMatches[5][$i];
  257. $defaultValue = $allMatches[6][$i];
  258. $opt = new ConfigureOption(
  259. ($option ?: '--with-' . $name),
  260. ($desc ?: $optionDesc),
  261. $optionValueHint
  262. );
  263. if ($defaultValue) {
  264. $opt->setDefaultValue($opt);
  265. }
  266. $ext->addConfigureOption($opt);
  267. }
  268. }
  269. return $ext;
  270. } else {
  271. throw new Exception("Can not parse config.m4: $m4Path");
  272. }
  273. }
  274. public static function createPeclExtension($packageName, $packageXmlPath)
  275. {
  276. $parser = new PackageXmlParser();
  277. $package = $parser->parse($packageXmlPath);
  278. $ext = new PeclExtension($packageName);
  279. $ext->setPackage($package);
  280. /*
  281. * xhprof stores package.xml in the root directory, but putting the
  282. * config.m4 in the extension directory.
  283. * the path can be retrieve from the contents part from the package.xml
  284. */
  285. if ($m4path = $ext->findConfigM4FileFromPackageXml()) {
  286. $sourceDirectory = dirname($packageXmlPath);
  287. $m4dir = dirname($m4path);
  288. if ($m4dir != '.') {
  289. $sourceDirectory .= DIRECTORY_SEPARATOR . $m4dir;
  290. }
  291. $ext->setSourceDirectory($sourceDirectory);
  292. } else {
  293. $ext->setSourceDirectory(dirname($packageXmlPath));
  294. }
  295. return $ext;
  296. }
  297. }