home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / phing / Phing.php < prev    next >
Encoding:
PHP Script  |  2007-11-04  |  38.8 KB  |  1,261 lines

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