Phing.php 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372
  1. <?php
  2. /*
  3. * $Id: Phing.php 905 2010-10-05 16:28:03Z mrook $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. require_once 'phing/Project.php';
  22. require_once 'phing/ProjectComponent.php';
  23. require_once 'phing/Target.php';
  24. require_once 'phing/Task.php';
  25. include_once 'phing/BuildException.php';
  26. include_once 'phing/ConfigurationException.php';
  27. include_once 'phing/BuildEvent.php';
  28. include_once 'phing/parser/Location.php';
  29. include_once 'phing/parser/ExpatParser.php';
  30. include_once 'phing/parser/AbstractHandler.php';
  31. include_once 'phing/parser/ProjectConfigurator.php';
  32. include_once 'phing/parser/RootHandler.php';
  33. include_once 'phing/parser/ProjectHandler.php';
  34. include_once 'phing/parser/TaskHandler.php';
  35. include_once 'phing/parser/TargetHandler.php';
  36. include_once 'phing/parser/DataTypeHandler.php';
  37. include_once 'phing/parser/NestedElementHandler.php';
  38. include_once 'phing/system/util/Properties.php';
  39. include_once 'phing/util/StringHelper.php';
  40. include_once 'phing/system/io/PhingFile.php';
  41. include_once 'phing/system/io/OutputStream.php';
  42. include_once 'phing/system/io/FileOutputStream.php';
  43. include_once 'phing/system/io/FileReader.php';
  44. include_once 'phing/system/util/Register.php';
  45. /**
  46. * Entry point into Phing. This class handles the full lifecycle of a build -- from
  47. * parsing & handling commandline arguments to assembling the project to shutting down
  48. * and cleaning up in the end.
  49. *
  50. * If you are invoking Phing from an external application, this is still
  51. * the class to use. Your applicaiton can invoke the start() method, passing
  52. * any commandline arguments or additional properties.
  53. *
  54. * @author Andreas Aderhold <andi@binarycloud.com>
  55. * @author Hans Lellelid <hans@xmpl.org>
  56. * @version $Revision: 905 $
  57. * @package phing
  58. */
  59. class Phing {
  60. /** The default build file name */
  61. const DEFAULT_BUILD_FILENAME = "build.xml";
  62. /** Our current message output status. Follows Project::MSG_XXX */
  63. private static $msgOutputLevel = Project::MSG_INFO;
  64. /** PhingFile that we are using for configuration */
  65. private $buildFile = null;
  66. /** The build targets */
  67. private $targets = array();
  68. /**
  69. * Set of properties that are passed in from commandline or invoking code.
  70. * @var Properties
  71. */
  72. private static $definedProps;
  73. /** Names of classes to add as listeners to project */
  74. private $listeners = array();
  75. private $loggerClassname = null;
  76. /** The class to handle input (can be only one). */
  77. private $inputHandlerClassname;
  78. /** Indicates if this phing should be run */
  79. private $readyToRun = false;
  80. /** Indicates we should only parse and display the project help information */
  81. private $projectHelp = false;
  82. /** Used by utility function getResourcePath() */
  83. private static $importPaths;
  84. /** System-wide static properties (moved from System) */
  85. private static $properties = array();
  86. /** Static system timer. */
  87. private static $timer;
  88. /** The current Project */
  89. private static $currentProject;
  90. /** Whether to capture PHP errors to buffer. */
  91. private static $phpErrorCapture = false;
  92. /** Array of captured PHP errors */
  93. private static $capturedPhpErrors = array();
  94. /**
  95. * @var OUtputStream Stream for standard output.
  96. */
  97. private static $out;
  98. /**
  99. * @var OutputStream Stream for error output.
  100. */
  101. private static $err;
  102. /**
  103. * @var boolean Whether we are using a logfile.
  104. */
  105. private static $isLogFileUsed = false;
  106. /**
  107. * Array to hold original ini settings that Phing changes (and needs
  108. * to restore in restoreIni() method).
  109. *
  110. * @var array Struct of array(setting-name => setting-value)
  111. * @see restoreIni()
  112. */
  113. private static $origIniSettings = array();
  114. /**
  115. * Entry point allowing for more options from other front ends.
  116. *
  117. * This method encapsulates the complete build lifecycle.
  118. *
  119. * @param array $args The commandline args passed to phing shell script.
  120. * @param array $additionalUserProperties Any additional properties to be passed to Phing (alternative front-end might implement this).
  121. * These additional properties will be available using the getDefinedProperty() method and will
  122. * be added to the project's "user" properties
  123. * @see execute()
  124. * @see runBuild()
  125. * @throws Exception - if there is an error during build
  126. */
  127. public static function start($args, array $additionalUserProperties = null) {
  128. try {
  129. $m = new Phing();
  130. $m->execute($args);
  131. } catch (Exception $exc) {
  132. self::handleLogfile();
  133. throw $exc;
  134. }
  135. if ($additionalUserProperties !== null) {
  136. foreach($additionalUserProperties as $key => $value) {
  137. $m->setDefinedProperty($key, $value);
  138. }
  139. }
  140. try {
  141. $m->runBuild();
  142. } catch(Exception $exc) {
  143. self::handleLogfile();
  144. throw $exc;
  145. }
  146. // everything fine, shutdown
  147. self::handleLogfile();
  148. }
  149. /**
  150. * Prints the message of the Exception if it's not null.
  151. * @param Exception $t
  152. */
  153. public static function printMessage(Exception $t) {
  154. if (self::$err === null) { // Make sure our error output is initialized
  155. self::initializeOutputStreams();
  156. }
  157. if (self::getMsgOutputLevel() >= Project::MSG_VERBOSE) {
  158. self::$err->write($t->__toString() . PHP_EOL);
  159. } else {
  160. self::$err->write($t->getMessage() . PHP_EOL);
  161. }
  162. }
  163. /**
  164. * Sets the stdout and stderr streams if they are not already set.
  165. */
  166. private static function initializeOutputStreams() {
  167. if (self::$out === null) {
  168. self::$out = new OutputStream(fopen("php://stdout", "w"));
  169. }
  170. if (self::$err === null) {
  171. self::$err = new OutputStream(fopen("php://stderr", "w"));
  172. }
  173. }
  174. /**
  175. * Sets the stream to use for standard (non-error) output.
  176. * @param OutputStream $stream The stream to use for standard output.
  177. */
  178. public static function setOutputStream(OutputStream $stream) {
  179. self::$out = $stream;
  180. }
  181. /**
  182. * Gets the stream to use for standard (non-error) output.
  183. * @return OutputStream
  184. */
  185. public static function getOutputStream() {
  186. return self::$out;
  187. }
  188. /**
  189. * Sets the stream to use for error output.
  190. * @param OutputStream $stream The stream to use for error output.
  191. */
  192. public static function setErrorStream(OutputStream $stream) {
  193. self::$err = $stream;
  194. }
  195. /**
  196. * Gets the stream to use for error output.
  197. * @return OutputStream
  198. */
  199. public static function getErrorStream() {
  200. return self::$err;
  201. }
  202. /**
  203. * Close logfiles, if we have been writing to them.
  204. *
  205. * @since Phing 2.3.0
  206. */
  207. private static function handleLogfile() {
  208. if (self::$isLogFileUsed) {
  209. self::$err->close();
  210. self::$out->close();
  211. }
  212. }
  213. /**
  214. * Making output level a static property so that this property
  215. * can be accessed by other parts of the system, enabling
  216. * us to display more information -- e.g. backtraces -- for "debug" level.
  217. * @return int
  218. */
  219. public static function getMsgOutputLevel() {
  220. return self::$msgOutputLevel;
  221. }
  222. /**
  223. * Command line entry point. This method kicks off the building
  224. * of a project object and executes a build using either a given
  225. * target or the default target.
  226. *
  227. * @param array $args Command line args.
  228. * @return void
  229. */
  230. public static function fire($args) {
  231. self::start($args, null);
  232. }
  233. /**
  234. * Setup/initialize Phing environment from commandline args.
  235. * @param array $args commandline args passed to phing shell.
  236. * @return void
  237. */
  238. public function execute($args) {
  239. self::$definedProps = new Properties();
  240. $this->searchForThis = null;
  241. // 1) First handle any options which should always
  242. // Note: The order in which these are executed is important (if multiple of these options are specified)
  243. if (in_array('-help', $args) || in_array('-h', $args)) {
  244. $this->printUsage();
  245. return;
  246. }
  247. if (in_array('-version', $args) || in_array('-v', $args)) {
  248. $this->printVersion();
  249. return;
  250. }
  251. // 2) Next pull out stand-alone args.
  252. // Note: The order in which these are executed is important (if multiple of these options are specified)
  253. if (false !== ($key = array_search('-quiet', $args, true))) {
  254. self::$msgOutputLevel = Project::MSG_WARN;
  255. unset($args[$key]);
  256. }
  257. if (false !== ($key = array_search('-verbose', $args, true))) {
  258. self::$msgOutputLevel = Project::MSG_VERBOSE;
  259. unset($args[$key]);
  260. }
  261. if (false !== ($key = array_search('-debug', $args, true))) {
  262. self::$msgOutputLevel = Project::MSG_DEBUG;
  263. unset($args[$key]);
  264. }
  265. // 3) Finally, cycle through to parse remaining args
  266. //
  267. $keys = array_keys($args); // Use keys and iterate to max(keys) since there may be some gaps
  268. $max = $keys ? max($keys) : -1;
  269. for($i=0; $i <= $max; $i++) {
  270. if (!array_key_exists($i, $args)) {
  271. // skip this argument, since it must have been removed above.
  272. continue;
  273. }
  274. $arg = $args[$i];
  275. if ($arg == "-logfile") {
  276. try {
  277. // see: http://phing.info/trac/ticket/65
  278. if (!isset($args[$i+1])) {
  279. $msg = "You must specify a log file when using the -logfile argument\n";
  280. throw new ConfigurationException($msg);
  281. } else {
  282. $logFile = new PhingFile($args[++$i]);
  283. $out = new FileOutputStream($logFile); // overwrite
  284. self::setOutputStream($out);
  285. self::setErrorStream($out);
  286. self::$isLogFileUsed = true;
  287. }
  288. } catch (IOException $ioe) {
  289. $msg = "Cannot write on the specified log file. Make sure the path exists and you have write permissions.";
  290. throw new ConfigurationException($msg, $ioe);
  291. }
  292. } elseif ($arg == "-buildfile" || $arg == "-file" || $arg == "-f") {
  293. if (!isset($args[$i+1])) {
  294. $msg = "You must specify a buildfile when using the -buildfile argument.";
  295. throw new ConfigurationException($msg);
  296. } else {
  297. $this->buildFile = new PhingFile($args[++$i]);
  298. }
  299. } elseif ($arg == "-listener") {
  300. if (!isset($args[$i+1])) {
  301. $msg = "You must specify a listener class when using the -listener argument";
  302. throw new ConfigurationException($msg);
  303. } else {
  304. $this->listeners[] = $args[++$i];
  305. }
  306. } elseif (StringHelper::startsWith("-D", $arg)) {
  307. $name = substr($arg, 2);
  308. $value = null;
  309. $posEq = strpos($name, "=");
  310. if ($posEq !== false) {
  311. $value = substr($name, $posEq+1);
  312. $name = substr($name, 0, $posEq);
  313. } elseif ($i < count($args)-1 && !StringHelper::startsWith("-D", $arg)) {
  314. $value = $args[++$i];
  315. }
  316. self::$definedProps->setProperty($name, $value);
  317. } elseif ($arg == "-logger") {
  318. if (!isset($args[$i+1])) {
  319. $msg = "You must specify a classname when using the -logger argument";
  320. throw new ConfigurationException($msg);
  321. } else {
  322. $this->loggerClassname = $args[++$i];
  323. }
  324. } elseif ($arg == "-inputhandler") {
  325. if ($this->inputHandlerClassname !== null) {
  326. throw new ConfigurationException("Only one input handler class may be specified.");
  327. }
  328. if (!isset($args[$i+1])) {
  329. $msg = "You must specify a classname when using the -inputhandler argument";
  330. throw new ConfigurationException($msg);
  331. } else {
  332. $this->inputHandlerClassname = $args[++$i];
  333. }
  334. } elseif ($arg == "-longtargets") {
  335. self::$definedProps->setProperty('phing.showlongtargets', 1);
  336. } elseif ($arg == "-projecthelp" || $arg == "-targets" || $arg == "-list" || $arg == "-l" || $arg == "-p") {
  337. // set the flag to display the targets and quit
  338. $this->projectHelp = true;
  339. } elseif ($arg == "-find") {
  340. // eat up next arg if present, default to build.xml
  341. if ($i < count($args)-1) {
  342. $this->searchForThis = $args[++$i];
  343. } else {
  344. $this->searchForThis = self::DEFAULT_BUILD_FILENAME;
  345. }
  346. } elseif (substr($arg,0,1) == "-") {
  347. // we don't have any more args
  348. self::$err->write("Unknown argument: $arg" . PHP_EOL);
  349. self::printUsage();
  350. return;
  351. } else {
  352. // if it's no other arg, it may be the target
  353. array_push($this->targets, $arg);
  354. }
  355. }
  356. // if buildFile was not specified on the command line,
  357. if ($this->buildFile === null) {
  358. // but -find then search for it
  359. if ($this->searchForThis !== null) {
  360. $this->buildFile = $this->_findBuildFile(self::getProperty("user.dir"), $this->searchForThis);
  361. } else {
  362. $this->buildFile = new PhingFile(self::DEFAULT_BUILD_FILENAME);
  363. }
  364. }
  365. // make sure buildfile exists
  366. if (!$this->buildFile->exists()) {
  367. throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " does not exist!");
  368. }
  369. // make sure it's not a directory
  370. if ($this->buildFile->isDirectory()) {
  371. throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " is a dir!");
  372. }
  373. $this->readyToRun = true;
  374. }
  375. /**
  376. * Helper to get the parent file for a given file.
  377. *
  378. * @param PhingFile $file
  379. * @return PhingFile Parent file or null if none
  380. */
  381. private function _getParentFile(PhingFile $file) {
  382. $filename = $file->getAbsolutePath();
  383. $file = new PhingFile($filename);
  384. $filename = $file->getParent();
  385. return ($filename === null) ? null : new PhingFile($filename);
  386. }
  387. /**
  388. * Search parent directories for the build file.
  389. *
  390. * Takes the given target as a suffix to append to each
  391. * parent directory in search of a build file. Once the
  392. * root of the file-system has been reached an exception
  393. * is thrown.
  394. *
  395. * @param string $start Start file path.
  396. * @param string $suffix Suffix filename to look for in parents.
  397. * @return PhingFile A handle to the build file
  398. *
  399. * @throws BuildException Failed to locate a build file
  400. */
  401. private function _findBuildFile($start, $suffix) {
  402. $startf = new PhingFile($start);
  403. $parent = new PhingFile($startf->getAbsolutePath());
  404. $file = new PhingFile($parent, $suffix);
  405. // check if the target file exists in the current directory
  406. while (!$file->exists()) {
  407. // change to parent directory
  408. $parent = $this->_getParentFile($parent);
  409. // if parent is null, then we are at the root of the fs,
  410. // complain that we can't find the build file.
  411. if ($parent === null) {
  412. throw new ConfigurationException("Could not locate a build file!");
  413. }
  414. // refresh our file handle
  415. $file = new PhingFile($parent, $suffix);
  416. }
  417. return $file;
  418. }
  419. /**
  420. * Executes the build.
  421. * @return void
  422. */
  423. function runBuild() {
  424. if (!$this->readyToRun) {
  425. return;
  426. }
  427. $project = new Project();
  428. self::setCurrentProject($project);
  429. set_error_handler(array('Phing', 'handlePhpError'));
  430. $error = null;
  431. $this->addBuildListeners($project);
  432. $this->addInputHandler($project);
  433. // set this right away, so that it can be used in logging.
  434. $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
  435. try {
  436. $project->fireBuildStarted();
  437. $project->init();
  438. } catch (Exception $exc) {
  439. $project->fireBuildFinished($exc);
  440. throw $exc;
  441. }
  442. $project->setUserProperty("phing.version", $this->getPhingVersion());
  443. $e = self::$definedProps->keys();
  444. while (count($e)) {
  445. $arg = (string) array_shift($e);
  446. $value = (string) self::$definedProps->getProperty($arg);
  447. $project->setUserProperty($arg, $value);
  448. }
  449. unset($e);
  450. $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
  451. // first use the Configurator to create the project object
  452. // from the given build file.
  453. try {
  454. ProjectConfigurator::configureProject($project, $this->buildFile);
  455. } catch (Exception $exc) {
  456. $project->fireBuildFinished($exc);
  457. restore_error_handler();
  458. self::unsetCurrentProject();
  459. throw $exc;
  460. }
  461. // make sure that we have a target to execute
  462. if (count($this->targets) === 0) {
  463. $this->targets[] = $project->getDefaultTarget();
  464. }
  465. // make sure that minimum required phing version is satisfied
  466. try {
  467. $this->comparePhingVersion($project->getPhingVersion());
  468. } catch(Exception $exc) {
  469. $project->fireBuildFinished($exc);
  470. restore_error_handler();
  471. self::unsetCurrentProject();
  472. throw $exc;
  473. }
  474. // execute targets if help param was not given
  475. if (!$this->projectHelp) {
  476. try {
  477. $project->executeTargets($this->targets);
  478. } catch (Exception $exc) {
  479. $project->fireBuildFinished($exc);
  480. restore_error_handler();
  481. self::unsetCurrentProject();
  482. throw $exc;
  483. }
  484. }
  485. // if help is requested print it
  486. if ($this->projectHelp) {
  487. try {
  488. $this->printDescription($project);
  489. $this->printTargets($project);
  490. } catch (Exception $exc) {
  491. $project->fireBuildFinished($exc);
  492. restore_error_handler();
  493. self::unsetCurrentProject();
  494. throw $exc;
  495. }
  496. }
  497. // finally {
  498. if (!$this->projectHelp) {
  499. $project->fireBuildFinished(null);
  500. }
  501. restore_error_handler();
  502. self::unsetCurrentProject();
  503. }
  504. private function comparePhingVersion($version) {
  505. $current = strtolower(self::getPhingVersion());
  506. $current = trim(str_replace('phing', '', $current));
  507. // make sure that version checks are not applied to trunk
  508. if('dev' === $current) {
  509. return 1;
  510. }
  511. if(-1 == version_compare($current, $version)) {
  512. throw new BuildException(
  513. sprintf('Incompatible Phing version (%s). Version "%s" required.', $current, $version));
  514. }
  515. }
  516. /**
  517. * Bind any registered build listeners to this project.
  518. *
  519. * This means adding the logger and any build listeners that were specified
  520. * with -listener arg.
  521. *
  522. * @param Project $project
  523. * @return void
  524. */
  525. private function addBuildListeners(Project $project) {
  526. // Add the default listener
  527. $project->addBuildListener($this->createLogger());
  528. foreach($this->listeners as $listenerClassname) {
  529. try {
  530. $clz = Phing::import($listenerClassname);
  531. } catch (Exception $x) {
  532. $msg = "Unable to instantiate specified listener "
  533. . "class " . $listenerClassname . " : "
  534. . $e->getMessage();
  535. throw new ConfigurationException($msg);
  536. }
  537. $listener = new $clz();
  538. if ($listener instanceof StreamRequiredBuildLogger) {
  539. throw new ConfigurationException("Unable to add " . $listenerClassname . " as a listener, since it requires explicit error/output streams. (You can specify it as a -logger.)");
  540. }
  541. $project->addBuildListener($listener);
  542. }
  543. }
  544. /**
  545. * Creates the InputHandler and adds it to the project.
  546. *
  547. * @param Project $project the project instance.
  548. *
  549. * @throws BuildException if a specified InputHandler
  550. * class could not be loaded.
  551. */
  552. private function addInputHandler(Project $project) {
  553. if ($this->inputHandlerClassname === null) {
  554. $handler = new DefaultInputHandler();
  555. } else {
  556. try {
  557. $clz = Phing::import($this->inputHandlerClassname);
  558. $handler = new $clz();
  559. if ($project !== null && method_exists($handler, 'setProject')) {
  560. $handler->setProject($project);
  561. }
  562. } catch (Exception $e) {
  563. $msg = "Unable to instantiate specified input handler "
  564. . "class " . $this->inputHandlerClassname . " : "
  565. . $e->getMessage();
  566. throw new ConfigurationException($msg);
  567. }
  568. }
  569. $project->setInputHandler($handler);
  570. }
  571. /**
  572. * Creates the default build logger for sending build events to the log.
  573. * @return BuildLogger The created Logger
  574. */
  575. private function createLogger() {
  576. if ($this->loggerClassname !== null) {
  577. self::import($this->loggerClassname);
  578. // get class name part
  579. $classname = self::import($this->loggerClassname);
  580. $logger = new $classname;
  581. if (!($logger instanceof BuildLogger)) {
  582. throw new BuildException($classname . ' does not implement the BuildLogger interface.');
  583. }
  584. } else {
  585. require_once 'phing/listener/DefaultLogger.php';
  586. $logger = new DefaultLogger();
  587. }
  588. $logger->setMessageOutputLevel(self::$msgOutputLevel);
  589. $logger->setOutputStream(self::$out);
  590. $logger->setErrorStream(self::$err);
  591. return $logger;
  592. }
  593. /**
  594. * Sets the current Project
  595. * @param Project $p
  596. */
  597. public static function setCurrentProject($p) {
  598. self::$currentProject = $p;
  599. }
  600. /**
  601. * Unsets the current Project
  602. */
  603. public static function unsetCurrentProject() {
  604. self::$currentProject = null;
  605. }
  606. /**
  607. * Gets the current Project.
  608. * @return Project Current Project or NULL if none is set yet/still.
  609. */
  610. public static function getCurrentProject() {
  611. return self::$currentProject;
  612. }
  613. /**
  614. * A static convenience method to send a log to the current (last-setup) Project.
  615. * If there is no currently-configured Project, then this will do nothing.
  616. * @param string $message
  617. * @param int $priority Project::MSG_INFO, etc.
  618. */
  619. public static function log($message, $priority = Project::MSG_INFO) {
  620. $p = self::getCurrentProject();
  621. if ($p) {
  622. $p->log($message, $priority);
  623. }
  624. }
  625. /**
  626. * Error handler for PHP errors encountered during the build.
  627. * This uses the logging for the currently configured project.
  628. */
  629. public static function handlePhpError($level, $message, $file, $line) {
  630. // don't want to print supressed errors
  631. if (error_reporting() > 0) {
  632. if (self::$phpErrorCapture) {
  633. self::$capturedPhpErrors[] = array('message' => $message, 'level' => $level, 'line' => $line, 'file' => $file);
  634. } else {
  635. $message = '[PHP Error] ' . $message;
  636. $message .= ' [line ' . $line . ' of ' . $file . ']';
  637. switch ($level) {
  638. case 16384: // E_USER_DEPRECATED
  639. case 8192: // E_DEPRECATED
  640. case E_STRICT:
  641. case E_NOTICE:
  642. case E_USER_NOTICE:
  643. self::log($message, Project::MSG_VERBOSE);
  644. break;
  645. case E_WARNING:
  646. case E_USER_WARNING:
  647. self::log($message, Project::MSG_WARN);
  648. break;
  649. case E_ERROR:
  650. case E_USER_ERROR:
  651. default:
  652. self::log($message, Project::MSG_ERR);
  653. } // switch
  654. } // if phpErrorCapture
  655. } // if not @
  656. }
  657. /**
  658. * Begins capturing PHP errors to a buffer.
  659. * While errors are being captured, they are not logged.
  660. */
  661. public static function startPhpErrorCapture() {
  662. self::$phpErrorCapture = true;
  663. self::$capturedPhpErrors = array();
  664. }
  665. /**
  666. * Stops capturing PHP errors to a buffer.
  667. * The errors will once again be logged after calling this method.
  668. */
  669. public static function stopPhpErrorCapture() {
  670. self::$phpErrorCapture = false;
  671. }
  672. /**
  673. * Clears the captured errors without affecting the starting/stopping of the capture.
  674. */
  675. public static function clearCapturedPhpErrors() {
  676. self::$capturedPhpErrors = array();
  677. }
  678. /**
  679. * Gets any PHP errors that were captured to buffer.
  680. * @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level)
  681. */
  682. public static function getCapturedPhpErrors() {
  683. return self::$capturedPhpErrors;
  684. }
  685. /** Prints the usage of how to use this class */
  686. public static function printUsage() {
  687. $msg = "";
  688. $msg .= "phing [options] [target [target2 [target3] ...]]" . PHP_EOL;
  689. $msg .= "Options: " . PHP_EOL;
  690. $msg .= " -h -help print this message" . PHP_EOL;
  691. $msg .= " -l -list list available targets in this project" . PHP_EOL;
  692. $msg .= " -v -version print the version information and exit" . PHP_EOL;
  693. $msg .= " -q -quiet be extra quiet" . PHP_EOL;
  694. $msg .= " -verbose be extra verbose" . PHP_EOL;
  695. $msg .= " -debug print debugging information" . PHP_EOL;
  696. $msg .= " -longtargets show target descriptions during build" . PHP_EOL;
  697. $msg .= " -logfile <file> use given file for log" . PHP_EOL;
  698. $msg .= " -logger <classname> the class which is to perform logging" . PHP_EOL;
  699. $msg .= " -f -buildfile <file> use given buildfile" . PHP_EOL;
  700. $msg .= " -D<property>=<value> use value for given property" . PHP_EOL;
  701. $msg .= " -find <file> search for buildfile towards the root of the" . PHP_EOL;
  702. $msg .= " filesystem and use it" . PHP_EOL;
  703. $msg .= " -inputhandler <file> the class to use to handle user input" . PHP_EOL;
  704. //$msg .= " -recursive <file> search for buildfile downwards and use it" . PHP_EOL;
  705. $msg .= PHP_EOL;
  706. $msg .= "Report bugs to <dev@phing.tigris.org>".PHP_EOL;
  707. self::$err->write($msg);
  708. }
  709. /**
  710. * Prints the current Phing version.
  711. */
  712. public static function printVersion() {
  713. self::$out->write(self::getPhingVersion().PHP_EOL);
  714. }
  715. /**
  716. * Gets the current Phing version based on VERSION.TXT file.
  717. * @return string
  718. * @throws BuildException - if unable to find version file.
  719. */
  720. public static function getPhingVersion() {
  721. $versionPath = self::getResourcePath("phing/etc/VERSION.TXT");
  722. if ($versionPath === null) {
  723. $versionPath = self::getResourcePath("etc/VERSION.TXT");
  724. }
  725. if ($versionPath === null) {
  726. throw new ConfigurationException("No VERSION.TXT file found; try setting phing.home environment variable.");
  727. }
  728. try { // try to read file
  729. $buffer = null;
  730. $file = new PhingFile($versionPath);
  731. $reader = new FileReader($file);
  732. $reader->readInto($buffer);
  733. $buffer = trim($buffer);
  734. //$buffer = "PHING version 1.0, Released 2002-??-??";
  735. $phingVersion = $buffer;
  736. } catch (IOException $iox) {
  737. throw new ConfigurationException("Can't read version information file");
  738. }
  739. return $phingVersion;
  740. }
  741. /**
  742. * Print the project description, if any
  743. */
  744. public static function printDescription(Project $project) {
  745. if ($project->getDescription() !== null) {
  746. self::$out->write($project->getDescription() . PHP_EOL);
  747. }
  748. }
  749. /** Print out a list of all targets in the current buildfile */
  750. function printTargets($project) {
  751. // find the target with the longest name
  752. $maxLength = 0;
  753. $targets = $project->getTargets();
  754. $targetNames = array_keys($targets);
  755. $targetName = null;
  756. $targetDescription = null;
  757. $currentTarget = null;
  758. // split the targets in top-level and sub-targets depending
  759. // on the presence of a description
  760. $subNames = array();
  761. $topNameDescMap = array();
  762. foreach($targets as $currentTarget) {
  763. $targetName = $currentTarget->getName();
  764. $targetDescription = $currentTarget->getDescription();
  765. // subtargets are targets w/o descriptions
  766. if ($targetDescription === null) {
  767. $subNames[] = $targetName;
  768. } else {
  769. // topNames and topDescriptions are handled later
  770. // here we store in hash map (for sorting purposes)
  771. $topNameDescMap[$targetName] = $targetDescription;
  772. if (strlen($targetName) > $maxLength) {
  773. $maxLength = strlen($targetName);
  774. }
  775. }
  776. }
  777. // Sort the arrays
  778. sort($subNames); // sort array values, resetting keys (which are numeric)
  779. ksort($topNameDescMap); // sort the keys (targetName) keeping key=>val associations
  780. $topNames = array_keys($topNameDescMap);
  781. $topDescriptions = array_values($topNameDescMap);
  782. $defaultTarget = $project->getDefaultTarget();
  783. if ($defaultTarget !== null && $defaultTarget !== "") {
  784. $defaultName = array();
  785. $defaultDesc = array();
  786. $defaultName[] = $defaultTarget;
  787. $indexOfDefDesc = array_search($defaultTarget, $topNames, true);
  788. if ($indexOfDefDesc !== false && $indexOfDefDesc >= 0) {
  789. $defaultDesc = array();
  790. $defaultDesc[] = $topDescriptions[$indexOfDefDesc];
  791. }
  792. $this->_printTargets($defaultName, $defaultDesc, "Default target:", $maxLength);
  793. }
  794. $this->_printTargets($topNames, $topDescriptions, "Main targets:", $maxLength);
  795. $this->_printTargets($subNames, null, "Subtargets:", 0);
  796. }
  797. /**
  798. * Writes a formatted list of target names with an optional description.
  799. *
  800. * @param array $names The names to be printed.
  801. * Must not be <code>null</code>.
  802. * @param array $descriptions The associated target descriptions.
  803. * May be <code>null</code>, in which case
  804. * no descriptions are displayed.
  805. * If non-<code>null</code>, this should have
  806. * as many elements as <code>names</code>.
  807. * @param string $heading The heading to display.
  808. * Should not be <code>null</code>.
  809. * @param int $maxlen The maximum length of the names of the targets.
  810. * If descriptions are given, they are padded to this
  811. * position so they line up (so long as the names really
  812. * <i>are</i> shorter than this).
  813. */
  814. private function _printTargets($names, $descriptions, $heading, $maxlen) {
  815. $spaces = ' ';
  816. while (strlen($spaces) < $maxlen) {
  817. $spaces .= $spaces;
  818. }
  819. $msg = "";
  820. $msg .= $heading . PHP_EOL;
  821. $msg .= str_repeat("-",79) . PHP_EOL;
  822. $total = count($names);
  823. for($i=0; $i < $total; $i++) {
  824. $msg .= " ";
  825. $msg .= $names[$i];
  826. if (!empty($descriptions)) {
  827. $msg .= substr($spaces, 0, $maxlen - strlen($names[$i]) + 2);
  828. $msg .= $descriptions[$i];
  829. }
  830. $msg .= PHP_EOL;
  831. }
  832. if ($total > 0) {
  833. self::$out->write($msg . PHP_EOL);
  834. }
  835. }
  836. /**
  837. * Import a dot-path notation class path.
  838. * @param string $dotPath
  839. * @param mixed $classpath String or object supporting __toString()
  840. * @return string The unqualified classname (which can be instantiated).
  841. * @throws BuildException - if cannot find the specified file
  842. */
  843. public static function import($dotPath, $classpath = null) {
  844. /// check if this is a PEAR-style path (@see http://pear.php.net/manual/en/standards.naming.php)
  845. if (strpos($dotPath, '.') === false && strpos($dotPath, '_') !== false) {
  846. $classname = $dotPath;
  847. $dotPath = str_replace('_', '.', $dotPath);
  848. } else {
  849. $classname = StringHelper::unqualify($dotPath);
  850. }
  851. // first check to see that the class specified hasn't already been included.
  852. // (this also handles case where this method is called w/ a classname rather than dotpath)
  853. if (class_exists($classname, false)) {
  854. return $classname;
  855. }
  856. $dotClassname = basename($dotPath);
  857. $dotClassnamePos = strlen($dotPath) - strlen($dotClassname);
  858. // 1- temporarily replace escaped '.' with another illegal char (#)
  859. $tmp = str_replace('\.', '##', $dotClassname);
  860. // 2- swap out the remaining '.' with DIR_SEP
  861. $tmp = strtr($tmp, '.', DIRECTORY_SEPARATOR);
  862. // 3- swap back the escaped '.'
  863. $tmp = str_replace('##', '.', $tmp);
  864. $classFile = $tmp . ".php";
  865. $path = substr_replace($dotPath, $classFile, $dotClassnamePos);
  866. Phing::__import($path, $classpath);
  867. return $classname;
  868. }
  869. /**
  870. * Import a PHP file
  871. * @param string $path Path to the PHP file
  872. * @param mixed $classpath String or object supporting __toString()
  873. * @throws BuildException - if cannot find the specified file
  874. */
  875. public static function __import($path, $classpath = null) {
  876. if ($classpath) {
  877. // Apparently casting to (string) no longer invokes __toString() automatically.
  878. if (is_object($classpath)) {
  879. $classpath = $classpath->__toString();
  880. }
  881. // classpaths are currently additive, but we also don't want to just
  882. // indiscriminantly prepand/append stuff to the include_path. This means
  883. // we need to parse current incldue_path, and prepend any
  884. // specified classpath locations that are not already in the include_path.
  885. //
  886. // NOTE: the reason why we do it this way instead of just changing include_path
  887. // and then changing it back, is that in many cases applications (e.g. Propel) will
  888. // include/require class files from within method calls. This means that not all
  889. // necessary files will be included in this import() call, and hence we can't
  890. // change the include_path back without breaking those apps. While this method could
  891. // be more expensive than switching & switching back (not sure, but maybe), it makes it
  892. // possible to write far less expensive run-time applications (e.g. using Propel), which is
  893. // really where speed matters more.
  894. $curr_parts = explode(PATH_SEPARATOR, get_include_path());
  895. $add_parts = explode(PATH_SEPARATOR, $classpath);
  896. $new_parts = array_diff($add_parts, $curr_parts);
  897. if ($new_parts) {
  898. set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts)));
  899. }
  900. }
  901. $ret = include_once($path);
  902. if ($ret === false) {
  903. $msg = "Error importing $path";
  904. if (self::getMsgOutputLevel() >= Project::MSG_DEBUG) {
  905. $x = new Exception("for-path-trace-only");
  906. $msg .= $x->getTraceAsString();
  907. }
  908. throw new ConfigurationException($msg);
  909. }
  910. }
  911. /**
  912. * Looks on include path for specified file.
  913. * @return string File found (null if no file found).
  914. */
  915. public static function getResourcePath($path) {
  916. if (self::$importPaths === null) {
  917. $paths = get_include_path();
  918. self::$importPaths = explode(PATH_SEPARATOR, ini_get("include_path"));
  919. }
  920. $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
  921. $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
  922. foreach (self::$importPaths as $prefix) {
  923. $testPath = $prefix . DIRECTORY_SEPARATOR . $path;
  924. if (file_exists($testPath)) {
  925. return $testPath;
  926. }
  927. }
  928. // Check for the property phing.home
  929. $homeDir = self::getProperty('phing.home');
  930. if ($homeDir) {
  931. $testPath = $homeDir . DIRECTORY_SEPARATOR . $path;
  932. if (file_exists($testPath)) {
  933. return $testPath;
  934. }
  935. }
  936. // If we are using this via PEAR then check for the file in the data dir
  937. // This is a bit of a hack, but works better than previous solution of assuming
  938. // data_dir is on the include_path.
  939. $dataDir = '/usr/share/php/data';
  940. if ($dataDir{0} != '@') { // if we're using PEAR then the @ DATA-DIR @ token will have been substituted.
  941. $testPath = $dataDir . DIRECTORY_SEPARATOR . $path;
  942. if (file_exists($testPath)) {
  943. return $testPath;
  944. }
  945. } else {
  946. // We're not using PEAR, so do one additional check based on path of
  947. // current file (Phing.php)
  948. $maybeHomeDir = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..');
  949. $testPath = $maybeHomeDir . DIRECTORY_SEPARATOR . $path;
  950. if (file_exists($testPath)) {
  951. return $testPath;
  952. }
  953. }
  954. return null;
  955. }
  956. // -------------------------------------------------------------------------------------------
  957. // System-wide methods (moved from System class, which had namespace conflicts w/ PEAR System)
  958. // -------------------------------------------------------------------------------------------
  959. /**
  960. * Set System constants which can be retrieved by calling Phing::getProperty($propName).
  961. * @return void
  962. */
  963. private static function setSystemConstants() {
  964. /*
  965. * PHP_OS returns on
  966. * WindowsNT4.0sp6 => WINNT
  967. * Windows2000 => WINNT
  968. * Windows ME => WIN32
  969. * Windows 98SE => WIN32
  970. * FreeBSD 4.5p7 => FreeBSD
  971. * Redhat Linux => Linux
  972. * Mac OS X => Darwin
  973. */
  974. self::setProperty('host.os', PHP_OS);
  975. // this is used by some tasks too
  976. self::setProperty('os.name', PHP_OS);
  977. // it's still possible this won't be defined,
  978. // e.g. if Phing is being included in another app w/o
  979. // using the phing.php script.
  980. if (!defined('PHP_CLASSPATH')) {
  981. define('PHP_CLASSPATH', get_include_path());
  982. }
  983. self::setProperty('php.classpath', PHP_CLASSPATH);
  984. // try to determine the host filesystem and set system property
  985. // used by Fileself::getFileSystem to instantiate the correct
  986. // abstraction layer
  987. switch (strtoupper(PHP_OS)) {
  988. case 'WINNT':
  989. self::setProperty('host.fstype', 'WINNT');
  990. self::setProperty('php.interpreter', getenv('PHP_COMMAND'));
  991. break;
  992. case 'WIN32':
  993. self::setProperty('host.fstype', 'WIN32');
  994. break;
  995. default:
  996. self::setProperty('host.fstype', 'UNIX');
  997. break;
  998. }
  999. self::setProperty('line.separator', PHP_EOL);
  1000. self::setProperty('php.version', PHP_VERSION);
  1001. self::setProperty('user.home', getenv('HOME'));
  1002. self::setProperty('application.startdir', getcwd());
  1003. self::setProperty('phing.startTime', gmdate('D, d M Y H:i:s', time()) . ' GMT');
  1004. // try to detect machine dependent information
  1005. $sysInfo = array();
  1006. if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && function_exists("posix_uname")) {
  1007. $sysInfo = posix_uname();
  1008. } else {
  1009. $sysInfo['nodename'] = php_uname('n');
  1010. $sysInfo['machine']= php_uname('m') ;
  1011. //this is a not so ideal substition, but maybe better than nothing
  1012. $sysInfo['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "unknown";
  1013. $sysInfo['release'] = php_uname('r');
  1014. $sysInfo['version'] = php_uname('v');
  1015. }
  1016. self::setProperty("host.name", isset($sysInfo['nodename']) ? $sysInfo['nodename'] : "unknown");
  1017. self::setProperty("host.arch", isset($sysInfo['machine']) ? $sysInfo['machine'] : "unknown");
  1018. self::setProperty("host.domain",isset($sysInfo['domain']) ? $sysInfo['domain'] : "unknown");
  1019. self::setProperty("host.os.release", isset($sysInfo['release']) ? $sysInfo['release'] : "unknown");
  1020. self::setProperty("host.os.version", isset($sysInfo['version']) ? $sysInfo['version'] : "unknown");
  1021. unset($sysInfo);
  1022. }
  1023. /**
  1024. * This gets a property that was set via command line or otherwise passed into Phing.
  1025. * "Defined" in this case means "externally defined". The reason this method exists is to
  1026. * provide a public means of accessing commandline properties for (e.g.) logger or listener
  1027. * scripts. E.g. to specify which logfile to use, PearLogger needs to be able to access
  1028. * the pear.log.name property.
  1029. *
  1030. * @param string $name
  1031. * @return string value of found property (or null, if none found).
  1032. */
  1033. public static function getDefinedProperty($name) {
  1034. return self::$definedProps->getProperty($name);
  1035. }
  1036. /**
  1037. * This sets a property that was set via command line or otherwise passed into Phing.
  1038. *
  1039. * @param string $name
  1040. * @return string value of found property (or null, if none found).
  1041. */
  1042. public static function setDefinedProperty($name, $value) {
  1043. return self::$definedProps->setProperty($name, $value);
  1044. }
  1045. /**
  1046. * Returns property value for a System property.
  1047. * System properties are "global" properties like application.startdir,
  1048. * and user.dir. Many of these correspond to similar properties in Java
  1049. * or Ant.
  1050. *
  1051. * @param string $paramName
  1052. * @return string Value of found property (or null, if none found).
  1053. */
  1054. public static function getProperty($propName) {
  1055. // some properties are detemined on each access
  1056. // some are cached, see below
  1057. // default is the cached value:
  1058. $val = isset(self::$properties[$propName]) ? self::$properties[$propName] : null;
  1059. // special exceptions
  1060. switch($propName) {
  1061. case 'user.dir':
  1062. $val = getcwd();
  1063. break;
  1064. }
  1065. return $val;
  1066. }
  1067. /** Retuns reference to all properties*/
  1068. public static function &getProperties() {
  1069. return self::$properties;
  1070. }
  1071. public static function setProperty($propName, $propValue) {
  1072. $propName = (string) $propName;
  1073. $oldValue = self::getProperty($propName);
  1074. self::$properties[$propName] = $propValue;
  1075. return $oldValue;
  1076. }
  1077. public static function currentTimeMillis() {
  1078. list($usec, $sec) = explode(" ",microtime());
  1079. return ((float)$usec + (float)$sec);
  1080. }
  1081. /**
  1082. * Sets the include path to PHP_CLASSPATH constant (if this has been defined).
  1083. * @return void
  1084. * @throws ConfigurationException - if the include_path could not be set (for some bizarre reason)
  1085. */
  1086. private static function setIncludePaths() {
  1087. if (defined('PHP_CLASSPATH')) {
  1088. $result = set_include_path(PHP_CLASSPATH);
  1089. if ($result === false) {
  1090. throw new ConfigurationException("Could not set PHP include_path.");
  1091. }
  1092. self::$origIniSettings['include_path'] = $result; // save original value for setting back later
  1093. }
  1094. }
  1095. /**
  1096. * Sets PHP INI values that Phing needs.
  1097. * @return void
  1098. */
  1099. private static function setIni() {
  1100. self::$origIniSettings['error_reporting'] = error_reporting(E_ALL);
  1101. // We won't bother storing original max_execution_time, since 1) the value in
  1102. // php.ini may be wrong (and there's no way to get the current value) and
  1103. // 2) it would mean something very strange to set it to a value less than time script
  1104. // has already been running, which would be the likely change.
  1105. set_time_limit(0);
  1106. self::$origIniSettings['magic_quotes_gpc'] = ini_set('magic_quotes_gpc', 'off');
  1107. self::$origIniSettings['short_open_tag'] = ini_set('short_open_tag', 'off');
  1108. self::$origIniSettings['default_charset'] = ini_set('default_charset', 'iso-8859-1');
  1109. self::$origIniSettings['register_globals'] = ini_set('register_globals', 'off');
  1110. self::$origIniSettings['allow_call_time_pass_reference'] = ini_set('allow_call_time_pass_reference', 'on');
  1111. self::$origIniSettings['track_errors'] = ini_set('track_errors', 1);
  1112. // should return memory limit in MB
  1113. $mem_limit = (int) ini_get('memory_limit');
  1114. if ($mem_limit < 32 && $mem_limit > -1) {
  1115. // We do *not* need to save the original value here, since we don't plan to restore
  1116. // this after shutdown (we don't trust the effectiveness of PHP's garbage collection).
  1117. ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects
  1118. }
  1119. }
  1120. /**
  1121. * Restores [most] PHP INI values to their pre-Phing state.
  1122. *
  1123. * Currently the following settings are not restored:
  1124. * - max_execution_time (because getting current time limit is not possible)
  1125. * - memory_limit (which may have been increased by Phing)
  1126. *
  1127. * @return void
  1128. */
  1129. private static function restoreIni()
  1130. {
  1131. foreach(self::$origIniSettings as $settingName => $settingValue) {
  1132. switch($settingName) {
  1133. case 'error_reporting':
  1134. error_reporting($settingValue);
  1135. break;
  1136. default:
  1137. ini_set($settingName, $settingValue);
  1138. }
  1139. }
  1140. }
  1141. /**
  1142. * Returns reference to Timer object.
  1143. * @return Timer
  1144. */
  1145. public static function getTimer() {
  1146. if (self::$timer === null) {
  1147. include_once 'phing/system/util/Timer.php';
  1148. self::$timer= new Timer();
  1149. }
  1150. return self::$timer;
  1151. }
  1152. /**
  1153. * Start up Phing.
  1154. * Sets up the Phing environment but does not initiate the build process.
  1155. * @return void
  1156. * @throws Exception - If the Phing environment cannot be initialized.
  1157. */
  1158. public static function startup() {
  1159. // setup STDOUT and STDERR defaults
  1160. self::initializeOutputStreams();
  1161. // some init stuff
  1162. self::getTimer()->start();
  1163. self::setSystemConstants();
  1164. self::setIncludePaths();
  1165. self::setIni();
  1166. }
  1167. /**
  1168. * Halts the system.
  1169. * @deprecated This method is deprecated and is no longer called by Phing internally. Any
  1170. * normal shutdown routines are handled by the shutdown() method.
  1171. * @see shutdown()
  1172. */
  1173. public static function halt() {
  1174. self::shutdown();
  1175. }
  1176. /**
  1177. * Performs any shutdown routines, such as stopping timers.
  1178. * @return void
  1179. */
  1180. public static function shutdown() {
  1181. self::restoreIni();
  1182. self::getTimer()->stop();
  1183. }
  1184. }