diff options
author | ctrlaltca <> | 2012-11-18 17:18:54 +0000 |
---|---|---|
committer | ctrlaltca <> | 2012-11-18 17:18:54 +0000 |
commit | cfd546c6b3240bd9d1ef34a9f6201b360fac3261 (patch) | |
tree | 626b0885f54cc5a066e92b3d6b4307756ace09c2 /buildscripts/phing/classes | |
parent | 17139224a1f1f2bbdcc1f1a98cb20f60a12bdfab (diff) |
update ping. Part 2: added ping 2.4.12, fixed caller script
Diffstat (limited to 'buildscripts/phing/classes')
364 files changed, 68585 insertions, 0 deletions
diff --git a/buildscripts/phing/classes/phing/BuildEvent.php b/buildscripts/phing/classes/phing/BuildEvent.php new file mode 100755 index 00000000..581f7cf4 --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildEvent.php @@ -0,0 +1,198 @@ +<?php +/* + * $Id: 47c800dd15c2367bb5890065f96ffceb7ae9ba55 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/lang/EventObject.php'; + +/** + * Encapsulates a build specific event. + * + * <p>We have three sources of events all handled by this class: + * + * <ul> + * <li>Project level events</li> + * <li>Target level events</li> + * <li>Task level events</li> + * </ul> + * + * <p> Events are all fired from the project class by creating an event object + * using this class and passing it to the listeners. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing + */ +class BuildEvent extends EventObject { + + /** + * A reference to the project + * @var Project + */ + protected $project; + + /** + * A reference to the target + * @var Target + */ + protected $target; + + /** + * A reference to the task + * + * @var Task + */ + protected $task; + + /** + * The message of this event, if the event is a message + * @var string + */ + protected $message = null; + + /** + * The priority of the message + * + * @var string + * @see $message + */ + protected $priority = Project::MSG_VERBOSE; + + /** + * The execption that caused the event, if any + * + * @var object + */ + protected $exception = null; + + /** + * Construct a BuildEvent for a project, task or target source event + * + * @param object project the project that emitted the event. + */ + public function __construct($source) { + parent::__construct($source); + if ($source instanceof Project) { + $this->project = $source; + $this->target = null; + $this->task = null; + } elseif ($source instanceof Target) { + $this->project = $source->getProject(); + $this->target = $source; + $this->task = null; + } elseif ($source instanceof Task) { + $this->project = $source->getProject(); + $this->target = $source->getOwningTarget(); + $this->task = $source; + } else { + throw new Exception("Can not construct BuildEvent, unknown source given."); + } + } + + /** + * Sets the message with details and the message priority for this event. + * + * @param string The string message of the event + * @param integer The priority this message should have + */ + public function setMessage($message, $priority) { + $this->message = (string) $message; + $this->priority = (int) $priority; + } + + /** + * Set the exception that was the cause of this event. + * + * @param Exception The exception that caused the event + */ + public function setException($exception) { + $this->exception = $exception; + } + + /** + * Returns the project instance that fired this event. + * + * The reference to the project instance is set by the constructor if this + * event was fired from the project class. + * + * @return Project The project instance that fired this event + */ + public function getProject() { + return $this->project; + } + + /** + * Returns the target instance that fired this event. + * + * The reference to the target instance is set by the constructor if this + * event was fired from the target class. + * + * @return Target The target that fired this event + */ + public function getTarget() { + return $this->target; + } + + /** + * Returns the target instance that fired this event. + * + * The reference to the task instance is set by the constructor if this + * event was fired within a task. + * + * @return Task The task that fired this event + */ + public function getTask() { + return $this->task; + } + + /** + * Returns the logging message. This field will only be set for + * "messageLogged" events. + * + * @return string The log message + */ + function getMessage() { + return $this->message; + } + + /** + * Returns the priority of the logging message. This field will only + * be set for "messageLogged" events. + * + * @return integer The message priority + */ + function getPriority() { + return $this->priority; + } + + /** + * Returns the exception that was thrown, if any. + * This field will only be set for "taskFinished", "targetFinished", and + * "buildFinished" events. + * + * @see BuildListener::taskFinished() + * @see BuildListener::targetFinished() + * @see BuildListener::buildFinished() + * @return Exception + */ + public function getException() { + return $this->exception; + } +} diff --git a/buildscripts/phing/classes/phing/BuildException.php b/buildscripts/phing/classes/phing/BuildException.php new file mode 100755 index 00000000..dda1a05a --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildException.php @@ -0,0 +1,124 @@ +<?php +/* + * $Id: 5edc6b90e055d23ecceac1b6fd7e5fa80e86e006 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * BuildException is for when things go wrong in a build execution. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @version $Id$ + * @package phing + */ +class BuildException extends Exception { + + /** + * Location in the xml file. + * @var Location + */ + protected $location; + + /** + * The nested "cause" exception. + * @var Exception + */ + protected $cause; + + /** + * Construct a BuildException. + * Supported signatures: + * throw new BuildException($causeExc); + * throw new BuildException($msg); + * throw new Buildexception($causeExc, $loc); + * throw new BuildException($msg, $causeExc); + * throw new BuildException($msg, $loc); + * throw new BuildException($msg, $causeExc, $loc); + * @param Exception|string $p1 + * @param Location|Exception|null $p2 + * @param Location|null $p3 + */ + public function __construct($p1, $p2 = null, $p3 = null) { + + $cause = null; + $loc = null; + $msg = ""; + + if ($p3 !== null) { + $cause = $p2; + $loc = $p3; + $msg = $p1; + } elseif ($p2 !== null) { + if ($p2 instanceof Exception) { + $cause = $p2; + $msg = $p1; + } elseif ($p2 instanceof Location) { + $loc = $p2; + if ($p1 instanceof Exception) { + $cause = $p1; + } else { + $msg = $p1; + } + } + } elseif ($p1 instanceof Exception) { + $cause = $p1; + } else { + $msg = $p1; + } + + parent::__construct($msg); + + if ($cause !== null) { + $this->cause = $cause; + $this->message .= " [wrapped: " . $cause->getMessage() ."]"; + } + + if ($loc !== null) { + $this->setLocation($loc); + } + } + + /** + * Gets the cause exception. + * + * @return Exception + */ + public function getCause() { + return $this->cause; + } + + /** + * Gets the location of error in XML file. + * + * @return Location + */ + public function getLocation() { + return $this->location; + } + + /** + * Sets the location of error in XML file. + * + * @param Location $loc + */ + public function setLocation(Location $loc) { + $this->location = $loc; + $this->message = $loc->toString() . ': ' . $this->message; + } + +} diff --git a/buildscripts/phing/classes/phing/BuildListener.php b/buildscripts/phing/classes/phing/BuildListener.php new file mode 100755 index 00000000..a4086aa3 --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildListener.php @@ -0,0 +1,91 @@ +<?php +/* + * $Id: 46e871c830686838e509a3b0e173e7f4c0736ab3 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Interface for build listeners. + * + * Classes that implement a listener must extend this class and (faux)implement + * all methods that are decleard as dummies below. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see BuildEvent + * @see Project::addBuildListener() + * @package phing + */ +interface BuildListener { + + /** + * Fired before any targets are started. + * + * @param BuildEvent $event The BuildEvent + */ + public function buildStarted(BuildEvent $event); + + /** + * Fired after the last target has finished. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getException() + */ + public function buildFinished(BuildEvent $event); + + /** + * Fired when a target is started. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getTarget() + */ + public function targetStarted(BuildEvent $event); + + /** + * Fired when a target has finished. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent#getException() + */ + public function targetFinished(BuildEvent $event); + + /** + * Fired when a task is started. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getTask() + */ + public function taskStarted(BuildEvent $event); + + /** + * Fired when a task has finished. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getException() + */ + public function taskFinished(BuildEvent $event); + + /** + * Fired whenever a message is logged. + * + * @param BuildEvent $event The BuildEvent + * @see BuildEvent::getMessage() + */ + public function messageLogged(BuildEvent $event); +} diff --git a/buildscripts/phing/classes/phing/BuildLogger.php b/buildscripts/phing/classes/phing/BuildLogger.php new file mode 100755 index 00000000..03458159 --- /dev/null +++ b/buildscripts/phing/classes/phing/BuildLogger.php @@ -0,0 +1,70 @@ +<?php +/* + * $Id: 64eb45bf98f65e415da03c93c4f0d89573a5e29c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/BuildListener.php'; + +/** + * Interface for build loggers. + * + * Build loggers are build listeners but with some additional functionality: + * - They can be configured with a log level (below which they will ignore messages) + * - They have error and output streams + * + * Classes that implement a listener must implement this interface. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see BuildEvent + * @see Project::addBuildListener() + * @package phing + */ +interface BuildLogger extends BuildListener { + + /** + * Sets the min log level that this logger should respect. + * + * Messages below this level are ignored. + * + * Constants for the message levels are in Project.php. The order of + * the levels, from least to most verbose, is: + * - Project::MSG_ERR + * - Project::MSG_WARN + * - Project::MSG_INFO + * - Project::MSG_VERBOSE + * - Project::MSG_DEBUG + * + * @param int $level The log level integer (e.g. Project::MSG_VERBOSE, etc.). + */ + public function setMessageOutputLevel($level); + + /** + * Sets the standard output stream to use. + * @param OutputStream $output Configured output stream (e.g. STDOUT) for standard output. + */ + public function setOutputStream(OutputStream $output); + + /** + * Sets the output stream to use for errors. + * @param OutputStream $err Configured output stream (e.g. STDERR) for errors. + */ + public function setErrorStream(OutputStream $err); + +} diff --git a/buildscripts/phing/classes/phing/ConfigurationException.php b/buildscripts/phing/classes/phing/ConfigurationException.php new file mode 100755 index 00000000..81735e1b --- /dev/null +++ b/buildscripts/phing/classes/phing/ConfigurationException.php @@ -0,0 +1,85 @@ +<?php +/* + * $Id: 6070efdfa67cb145fd48525e88737d39c8eff4b2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * ConfigurationException is thrown by Phing during the configuration and setup phase of the project. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing + */ +class ConfigurationException extends Exception { + + /** + * Location in the xml file. + * @var Location + */ + protected $location; + + /** + * The nested "cause" exception. + * @var Exception + */ + protected $cause; + + /** + * Construct a ConfigurationException. + * Supported signatures: + * throw new BuildException($causeExc); + * throw new BuildException($msg); + * throw new BuildException($msg, $causeExc); + * @param Exception|string $p1 + * @param Exception|null $p2 + */ + public function __construct($p1, $p2 = null) { + + $cause = null; + $msg = ""; + + if ($p2 !== null) { + if ($p2 instanceof Exception) { + $cause = $p2; + $msg = $p1; + } + } elseif ($p1 instanceof Exception) { + $cause = $p1; + } else { + $msg = $p1; + } + + parent::__construct($msg); + + if ($cause !== null) { + $this->cause = $cause; + $this->message .= " [wrapped: " . $cause->getMessage() ."]"; + } + } + + /** + * Gets the cause exception. + * + * @return Exception + */ + public function getCause() { + return $this->cause; + } + +} diff --git a/buildscripts/phing/classes/phing/IntrospectionHelper.php b/buildscripts/phing/classes/phing/IntrospectionHelper.php new file mode 100755 index 00000000..8bcf2456 --- /dev/null +++ b/buildscripts/phing/classes/phing/IntrospectionHelper.php @@ -0,0 +1,575 @@ +<?php + +/* + * $Id: a5f2dbd71bafd2c363026d911132c3b47bf44239 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/types/Reference.php'; +include_once 'phing/types/Path.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * Helper class that collects the methods that a task or nested element + * holds to set attributes, create nested elements or hold PCDATA + * elements. + * + *<ul> + * <li><strong>SMART-UP INLINE DOCS</strong></li> + * <li><strong>POLISH-UP THIS CLASS</strong></li> + *</ul> + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: a5f2dbd71bafd2c363026d911132c3b47bf44239 $ + * @package phing + */ +class IntrospectionHelper { + + + + /** + * Holds the attribute setter methods. + * + * @var array string[] + */ + private $attributeSetters = array(); + + /** + * Holds methods to create nested elements. + * + * @var array string[] + */ + private $nestedCreators = array(); + + /** + * Holds methods to store configured nested elements. + * + * @var array string[] + */ + private $nestedStorers = array(); + + /** + * Map from attribute names to nested types. + */ + private $nestedTypes = array(); + + /** + * New idea in phing: any class can register certain + * keys -- e.g. "task.current_file" -- which can be used in + * task attributes, if supported. In the build XML these + * are referred to like this: + * <regexp pattern="\n" replace="%{task.current_file}"/> + * In the type/task a listener method must be defined: + * function setListeningReplace($slot) {} + * @var array string[] + */ + private $slotListeners = array(); + + /** + * The method to add PCDATA stuff. + * + * @var string Method name of the addText (redundant?) method, if class supports it :) + */ + private $methodAddText = null; + + /** + * The Class that's been introspected. + * + * @var object + * @access private + */ + private $bean; + + /** + * The cache of IntrospectionHelper classes instantiated by getHelper(). + * @var array IntrospectionHelpers[] + */ + private static $helpers = array(); + + /** + * Factory method for helper objects. + * + * @param string $class The class to create a Helper for + */ + public static function getHelper($class) { + if (!isset(self::$helpers[$class])) { + self::$helpers[$class] = new IntrospectionHelper($class); + } + return self::$helpers[$class]; + } + + /** + * This function constructs a new introspection helper for a specific class. + * + * This method loads all methods for the specified class and categorizes them + * as setters, creators, slot listeners, etc. This way, the setAttribue() doesn't + * need to perform any introspection -- either the requested attribute setter/creator + * exists or it does not & a BuildException is thrown. + * + * @param string $class The classname for this IH. + */ + public function __construct($class) { + + $this->bean = new ReflectionClass($class); + + //$methods = get_class_methods($bean); + foreach($this->bean->getMethods() as $method) { + + if ($method->isPublic()) { + + // We're going to keep case-insensitive method names + // for as long as we're allowed :) It makes it much + // easier to map XML attributes to PHP class method names. + $name = strtolower($method->getName()); + + // There are a few "reserved" names that might look like attribute setters + // but should actually just be skipped. (Note: this means you can't ever + // have an attribute named "location" or "tasktype" or a nested element named "task".) + if ($name === "setlocation" || $name === "settasktype" || $name === "addtask") { + continue; + } + + if ($name === "addtext") { + + $this->methodAddText = $method; + + } elseif (strpos($name, "setlistening") === 0) { + + // Phing supports something unique called "RegisterSlots" + // These are dynamic values that use a basic slot system so that + // classes can register to listen to specific slots, and the value + // will always be grabbed from the slot (and never set in the project + // component). This is useful for things like tracking the current + // file being processed by a filter (e.g. AppendTask sets an append.current_file + // slot, which can be ready by the XSLTParam type.) + + if (count($method->getParameters()) !== 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter."); + } + + $this->slotListeners[$name] = $method; + + } elseif (strpos($name, "set") === 0) { + + // A standard attribute setter. + + if (count($method->getParameters()) !== 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter."); + } + + $this->attributeSetters[$name] = $method; + + } elseif (strpos($name, "create") === 0) { + + if ($method->getNumberOfRequiredParameters() > 0) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() may not take any parameters."); + } + + // Because PHP doesn't support return types, we are going to do + // two things here to guess return type: + // 1) parse comments for an explicit value + // 2) if that fails, assume that the part of the method after "create" + // is the name of the return type (in many cases it is not) + + // This isn't super important -- i.e. we're not instantaiting classes + // based on this information. It's more just so that IntrospectionHelper + // can keep track of all the nested types -- and provide more helpful + // exception messages, etc. + + preg_match('/@return[\s]+([\w]+)/', $method->getDocComment(), $matches); + if (!empty($matches[1]) && class_exists($matches[1], false)) { + $this->nestedTypes[$name] = $matches[1]; + } else { + // assume that method createEquals() creates object of type "Equals" + // (that example would be false, of course) + $this->nestedTypes[$name] = $this->getPropertyName($name, "create"); + } + + $this->nestedCreators[$name] = $method; + + } elseif (strpos($name, "addconfigured") === 0) { + + // *must* use class hints if using addConfigured ... + + // 1 param only + $params = $method->getParameters(); + + if (count($params) < 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter."); + } + + if (count($params) > 1) { + $this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)"); + } + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + if ($classname === null) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter."); + } + + $this->nestedTypes[$name] = $classname; + + $this->nestedStorers[$name] = $method; + + } elseif (strpos($name, "add") === 0) { + + // *must* use class hints if using add ... + + // 1 param only + $params = $method->getParameters(); + if (count($params) < 1) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter."); + } + + if (count($params) > 1) { + $this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)"); + } + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + // we don't use the classname here, but we need to make sure it exists before + // we later try to instantiate a non-existant class + if ($classname === null) { + throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter."); + } + + $this->nestedCreators[$name] = $method; + } + } // if $method->isPublic() + } // foreach + } + + + /** + * Sets the named attribute. + * @param Project $project + * @param string $element + * @param string $attributeName + * @param mixed $value + */ + public function setAttribute(Project $project, $element, $attributeName, &$value) { + + // we want to check whether the value we are setting looks like + // a slot-listener variable: %{task.current_file} + // + // slot-listener variables are not like properties, in that they cannot be mixed with + // other text values. The reason for this disparity is that properties are only + // set when first constructing objects from XML, whereas slot-listeners are always dynamic. + // + // This is made possible by PHP5 (objects automatically passed by reference) and PHP's loose + // typing. + + if (StringHelper::isSlotVar($value)) { + + $as = "setlistening" . strtolower($attributeName); + + if (!isset($this->slotListeners[$as])) { + $msg = $this->getElementName($project, $element) . " doesn't support a slot-listening '$attributeName' attribute."; + throw new BuildException($msg); + } + + $method = $this->slotListeners[$as]; + + $key = StringHelper::slotVar($value); + $value = Register::getSlot($key); // returns a RegisterSlot object which will hold current value of that register (accessible using getValue()) + + } else { + + // Traditional value options + + $as = "set".strtolower($attributeName); + + if (!isset($this->attributeSetters[$as])) { + $msg = $this->getElementName($project, $element) . " doesn't support the '$attributeName' attribute."; + throw new BuildException($msg); + } + + $method = $this->attributeSetters[$as]; + + if ($as == "setrefid") { + $value = new Reference($value); + } else { + // value is a string representation of a boolean type, + // convert it to primitive + if (StringHelper::isBoolean($value)) { + + $value = StringHelper::booleanValue($value); + } + + // does method expect a PhingFile object? if so, then + // pass a project-relative file. + $params = $method->getParameters(); + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + // there should only be one param; we'll just assume .... + if ($classname !== null) { + switch(strtolower($classname)) { + case "phingfile": + $value = $project->resolveFile($value); + break; + case "path": + $value = new Path($project, $value); + break; + case "reference": + $value = new Reference($value); + break; + // any other object params we want to support should go here ... + } + + } // if hint !== null + + } // if not setrefid + + } // if is slot-listener + + try { + $project->log(" -calling setter ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + $method->invoke($element, $value); + } catch(Exception $exc) { + throw new BuildException($exc); + } + + } + + /** + * Adds PCDATA areas. + * + * @param Project $project + * @param string $element + * @param string $text + */ + public function addText(Project $project, $element, $text) { + if ($this->methodAddText === null) { + $msg = $this->getElementName($project, $element)." doesn't support nested text data."; + throw new BuildException($msg); + } + try { + $method = $this->methodAddText; + $method->invoke($element, $text); + } catch (Exception $exc) { + throw new BuildException($exc); + } + } + + /** + * Creates a named nested element. + * + * Valid creators can be in the form createFoo() or addFoo(Bar). + * + * @param Project $project + * @param string $element + * @param string $elementName + * @return object Returns the nested element. + * @throws BuildException + */ + public function createElement(Project $project, $element, $elementName) { + + $addMethod = "add".strtolower($elementName); + $createMethod = "create".strtolower($elementName); + $nestedElement = null; + + if (isset($this->nestedCreators[$createMethod])) { + + $method = $this->nestedCreators[$createMethod]; + try { // try to invoke the creator method on object + $project->log(" -calling creator ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + $nestedElement = $method->invoke($element); + } catch (Exception $exc) { + throw new BuildException($exc); + } + + } elseif (isset($this->nestedCreators[$addMethod])) { + + $method = $this->nestedCreators[$addMethod]; + + // project components must use class hints to support the add methods + + try { // try to invoke the adder method on object + + $project->log(" -calling adder ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + // we've already assured that correct num of params + // exist and that method is using class hints + $params = $method->getParameters(); + + $classname = null; + + if (($hint = $params[0]->getClass()) !== null) { + $classname = $hint->getName(); + } + + // create a new instance of the object and add it via $addMethod + $nestedElement = new $classname(); + + $method->invoke($element, $nestedElement); + + } catch (Exception $exc) { + throw new BuildException($exc); + } + } else { + $msg = $this->getElementName($project, $element) . " doesn't support the '$elementName' creator/adder."; + throw new BuildException($msg); + } + + if ($nestedElement instanceof ProjectComponent) { + $nestedElement->setProject($project); + } + + return $nestedElement; + } + + /** + * Creates a named nested element. + * + * @param Project $project + * @param string $element + * @param string $child + * @param string|null $elementName + * @return void + * @throws BuildException + */ + public function storeElement($project, $element, $child, $elementName = null) { + + if ($elementName === null) { + return; + } + + $storer = "addconfigured".strtolower($elementName); + + if (isset($this->nestedStorers[$storer])) { + + $method = $this->nestedStorers[$storer]; + + try { + $project->log(" -calling storer ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", Project::MSG_DEBUG); + $method->invoke($element, $child); + } catch (Exception $exc) { + throw new BuildException($exc); + } + } + + } + + /** + * Does the introspected class support PCDATA? + * @return boolean + */ + public function supportsCharacters() { + return ($this->methodAddText !== null); + } + + /** + * Return all attribues supported by the introspected class. + * @return string[] + */ + public function getAttributes() { + $attribs = array(); + foreach (array_keys($this->attributeSetters) as $setter) { + $attribs[] =$this->getPropertyName($setter, "set"); + } + return $attribs; + } + + /** + * Return all nested elements supported by the introspected class. + * @return string[] + */ + public function getNestedElements() { + return $this->nestedTypes; + } + + /** + * Get the the name for an element. + * When possible the full classnam (phing.tasks.system.PropertyTask) will + * be returned. If not available (loaded in taskdefs or typedefs) then the + * XML element name will be returned. + * + * @param Project $project + * @param object $element The Task or type element. + * @return string Fully qualified class name of element when possible. + */ + public function getElementName(Project $project, $element) { + + $taskdefs = $project->getTaskDefinitions(); + $typedefs = $project->getDataTypeDefinitions(); + + // check if class of element is registered with project (tasks & types) + // most element types don't have a getTag() method + $elClass = get_class($element); + + if (!in_array('getTag', get_class_methods($elClass))) { + // loop through taskdefs and typesdefs and see if the class name + // matches (case-insensitive) any of the classes in there + foreach(array_merge($taskdefs, $typedefs) as $elName => $class) { + if (0 === strcasecmp($elClass, StringHelper::unqualify($class))) { + return $class; + } + } + return "$elClass (unknown)"; + } else { + // ->getTag() method does exist, so use it + $elName = $element->getTag(); + if (isset($taskdefs[$elName])) { + return $taskdefs[$elName]; + } elseif (isset($typedefs[$elName])) { + + return $typedefs[$elName]; + } else { + return "$elName (unknown)"; + } + } + } + + /** + * Extract the name of a property from a method name - subtracting a given prefix. + * + * @param string $methodName + * @param string $prefix + * @return string + */ + public function getPropertyName($methodName, $prefix) { + $start = strlen($prefix); + return strtolower(substr($methodName, $start)); + } + + /** + * Prints warning message to screen if -debug was used. + * @param string $msg + */ + public function warn($msg) { + if (Phing::getMsgOutputLevel() === Project::MSG_DEBUG) { + print("[IntrospectionHelper] " . $msg . "\n"); + } + } + +} diff --git a/buildscripts/phing/classes/phing/Phing.php b/buildscripts/phing/classes/phing/Phing.php new file mode 100755 index 00000000..d663ee42 --- /dev/null +++ b/buildscripts/phing/classes/phing/Phing.php @@ -0,0 +1,1414 @@ +<?php +/* + * $Id: 1ad418f51ac07c9afaadf1c1f4befd5535f5b390 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Project.php'; +require_once 'phing/ProjectComponent.php'; +require_once 'phing/Target.php'; +require_once 'phing/Task.php'; + +include_once 'phing/BuildException.php'; +include_once 'phing/ConfigurationException.php'; +include_once 'phing/BuildEvent.php'; + +include_once 'phing/parser/Location.php'; +include_once 'phing/parser/ExpatParser.php'; +include_once 'phing/parser/AbstractHandler.php'; +include_once 'phing/parser/ProjectConfigurator.php'; +include_once 'phing/parser/RootHandler.php'; +include_once 'phing/parser/ProjectHandler.php'; +include_once 'phing/parser/TaskHandler.php'; +include_once 'phing/parser/TargetHandler.php'; +include_once 'phing/parser/DataTypeHandler.php'; +include_once 'phing/parser/NestedElementHandler.php'; + +include_once 'phing/system/util/Properties.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/OutputStream.php'; +include_once 'phing/system/io/FileOutputStream.php'; +include_once 'phing/system/io/FileReader.php'; +include_once 'phing/system/util/Register.php'; + +/** + * Entry point into Phing. This class handles the full lifecycle of a build -- from + * parsing & handling commandline arguments to assembling the project to shutting down + * and cleaning up in the end. + * + * If you are invoking Phing from an external application, this is still + * the class to use. Your applicaiton can invoke the start() method, passing + * any commandline arguments or additional properties. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: 1ad418f51ac07c9afaadf1c1f4befd5535f5b390 $ + * @package phing + */ +class Phing { + + /** The default build file name */ + const DEFAULT_BUILD_FILENAME = "build.xml"; + + /** Our current message output status. Follows Project::MSG_XXX */ + private static $msgOutputLevel = Project::MSG_INFO; + + /** PhingFile that we are using for configuration */ + private $buildFile = null; + + /** The build targets */ + private $targets = array(); + + /** + * Set of properties that are passed in from commandline or invoking code. + * @var Properties + */ + private static $definedProps; + + /** Names of classes to add as listeners to project */ + private $listeners = array(); + + private $loggerClassname = null; + + /** The class to handle input (can be only one). */ + private $inputHandlerClassname; + + /** Indicates if this phing should be run */ + private $readyToRun = false; + + /** Indicates we should only parse and display the project help information */ + private $projectHelp = false; + + /** Used by utility function getResourcePath() */ + private static $importPaths; + + /** System-wide static properties (moved from System) */ + private static $properties = array(); + + /** Static system timer. */ + private static $timer; + + /** The current Project */ + private static $currentProject; + + /** Whether to capture PHP errors to buffer. */ + private static $phpErrorCapture = false; + + /** Array of captured PHP errors */ + private static $capturedPhpErrors = array(); + + /** + * @var OUtputStream Stream for standard output. + */ + private static $out; + + /** + * @var OutputStream Stream for error output. + */ + private static $err; + + /** + * @var boolean Whether we are using a logfile. + */ + private static $isLogFileUsed = false; + + /** + * Array to hold original ini settings that Phing changes (and needs + * to restore in restoreIni() method). + * + * @var array Struct of array(setting-name => setting-value) + * @see restoreIni() + */ + private static $origIniSettings = array(); + + /** + * Entry point allowing for more options from other front ends. + * + * This method encapsulates the complete build lifecycle. + * + * @param array $args The commandline args passed to phing shell script. + * @param array $additionalUserProperties Any additional properties to be passed to Phing (alternative front-end might implement this). + * These additional properties will be available using the getDefinedProperty() method and will + * be added to the project's "user" properties + * @see execute() + * @see runBuild() + * @throws Exception - if there is an error during build + */ + public static function start($args, array $additionalUserProperties = null) { + + try { + $m = new Phing(); + $m->execute($args); + } catch (Exception $exc) { + self::handleLogfile(); + throw $exc; + } + + if ($additionalUserProperties !== null) { + foreach($additionalUserProperties as $key => $value) { + $m->setDefinedProperty($key, $value); + } + } + + try { + $m->runBuild(); + } catch(Exception $exc) { + self::handleLogfile(); + throw $exc; + } + + // everything fine, shutdown + self::handleLogfile(); + } + + /** + * Prints the message of the Exception if it's not null. + * @param Exception $t + */ + public static function printMessage(Exception $t) { + if (self::$err === null) { // Make sure our error output is initialized + self::initializeOutputStreams(); + } + if (self::getMsgOutputLevel() >= Project::MSG_VERBOSE) { + self::$err->write($t->__toString() . PHP_EOL); + } else { + self::$err->write($t->getMessage() . PHP_EOL); + } + } + + /** + * Sets the stdout and stderr streams if they are not already set. + */ + private static function initializeOutputStreams() { + if (self::$out === null) { + self::$out = new OutputStream(fopen("php://stdout", "w")); + } + if (self::$err === null) { + self::$err = new OutputStream(fopen("php://stderr", "w")); + } + } + + /** + * Sets the stream to use for standard (non-error) output. + * @param OutputStream $stream The stream to use for standard output. + */ + public static function setOutputStream(OutputStream $stream) { + self::$out = $stream; + } + + /** + * Gets the stream to use for standard (non-error) output. + * @return OutputStream + */ + public static function getOutputStream() { + return self::$out; + } + + /** + * Sets the stream to use for error output. + * @param OutputStream $stream The stream to use for error output. + */ + public static function setErrorStream(OutputStream $stream) { + self::$err = $stream; + } + + /** + * Gets the stream to use for error output. + * @return OutputStream + */ + public static function getErrorStream() { + return self::$err; + } + + /** + * Close logfiles, if we have been writing to them. + * + * @since Phing 2.3.0 + */ + private static function handleLogfile() { + if (self::$isLogFileUsed) { + self::$err->close(); + self::$out->close(); + } + } + + /** + * Making output level a static property so that this property + * can be accessed by other parts of the system, enabling + * us to display more information -- e.g. backtraces -- for "debug" level. + * @return int + */ + public static function getMsgOutputLevel() { + return self::$msgOutputLevel; + } + + /** + * Command line entry point. This method kicks off the building + * of a project object and executes a build using either a given + * target or the default target. + * + * @param array $args Command line args. + * @return void + */ + public static function fire($args) { + self::start($args, null); + } + + /** + * Setup/initialize Phing environment from commandline args. + * @param array $args commandline args passed to phing shell. + * @return void + */ + public function execute($args) { + + self::$definedProps = new Properties(); + $this->searchForThis = null; + + // 1) First handle any options which should always + // Note: The order in which these are executed is important (if multiple of these options are specified) + + if (in_array('-help', $args) || in_array('-h', $args)) { + $this->printUsage(); + return; + } + + if (in_array('-version', $args) || in_array('-v', $args)) { + $this->printVersion(); + return; + } + + // 2) Next pull out stand-alone args. + // Note: The order in which these are executed is important (if multiple of these options are specified) + + if (false !== ($key = array_search('-quiet', $args, true)) || false !== ($key = array_search('-q', $args, true))) { + self::$msgOutputLevel = Project::MSG_WARN; + unset($args[$key]); + } + + if (false !== ($key = array_search('-verbose', $args, true))) { + self::$msgOutputLevel = Project::MSG_VERBOSE; + unset($args[$key]); + } + + if (false !== ($key = array_search('-debug', $args, true))) { + self::$msgOutputLevel = Project::MSG_DEBUG; + unset($args[$key]); + } + + // 3) Finally, cycle through to parse remaining args + // + $keys = array_keys($args); // Use keys and iterate to max(keys) since there may be some gaps + $max = $keys ? max($keys) : -1; + for($i=0; $i <= $max; $i++) { + + if (!array_key_exists($i, $args)) { + // skip this argument, since it must have been removed above. + continue; + } + + $arg = $args[$i]; + + if ($arg == "-logfile") { + try { + // see: http://phing.info/trac/ticket/65 + if (!isset($args[$i+1])) { + $msg = "You must specify a log file when using the -logfile argument\n"; + throw new ConfigurationException($msg); + } else { + $logFile = new PhingFile($args[++$i]); + $out = new FileOutputStream($logFile); // overwrite + self::setOutputStream($out); + self::setErrorStream($out); + self::$isLogFileUsed = true; + } + } catch (IOException $ioe) { + $msg = "Cannot write on the specified log file. Make sure the path exists and you have write permissions."; + throw new ConfigurationException($msg, $ioe); + } + } elseif ($arg == "-buildfile" || $arg == "-file" || $arg == "-f") { + if (!isset($args[$i+1])) { + $msg = "You must specify a buildfile when using the -buildfile argument."; + throw new ConfigurationException($msg); + } else { + $this->buildFile = new PhingFile($args[++$i]); + } + } elseif ($arg == "-listener") { + if (!isset($args[$i+1])) { + $msg = "You must specify a listener class when using the -listener argument"; + throw new ConfigurationException($msg); + } else { + $this->listeners[] = $args[++$i]; + } + } elseif (StringHelper::startsWith("-D", $arg)) { + $name = substr($arg, 2); + $value = null; + $posEq = strpos($name, "="); + if ($posEq !== false) { + $value = substr($name, $posEq+1); + $name = substr($name, 0, $posEq); + } elseif ($i < count($args)-1 && !StringHelper::startsWith("-D", $args[$i + 1])) { + $value = $args[++$i]; + } + self::$definedProps->setProperty($name, $value); + } elseif ($arg == "-logger") { + if (!isset($args[$i+1])) { + $msg = "You must specify a classname when using the -logger argument"; + throw new ConfigurationException($msg); + } else { + $this->loggerClassname = $args[++$i]; + } + } elseif ($arg == "-inputhandler") { + if ($this->inputHandlerClassname !== null) { + throw new ConfigurationException("Only one input handler class may be specified."); + } + if (!isset($args[$i+1])) { + $msg = "You must specify a classname when using the -inputhandler argument"; + throw new ConfigurationException($msg); + } else { + $this->inputHandlerClassname = $args[++$i]; + } + } elseif ($arg == "-propertyfile") { + if (!isset($args[$i+1])) { + $msg = "You must specify a filename when using the -propertyfile argument"; + throw new ConfigurationException($msg); + } else { + $p = new Properties(); + $p->load(new PhingFile($args[++$i])); + foreach ($p->getProperties() as $prop => $value) { + $this->setProperty($prop, $value); + } + } + } elseif ($arg == "-longtargets") { + self::$definedProps->setProperty('phing.showlongtargets', 1); + } elseif ($arg == "-projecthelp" || $arg == "-targets" || $arg == "-list" || $arg == "-l" || $arg == "-p") { + // set the flag to display the targets and quit + $this->projectHelp = true; + } elseif ($arg == "-find") { + // eat up next arg if present, default to build.xml + if ($i < count($args)-1) { + $this->searchForThis = $args[++$i]; + } else { + $this->searchForThis = self::DEFAULT_BUILD_FILENAME; + } + } elseif (substr($arg,0,1) == "-") { + // we don't have any more args + self::$err->write("Unknown argument: $arg" . PHP_EOL); + self::printUsage(); + return; + } else { + // if it's no other arg, it may be the target + array_push($this->targets, $arg); + } + } + + // if buildFile was not specified on the command line, + if ($this->buildFile === null) { + // but -find then search for it + if ($this->searchForThis !== null) { + $this->buildFile = $this->_findBuildFile(self::getProperty("user.dir"), $this->searchForThis); + } else { + $this->buildFile = new PhingFile(self::DEFAULT_BUILD_FILENAME); + } + } + // make sure buildfile exists + if (!$this->buildFile->exists()) { + throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " does not exist!"); + } + + // make sure it's not a directory + if ($this->buildFile->isDirectory()) { + throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " is a dir!"); + } + + $this->readyToRun = true; + } + + /** + * Helper to get the parent file for a given file. + * + * @param PhingFile $file + * @return PhingFile Parent file or null if none + */ + private function _getParentFile(PhingFile $file) { + $filename = $file->getAbsolutePath(); + $file = new PhingFile($filename); + $filename = $file->getParent(); + return ($filename === null) ? null : new PhingFile($filename); + } + + /** + * Search parent directories for the build file. + * + * Takes the given target as a suffix to append to each + * parent directory in search of a build file. Once the + * root of the file-system has been reached an exception + * is thrown. + * + * @param string $start Start file path. + * @param string $suffix Suffix filename to look for in parents. + * @return PhingFile A handle to the build file + * + * @throws BuildException Failed to locate a build file + */ + private function _findBuildFile($start, $suffix) { + $startf = new PhingFile($start); + $parent = new PhingFile($startf->getAbsolutePath()); + $file = new PhingFile($parent, $suffix); + + // check if the target file exists in the current directory + while (!$file->exists()) { + // change to parent directory + $parent = $this->_getParentFile($parent); + + // if parent is null, then we are at the root of the fs, + // complain that we can't find the build file. + if ($parent === null) { + throw new ConfigurationException("Could not locate a build file!"); + } + // refresh our file handle + $file = new PhingFile($parent, $suffix); + } + return $file; + } + + /** + * Executes the build. + * @return void + */ + function runBuild() { + + if (!$this->readyToRun) { + return; + } + + $project = new Project(); + + self::setCurrentProject($project); + set_error_handler(array('Phing', 'handlePhpError')); + + $error = null; + + $this->addBuildListeners($project); + $this->addInputHandler($project); + + // set this right away, so that it can be used in logging. + $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath()); + + try { + $project->fireBuildStarted(); + $project->init(); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + throw $exc; + } + + $project->setUserProperty("phing.version", $this->getPhingVersion()); + + $e = self::$definedProps->keys(); + while (count($e)) { + $arg = (string) array_shift($e); + $value = (string) self::$definedProps->getProperty($arg); + $project->setUserProperty($arg, $value); + } + unset($e); + + $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath()); + + // first use the Configurator to create the project object + // from the given build file. + + try { + ProjectConfigurator::configureProject($project, $this->buildFile); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + + // make sure that we have a target to execute + if (count($this->targets) === 0) { + $this->targets[] = $project->getDefaultTarget(); + } + + // make sure that minimum required phing version is satisfied + try { + $this->comparePhingVersion($project->getPhingVersion()); + } catch(Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + + // execute targets if help param was not given + if (!$this->projectHelp) { + + try { + $project->executeTargets($this->targets); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + } + // if help is requested print it + if ($this->projectHelp) { + try { + $this->printDescription($project); + $this->printTargets($project); + } catch (Exception $exc) { + $project->fireBuildFinished($exc); + restore_error_handler(); + self::unsetCurrentProject(); + throw $exc; + } + } + + // finally { + if (!$this->projectHelp) { + $project->fireBuildFinished(null); + } + + restore_error_handler(); + self::unsetCurrentProject(); + } + + private function comparePhingVersion($version) { + $current = strtolower(self::getPhingVersion()); + $current = trim(str_replace('phing', '', $current)); + + // make sure that version checks are not applied to trunk + if('dev' === $current) { + return 1; + } + + if(-1 == version_compare($current, $version)) { + throw new BuildException( + sprintf('Incompatible Phing version (%s). Version "%s" required.', $current, $version)); + } + } + + /** + * Bind any registered build listeners to this project. + * + * This means adding the logger and any build listeners that were specified + * with -listener arg. + * + * @param Project $project + * @return void + */ + private function addBuildListeners(Project $project) { + // Add the default listener + $project->addBuildListener($this->createLogger()); + + foreach($this->listeners as $listenerClassname) { + try { + $clz = Phing::import($listenerClassname); + } catch (Exception $x) { + $msg = "Unable to instantiate specified listener " + . "class " . $listenerClassname . " : " + . $e->getMessage(); + throw new ConfigurationException($msg); + } + + $listener = new $clz(); + + if ($listener instanceof StreamRequiredBuildLogger) { + throw new ConfigurationException("Unable to add " . $listenerClassname . " as a listener, since it requires explicit error/output streams. (You can specify it as a -logger.)"); + } + $project->addBuildListener($listener); + } + } + + /** + * Creates the InputHandler and adds it to the project. + * + * @param Project $project the project instance. + * + * @throws BuildException if a specified InputHandler + * class could not be loaded. + */ + private function addInputHandler(Project $project) { + if ($this->inputHandlerClassname === null) { + $handler = new DefaultInputHandler(); + } else { + try { + $clz = Phing::import($this->inputHandlerClassname); + $handler = new $clz(); + if ($project !== null && method_exists($handler, 'setProject')) { + $handler->setProject($project); + } + } catch (Exception $e) { + $msg = "Unable to instantiate specified input handler " + . "class " . $this->inputHandlerClassname . " : " + . $e->getMessage(); + throw new ConfigurationException($msg); + } + } + $project->setInputHandler($handler); + } + + /** + * Creates the default build logger for sending build events to the log. + * @return BuildLogger The created Logger + */ + private function createLogger() { + if ($this->loggerClassname !== null) { + self::import($this->loggerClassname); + // get class name part + $classname = self::import($this->loggerClassname); + $logger = new $classname; + if (!($logger instanceof BuildLogger)) { + throw new BuildException($classname . ' does not implement the BuildLogger interface.'); + } + } else { + require_once 'phing/listener/DefaultLogger.php'; + $logger = new DefaultLogger(); + } + $logger->setMessageOutputLevel(self::$msgOutputLevel); + $logger->setOutputStream(self::$out); + $logger->setErrorStream(self::$err); + return $logger; + } + + /** + * Sets the current Project + * @param Project $p + */ + public static function setCurrentProject($p) { + self::$currentProject = $p; + } + + /** + * Unsets the current Project + */ + public static function unsetCurrentProject() { + self::$currentProject = null; + } + + /** + * Gets the current Project. + * @return Project Current Project or NULL if none is set yet/still. + */ + public static function getCurrentProject() { + return self::$currentProject; + } + + /** + * A static convenience method to send a log to the current (last-setup) Project. + * If there is no currently-configured Project, then this will do nothing. + * @param string $message + * @param int $priority Project::MSG_INFO, etc. + */ + public static function log($message, $priority = Project::MSG_INFO) { + $p = self::getCurrentProject(); + if ($p) { + $p->log($message, $priority); + } + } + + /** + * Error handler for PHP errors encountered during the build. + * This uses the logging for the currently configured project. + */ + public static function handlePhpError($level, $message, $file, $line) { + + // don't want to print supressed errors + if (error_reporting() > 0) { + + if (self::$phpErrorCapture) { + + self::$capturedPhpErrors[] = array('message' => $message, 'level' => $level, 'line' => $line, 'file' => $file); + + } else { + + $message = '[PHP Error] ' . $message; + $message .= ' [line ' . $line . ' of ' . $file . ']'; + + switch ($level) { + case 16384: // E_USER_DEPRECATED + case 8192: // E_DEPRECATED + case E_STRICT: + case E_NOTICE: + case E_USER_NOTICE: + self::log($message, Project::MSG_VERBOSE); + break; + case E_WARNING: + case E_USER_WARNING: + self::log($message, Project::MSG_WARN); + break; + case E_ERROR: + case E_USER_ERROR: + default: + self::log($message, Project::MSG_ERR); + + } // switch + + } // if phpErrorCapture + + } // if not @ + + } + + /** + * Begins capturing PHP errors to a buffer. + * While errors are being captured, they are not logged. + */ + public static function startPhpErrorCapture() { + self::$phpErrorCapture = true; + self::$capturedPhpErrors = array(); + } + + /** + * Stops capturing PHP errors to a buffer. + * The errors will once again be logged after calling this method. + */ + public static function stopPhpErrorCapture() { + self::$phpErrorCapture = false; + } + + /** + * Clears the captured errors without affecting the starting/stopping of the capture. + */ + public static function clearCapturedPhpErrors() { + self::$capturedPhpErrors = array(); + } + + /** + * Gets any PHP errors that were captured to buffer. + * @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level) + */ + public static function getCapturedPhpErrors() { + return self::$capturedPhpErrors; + } + + /** Prints the usage of how to use this class */ + public static function printUsage() { + + $msg = ""; + $msg .= "phing [options] [target [target2 [target3] ...]]" . PHP_EOL; + $msg .= "Options: " . PHP_EOL; + $msg .= " -h -help print this message" . PHP_EOL; + $msg .= " -l -list list available targets in this project" . PHP_EOL; + $msg .= " -v -version print the version information and exit" . PHP_EOL; + $msg .= " -q -quiet be extra quiet" . PHP_EOL; + $msg .= " -verbose be extra verbose" . PHP_EOL; + $msg .= " -debug print debugging information" . PHP_EOL; + $msg .= " -longtargets show target descriptions during build" . PHP_EOL; + $msg .= " -logfile <file> use given file for log" . PHP_EOL; + $msg .= " -logger <classname> the class which is to perform logging" . PHP_EOL; + $msg .= " -f -buildfile <file> use given buildfile" . PHP_EOL; + $msg .= " -D<property>=<value> use value for given property" . PHP_EOL; + $msg .= " -propertyfile <file> load all properties from file" . PHP_EOL; + $msg .= " -find <file> search for buildfile towards the root of the" . PHP_EOL; + $msg .= " filesystem and use it" . PHP_EOL; + $msg .= " -inputhandler <file> the class to use to handle user input" . PHP_EOL; + //$msg .= " -recursive <file> search for buildfile downwards and use it" . PHP_EOL; + $msg .= PHP_EOL; + $msg .= "Report bugs to <dev@phing.tigris.org>".PHP_EOL; + self::$err->write($msg); + } + + /** + * Prints the current Phing version. + */ + public static function printVersion() { + self::$out->write(self::getPhingVersion().PHP_EOL); + } + + /** + * Gets the current Phing version based on VERSION.TXT file. + * @return string + * @throws BuildException - if unable to find version file. + */ + public static function getPhingVersion() { + $versionPath = self::getResourcePath("phing/etc/VERSION.TXT"); + if ($versionPath === null) { + $versionPath = self::getResourcePath("etc/VERSION.TXT"); + } + if ($versionPath === null) { + throw new ConfigurationException("No VERSION.TXT file found; try setting phing.home environment variable."); + } + try { // try to read file + $buffer = null; + $file = new PhingFile($versionPath); + $reader = new FileReader($file); + $reader->readInto($buffer); + $buffer = trim($buffer); + //$buffer = "PHING version 1.0, Released 2002-??-??"; + $phingVersion = $buffer; + } catch (IOException $iox) { + throw new ConfigurationException("Can't read version information file"); + } + return $phingVersion; + } + + /** + * Print the project description, if any + */ + public static function printDescription(Project $project) { + if ($project->getDescription() !== null) { + self::$out->write($project->getDescription() . PHP_EOL); + } + } + + /** Print out a list of all targets in the current buildfile */ + function printTargets($project) { + // find the target with the longest name + $maxLength = 0; + $targets = $project->getTargets(); + $targetNames = array_keys($targets); + $targetName = null; + $targetDescription = null; + $currentTarget = null; + + // split the targets in top-level and sub-targets depending + // on the presence of a description + + $subNames = array(); + $topNameDescMap = array(); + + foreach($targets as $currentTarget) { + $targetName = $currentTarget->getName(); + $targetDescription = $currentTarget->getDescription(); + if ($currentTarget->isHidden()) { + continue; + } + + // subtargets are targets w/o descriptions + if ($targetDescription === null) { + $subNames[] = $targetName; + } else { + // topNames and topDescriptions are handled later + // here we store in hash map (for sorting purposes) + $topNameDescMap[$targetName] = $targetDescription; + if (strlen($targetName) > $maxLength) { + $maxLength = strlen($targetName); + } + } + } + + // Sort the arrays + sort($subNames); // sort array values, resetting keys (which are numeric) + ksort($topNameDescMap); // sort the keys (targetName) keeping key=>val associations + + $topNames = array_keys($topNameDescMap); + $topDescriptions = array_values($topNameDescMap); + + $defaultTarget = $project->getDefaultTarget(); + + if ($defaultTarget !== null && $defaultTarget !== "") { + $defaultName = array(); + $defaultDesc = array(); + $defaultName[] = $defaultTarget; + + $indexOfDefDesc = array_search($defaultTarget, $topNames, true); + if ($indexOfDefDesc !== false && $indexOfDefDesc >= 0) { + $defaultDesc = array(); + $defaultDesc[] = $topDescriptions[$indexOfDefDesc]; + } + + $this->_printTargets($defaultName, $defaultDesc, "Default target:", $maxLength); + + } + $this->_printTargets($topNames, $topDescriptions, "Main targets:", $maxLength); + $this->_printTargets($subNames, null, "Subtargets:", 0); + } + + /** + * Writes a formatted list of target names with an optional description. + * + * @param array $names The names to be printed. + * Must not be <code>null</code>. + * @param array $descriptions The associated target descriptions. + * May be <code>null</code>, in which case + * no descriptions are displayed. + * If non-<code>null</code>, this should have + * as many elements as <code>names</code>. + * @param string $heading The heading to display. + * Should not be <code>null</code>. + * @param int $maxlen The maximum length of the names of the targets. + * If descriptions are given, they are padded to this + * position so they line up (so long as the names really + * <i>are</i> shorter than this). + */ + private function _printTargets($names, $descriptions, $heading, $maxlen) { + + $spaces = ' '; + while (strlen($spaces) < $maxlen) { + $spaces .= $spaces; + } + $msg = ""; + $msg .= $heading . PHP_EOL; + $msg .= str_repeat("-",79) . PHP_EOL; + + $total = count($names); + for($i=0; $i < $total; $i++) { + $msg .= " "; + $msg .= $names[$i]; + if (!empty($descriptions)) { + $msg .= substr($spaces, 0, $maxlen - strlen($names[$i]) + 2); + $msg .= $descriptions[$i]; + } + $msg .= PHP_EOL; + } + if ($total > 0) { + self::$out->write($msg . PHP_EOL); + } + } + + /** + * Import a dot-path notation class path. + * @param string $dotPath + * @param mixed $classpath String or object supporting __toString() + * @return string The unqualified classname (which can be instantiated). + * @throws BuildException - if cannot find the specified file + */ + public static function import($dotPath, $classpath = null) { + + /// check if this is a PEAR-style path (@link http://pear.php.net/manual/en/standards.naming.php) + if (strpos($dotPath, '.') === false && strpos($dotPath, '_') !== false) { + $classname = $dotPath; + $dotPath = str_replace('_', '.', $dotPath); + } else { + $classname = StringHelper::unqualify($dotPath); + } + + // first check to see that the class specified hasn't already been included. + // (this also handles case where this method is called w/ a classname rather than dotpath) + if (class_exists($classname)) { + return $classname; + } + + $dotClassname = basename($dotPath); + $dotClassnamePos = strlen($dotPath) - strlen($dotClassname); + + // 1- temporarily replace escaped '.' with another illegal char (#) + $tmp = str_replace('\.', '##', $dotClassname); + // 2- swap out the remaining '.' with DIR_SEP + $tmp = strtr($tmp, '.', DIRECTORY_SEPARATOR); + // 3- swap back the escaped '.' + $tmp = str_replace('##', '.', $tmp); + + $classFile = $tmp . ".php"; + + $path = substr_replace($dotPath, $classFile, $dotClassnamePos); + + Phing::__import($path, $classpath); + + return $classname; + } + + /** + * Import a PHP file + * @param string $path Path to the PHP file + * @param mixed $classpath String or object supporting __toString() + * @throws BuildException - if cannot find the specified file + */ + public static function __import($path, $classpath = null) { + + if ($classpath) { + + // Apparently casting to (string) no longer invokes __toString() automatically. + if (is_object($classpath)) { + $classpath = $classpath->__toString(); + } + + // classpaths are currently additive, but we also don't want to just + // indiscriminantly prepand/append stuff to the include_path. This means + // we need to parse current incldue_path, and prepend any + // specified classpath locations that are not already in the include_path. + // + // NOTE: the reason why we do it this way instead of just changing include_path + // and then changing it back, is that in many cases applications (e.g. Propel) will + // include/require class files from within method calls. This means that not all + // necessary files will be included in this import() call, and hence we can't + // change the include_path back without breaking those apps. While this method could + // be more expensive than switching & switching back (not sure, but maybe), it makes it + // possible to write far less expensive run-time applications (e.g. using Propel), which is + // really where speed matters more. + + $curr_parts = explode(PATH_SEPARATOR, get_include_path()); + $add_parts = explode(PATH_SEPARATOR, $classpath); + $new_parts = array_diff($add_parts, $curr_parts); + if ($new_parts) { + set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts))); + } + } + + $ret = include_once($path); + + if ($ret === false) { + $msg = "Error importing $path"; + if (self::getMsgOutputLevel() >= Project::MSG_DEBUG) { + $x = new Exception("for-path-trace-only"); + $msg .= $x->getTraceAsString(); + } + throw new ConfigurationException($msg); + } + } + + /** + * Looks on include path for specified file. + * @return string File found (null if no file found). + */ + public static function getResourcePath($path) { + + if (self::$importPaths === null) { + $paths = get_include_path(); + self::$importPaths = explode(PATH_SEPARATOR, ini_get("include_path")); + } + + $path = str_replace('\\', DIRECTORY_SEPARATOR, $path); + $path = str_replace('/', DIRECTORY_SEPARATOR, $path); + + foreach (self::$importPaths as $prefix) { + $testPath = $prefix . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } + + // Check for the property phing.home + $homeDir = self::getProperty('phing.home'); + if ($homeDir) { + $testPath = $homeDir . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } + + // If we are using this via PEAR then check for the file in the data dir + // This is a bit of a hack, but works better than previous solution of assuming + // data_dir is on the include_path. + $dataDir = '@DATA-DIR@'; + if ($dataDir{0} != '@') { // if we're using PEAR then the @ DATA-DIR @ token will have been substituted. + if (!file_exists($dataDir)) { + self::log("The PEAR data_dir setting is incorrect: {$dataDir}.", Project::MSG_ERR); + self::log("Please edit using 'pear config-set data_dir ...' and re-install Phing.", Project::MSG_ERR); + return null; + } + + $testPath = $dataDir . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } else { + // We're not using PEAR, so do one additional check based on path of + // current file (Phing.php) + $maybeHomeDir = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..'); + $testPath = $maybeHomeDir . DIRECTORY_SEPARATOR . $path; + if (file_exists($testPath)) { + return $testPath; + } + } + + return null; + } + + // ------------------------------------------------------------------------------------------- + // System-wide methods (moved from System class, which had namespace conflicts w/ PEAR System) + // ------------------------------------------------------------------------------------------- + + /** + * Set System constants which can be retrieved by calling Phing::getProperty($propName). + * @return void + */ + private static function setSystemConstants() { + + /* + * PHP_OS returns on + * WindowsNT4.0sp6 => WINNT + * Windows2000 => WINNT + * Windows ME => WIN32 + * Windows 98SE => WIN32 + * FreeBSD 4.5p7 => FreeBSD + * Redhat Linux => Linux + * Mac OS X => Darwin + */ + self::setProperty('host.os', PHP_OS); + + // this is used by some tasks too + self::setProperty('os.name', PHP_OS); + + // it's still possible this won't be defined, + // e.g. if Phing is being included in another app w/o + // using the phing.php script. + if (!defined('PHP_CLASSPATH')) { + define('PHP_CLASSPATH', get_include_path()); + } + + self::setProperty('php.classpath', PHP_CLASSPATH); + + // try to determine the host filesystem and set system property + // used by Fileself::getFileSystem to instantiate the correct + // abstraction layer + + switch (strtoupper(PHP_OS)) { + case 'WINNT': + self::setProperty('host.fstype', 'WINNT'); + break; + case 'WIN32': + self::setProperty('host.fstype', 'WIN32'); + break; + default: + self::setProperty('host.fstype', 'UNIX'); + break; + } + + self::setProperty('php.interpreter', getenv('PHP_COMMAND')); + self::setProperty('line.separator', PHP_EOL); + self::setProperty('php.version', PHP_VERSION); + self::setProperty('user.home', getenv('HOME')); + self::setProperty('application.startdir', getcwd()); + self::setProperty('phing.startTime', gmdate('D, d M Y H:i:s', time()) . ' GMT'); + + // try to detect machine dependent information + $sysInfo = array(); + if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && function_exists("posix_uname")) { + $sysInfo = posix_uname(); + } else { + $sysInfo['nodename'] = php_uname('n'); + $sysInfo['machine']= php_uname('m') ; + //this is a not so ideal substition, but maybe better than nothing + $sysInfo['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "unknown"; + $sysInfo['release'] = php_uname('r'); + $sysInfo['version'] = php_uname('v'); + } + + + self::setProperty("host.name", isset($sysInfo['nodename']) ? $sysInfo['nodename'] : "unknown"); + self::setProperty("host.arch", isset($sysInfo['machine']) ? $sysInfo['machine'] : "unknown"); + self::setProperty("host.domain",isset($sysInfo['domain']) ? $sysInfo['domain'] : "unknown"); + self::setProperty("host.os.release", isset($sysInfo['release']) ? $sysInfo['release'] : "unknown"); + self::setProperty("host.os.version", isset($sysInfo['version']) ? $sysInfo['version'] : "unknown"); + unset($sysInfo); + } + + /** + * This gets a property that was set via command line or otherwise passed into Phing. + * "Defined" in this case means "externally defined". The reason this method exists is to + * provide a public means of accessing commandline properties for (e.g.) logger or listener + * scripts. E.g. to specify which logfile to use, PearLogger needs to be able to access + * the pear.log.name property. + * + * @param string $name + * @return string value of found property (or null, if none found). + */ + public static function getDefinedProperty($name) { + return self::$definedProps->getProperty($name); + } + + /** + * This sets a property that was set via command line or otherwise passed into Phing. + * + * @param string $name + * @return string value of found property (or null, if none found). + */ + public static function setDefinedProperty($name, $value) { + return self::$definedProps->setProperty($name, $value); + } + + /** + * Returns property value for a System property. + * System properties are "global" properties like application.startdir, + * and user.dir. Many of these correspond to similar properties in Java + * or Ant. + * + * @param string $paramName + * @return string Value of found property (or null, if none found). + */ + public static function getProperty($propName) { + + // some properties are detemined on each access + // some are cached, see below + + // default is the cached value: + $val = isset(self::$properties[$propName]) ? self::$properties[$propName] : null; + + // special exceptions + switch($propName) { + case 'user.dir': + $val = getcwd(); + break; + } + + return $val; + } + + /** Retuns reference to all properties*/ + public static function &getProperties() { + return self::$properties; + } + + public static function setProperty($propName, $propValue) { + $propName = (string) $propName; + $oldValue = self::getProperty($propName); + self::$properties[$propName] = $propValue; + return $oldValue; + } + + public static function currentTimeMillis() { + list($usec, $sec) = explode(" ",microtime()); + return ((float)$usec + (float)$sec); + } + + /** + * Sets the include path to PHP_CLASSPATH constant (if this has been defined). + * @return void + * @throws ConfigurationException - if the include_path could not be set (for some bizarre reason) + */ + private static function setIncludePaths() { + if (defined('PHP_CLASSPATH')) { + $result = set_include_path(PHP_CLASSPATH); + if ($result === false) { + throw new ConfigurationException("Could not set PHP include_path."); + } + self::$origIniSettings['include_path'] = $result; // save original value for setting back later + } + } + + /** + * Converts shorthand notation values as returned by ini_get() + * @see http://www.php.net/ini_get + * @param string $val + */ + private static function convertShorthand($val) + { + $val = trim($val); + $last = strtolower($val[strlen($val) - 1]); + + switch($last) { + // The 'G' modifier is available since PHP 5.1.0 + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } + + /** + * Sets PHP INI values that Phing needs. + * @return void + */ + private static function setIni() { + + self::$origIniSettings['error_reporting'] = error_reporting(E_ALL); + + // We won't bother storing original max_execution_time, since 1) the value in + // php.ini may be wrong (and there's no way to get the current value) and + // 2) it would mean something very strange to set it to a value less than time script + // has already been running, which would be the likely change. + + set_time_limit(0); + + self::$origIniSettings['magic_quotes_gpc'] = ini_set('magic_quotes_gpc', 'off'); + self::$origIniSettings['short_open_tag'] = ini_set('short_open_tag', 'off'); + self::$origIniSettings['default_charset'] = ini_set('default_charset', 'iso-8859-1'); + self::$origIniSettings['register_globals'] = ini_set('register_globals', 'off'); + self::$origIniSettings['allow_call_time_pass_reference'] = ini_set('allow_call_time_pass_reference', 'on'); + self::$origIniSettings['track_errors'] = ini_set('track_errors', 1); + + $mem_limit = (int) self::convertShorthand(ini_get('memory_limit')); + if ($mem_limit < (32 * 1024 * 1024) && $mem_limit > -1) { + // We do *not* need to save the original value here, since we don't plan to restore + // this after shutdown (we don't trust the effectiveness of PHP's garbage collection). + ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects + } + } + + /** + * Restores [most] PHP INI values to their pre-Phing state. + * + * Currently the following settings are not restored: + * - max_execution_time (because getting current time limit is not possible) + * - memory_limit (which may have been increased by Phing) + * + * @return void + */ + private static function restoreIni() + { + foreach(self::$origIniSettings as $settingName => $settingValue) { + switch($settingName) { + case 'error_reporting': + error_reporting($settingValue); + break; + default: + ini_set($settingName, $settingValue); + } + } + } + + /** + * Returns reference to Timer object. + * @return Timer + */ + public static function getTimer() { + if (self::$timer === null) { + include_once 'phing/system/util/Timer.php'; + self::$timer= new Timer(); + } + return self::$timer; + } + + /** + * Start up Phing. + * Sets up the Phing environment but does not initiate the build process. + * @return void + * @throws Exception - If the Phing environment cannot be initialized. + */ + public static function startup() { + + // setup STDOUT and STDERR defaults + self::initializeOutputStreams(); + + // some init stuff + self::getTimer()->start(); + + self::setSystemConstants(); + self::setIncludePaths(); + self::setIni(); + } + + /** + * Halts the system. + * @deprecated This method is deprecated and is no longer called by Phing internally. Any + * normal shutdown routines are handled by the shutdown() method. + * @see shutdown() + */ + public static function halt() { + self::shutdown(); + } + + /** + * Performs any shutdown routines, such as stopping timers. + * @return void + */ + public static function shutdown() { + self::restoreIni(); + self::getTimer()->stop(); + } + +} diff --git a/buildscripts/phing/classes/phing/Project.php b/buildscripts/phing/classes/phing/Project.php new file mode 100755 index 00000000..8e662543 --- /dev/null +++ b/buildscripts/phing/classes/phing/Project.php @@ -0,0 +1,1050 @@ +<?php +/* + * $Id: 7e67218e8e616860f9c746f9ab600f523089ea2e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/TaskAdapter.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/BuildEvent.php'; +include_once 'phing/input/DefaultInputHandler.php'; + +/** + * The Phing project class. Represents a completely configured Phing project. + * The class defines the project and all tasks/targets. It also contains + * methods to start a build as well as some properties and FileSystem + * abstraction. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: 7e67218e8e616860f9c746f9ab600f523089ea2e $ + * @package phing + */ +class Project { + + // Logging level constants. + const MSG_DEBUG = 4; + const MSG_VERBOSE = 3; + const MSG_INFO = 2; + const MSG_WARN = 1; + const MSG_ERR = 0; + + /** contains the targets */ + private $targets = array(); + /** global filterset (future use) */ + private $globalFilterSet = array(); + /** all globals filters (future use) */ + private $globalFilters = array(); + + /** Project properties map (usually String to String). */ + private $properties = array(); + + /** + * Map of "user" properties (as created in the Ant task, for example). + * Note that these key/value pairs are also always put into the + * project properties, so only the project properties need to be queried. + * Mapping is String to String. + */ + private $userProperties = array(); + + /** + * Map of inherited "user" properties - that are those "user" + * properties that have been created by tasks and not been set + * from the command line or a GUI tool. + * Mapping is String to String. + */ + private $inheritedProperties = array(); + + /** task definitions for this project*/ + private $taskdefs = array(); + + /** type definitions for this project */ + private $typedefs = array(); + + /** holds ref names and a reference to the referred object*/ + private $references = array(); + + /** The InputHandler being used by this project. */ + private $inputHandler; + + /* -- properties that come in via xml attributes -- */ + + /** basedir (PhingFile object) */ + private $basedir; + + /** the default target name */ + private $defaultTarget = 'all'; + + /** project name (required) */ + private $name; + + /** project description */ + private $description; + + /** require phing version */ + private $phingVersion; + + /** a FileUtils object */ + private $fileUtils; + + /** Build listeneers */ + private $listeners = array(); + + /** + * Constructor, sets any default vars. + */ + public function __construct() { + $this->fileUtils = new FileUtils(); + $this->inputHandler = new DefaultInputHandler(); + } + + /** + * Sets the input handler + * @param InputHandler $handler + */ + public function setInputHandler(InputHandler $handler) { + $this->inputHandler = $handler; + } + + /** + * Retrieves the current input handler. + * @return InputHandler + */ + public function getInputHandler() { + return $this->inputHandler; + } + + /** inits the project, called from main app */ + public function init() { + // set builtin properties + $this->setSystemProperties(); + + // load default tasks + $taskdefs = Phing::getResourcePath("phing/tasks/defaults.properties"); + + try { // try to load taskdefs + $props = new Properties(); + $in = new PhingFile((string)$taskdefs); + + if ($in === null) { + throw new BuildException("Can't load default task list"); + } + $props->load($in); + + $enum = $props->propertyNames(); + foreach($enum as $key) { + $value = $props->getProperty($key); + $this->addTaskDefinition($key, $value); + } + } catch (IOException $ioe) { + throw new BuildException("Can't load default task list"); + } + + // load default tasks + $typedefs = Phing::getResourcePath("phing/types/defaults.properties"); + + try { // try to load typedefs + $props = new Properties(); + $in = new PhingFile((string)$typedefs); + if ($in === null) { + throw new BuildException("Can't load default datatype list"); + } + $props->load($in); + + $enum = $props->propertyNames(); + foreach($enum as $key) { + $value = $props->getProperty($key); + $this->addDataTypeDefinition($key, $value); + } + } catch(IOException $ioe) { + throw new BuildException("Can't load default datatype list"); + } + } + + /** returns the global filterset (future use) */ + public function getGlobalFilterSet() { + return $this->globalFilterSet; + } + + // --------------------------------------------------------- + // Property methods + // --------------------------------------------------------- + + /** + * Sets a property. Any existing property of the same name + * is overwritten, unless it is a user property. + * @param string $name The name of property to set. + * Must not be <code>null</code>. + * @param string $value The new value of the property. + * Must not be <code>null</code>. + * @return void + */ + public function setProperty($name, $value) { + + // command line properties take precedence + if (isset($this->userProperties[$name])) { + $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE); + return; + } + + if (isset($this->properties[$name])) { + $this->log("Overriding previous definition of property " . $name, Project::MSG_VERBOSE); + } + + $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG); + $this->properties[$name] = $value; + } + + /** + * Sets a property if no value currently exists. If the property + * exists already, a message is logged and the method returns with + * no other effect. + * + * @param string $name The name of property to set. + * Must not be <code>null</code>. + * @param string $value The new value of the property. + * Must not be <code>null</code>. + * @since 2.0 + */ + public function setNewProperty($name, $value) { + if (isset($this->properties[$name])) { + $this->log("Override ignored for property " . $name, Project::MSG_DEBUG); + return; + } + $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG); + $this->properties[$name] = $value; + } + + /** + * Sets a user property, which cannot be overwritten by + * set/unset property calls. Any previous value is overwritten. + * @param string $name The name of property to set. + * Must not be <code>null</code>. + * @param string $value The new value of the property. + * Must not be <code>null</code>. + * @see #setProperty() + */ + public function setUserProperty($name, $value) { + $this->log("Setting ro project property: " . $name . " -> " . $value, Project::MSG_DEBUG); + $this->userProperties[$name] = $value; + $this->properties[$name] = $value; + } + + /** + * Sets a user property, which cannot be overwritten by set/unset + * property calls. Any previous value is overwritten. Also marks + * these properties as properties that have not come from the + * command line. + * + * @param string $name The name of property to set. + * Must not be <code>null</code>. + * @param string $value The new value of the property. + * Must not be <code>null</code>. + * @see #setProperty() + */ + public function setInheritedProperty($name, $value) { + $this->inheritedProperties[$name] = $value; + $this->setUserProperty($name, $value); + } + + /** + * Sets a property unless it is already defined as a user property + * (in which case the method returns silently). + * + * @param name The name of the property. + * Must not be <code>null</code>. + * @param value The property value. Must not be <code>null</code>. + */ + private function setPropertyInternal($name, $value) { + if (isset($this->userProperties[$name])) { + $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE); + return; + } + $this->properties[$name] = $value; + } + + /** + * Returns the value of a property, if it is set. + * + * @param string $name The name of the property. + * May be <code>null</code>, in which case + * the return value is also <code>null</code>. + * @return string The property value, or <code>null</code> for no match + * or if a <code>null</code> name is provided. + */ + public function getProperty($name) { + if (!isset($this->properties[$name])) { + return null; + } + $found = $this->properties[$name]; + // check to see if there are unresolved property references + if (false !== strpos($found, '${')) { + // attempt to resolve properties + $found = $this->replaceProperties($found); + // save resolved value + $this->properties[$name] = $found; + } + return $found; + } + + /** + * Replaces ${} style constructions in the given value with the + * string value of the corresponding data types. + * + * @param value The string to be scanned for property references. + * May be <code>null</code>. + * + * @return the given string with embedded property names replaced + * by values, or <code>null</code> if the given string is + * <code>null</code>. + * + * @exception BuildException if the given value has an unclosed + * property name, e.g. <code>${xxx</code> + */ + public function replaceProperties($value) { + return ProjectConfigurator::replaceProperties($this, $value, $this->properties); + } + + /** + * Returns the value of a user property, if it is set. + * + * @param string $name The name of the property. + * May be <code>null</code>, in which case + * the return value is also <code>null</code>. + * @return string The property value, or <code>null</code> for no match + * or if a <code>null</code> name is provided. + */ + public function getUserProperty($name) { + if (!isset($this->userProperties[$name])) { + return null; + } + return $this->userProperties[$name]; + } + + /** + * Returns a copy of the properties table. + * @return array A hashtable containing all properties + * (including user properties). + */ + public function getProperties() { + return $this->properties; + } + + /** + * Returns a copy of the user property hashtable + * @return a hashtable containing just the user properties + */ + public function getUserProperties() { + return $this->userProperties; + } + + /** + * Copies all user properties that have been set on the command + * line or a GUI tool from this instance to the Project instance + * given as the argument. + * + * <p>To copy all "user" properties, you will also have to call + * {@link #copyInheritedProperties copyInheritedProperties}.</p> + * + * @param Project $other the project to copy the properties to. Must not be null. + * @return void + * @since phing 2.0 + */ + public function copyUserProperties(Project $other) { + foreach($this->userProperties as $arg => $value) { + if (isset($this->inheritedProperties[$arg])) { + continue; + } + $other->setUserProperty($arg, $value); + } + } + + /** + * Copies all user properties that have not been set on the + * command line or a GUI tool from this instance to the Project + * instance given as the argument. + * + * <p>To copy all "user" properties, you will also have to call + * {@link #copyUserProperties copyUserProperties}.</p> + * + * @param Project $other the project to copy the properties to. Must not be null. + * + * @since phing 2.0 + */ + public function copyInheritedProperties(Project $other) { + foreach($this->userProperties as $arg => $value) { + if ($other->getUserProperty($arg) !== null) { + continue; + } + $other->setInheritedProperty($arg, $value); + } + } + + // --------------------------------------------------------- + // END Properties methods + // --------------------------------------------------------- + + + /** + * Sets default target + * @param string $targetName + */ + public function setDefaultTarget($targetName) { + $this->defaultTarget = (string) trim($targetName); + } + + /** + * Returns default target + * @return string + */ + public function getDefaultTarget() { + return (string) $this->defaultTarget; + } + + /** + * Sets the name of the current project + * + * @param string $name name of project + * @return void + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + public function setName($name) { + $this->name = (string) trim($name); + $this->setProperty("phing.project.name", $this->name); + } + + /** + * Returns the name of this project + * + * @return string projectname + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + public function getName() { + return (string) $this->name; + } + + /** + * Set the projects description + * @param string $description + */ + public function setDescription($description) { + $this->description = (string) trim($description); + } + + /** + * return the description, null otherwise + * @return string|null + */ + public function getDescription() { + return $this->description; + } + + /** + * Set the minimum required phing version + * @param string $version + */ + public function setPhingVersion($version) { + $version = str_replace('phing', '', strtolower($version)); + $this->phingVersion = (string)trim($version); + } + + /** + * Get the minimum required phing version + * @return string + */ + public function getPhingVersion() { + if($this->phingVersion === null) { + $this->setPhingVersion(Phing::getPhingVersion()); + } + return $this->phingVersion; + } + + /** + * Set basedir object from xm + * @param PhingFile|string $dir + */ + public function setBasedir($dir) { + if ($dir instanceof PhingFile) { + $dir = $dir->getAbsolutePath(); + } + + $dir = $this->fileUtils->normalize($dir); + + $dir = new PhingFile((string) $dir); + if (!$dir->exists()) { + throw new BuildException("Basedir ".$dir->getAbsolutePath()." does not exist"); + } + if (!$dir->isDirectory()) { + throw new BuildException("Basedir ".$dir->getAbsolutePath()." is not a directory"); + } + $this->basedir = $dir; + $this->setPropertyInternal("project.basedir", $this->basedir->getAbsolutePath()); + $this->log("Project base dir set to: " . $this->basedir->getPath(), Project::MSG_VERBOSE); + + // [HL] added this so that ./ files resolve correctly. This may be a mistake ... or may be in wrong place. + chdir($dir->getAbsolutePath()); + } + + /** + * Returns the basedir of this project + * + * @return PhingFile Basedir PhingFile object + * @access public + * @throws BuildException + * @author Andreas Aderhold, andi@binarycloud.com + */ + public function getBasedir() { + if ($this->basedir === null) { + try { // try to set it + $this->setBasedir("."); + } catch (BuildException $exc) { + throw new BuildException("Can not set default basedir. ".$exc->getMessage()); + } + } + return $this->basedir; + } + + /** + * Sets system properties and the environment variables for this project. + * + * @return void + */ + public function setSystemProperties() { + + // first get system properties + $systemP = array_merge( self::getProperties(), Phing::getProperties() ); + foreach($systemP as $name => $value) { + $this->setPropertyInternal($name, $value); + } + + // and now the env vars + foreach($_SERVER as $name => $value) { + // skip arrays + if (is_array($value)) { + continue; + } + $this->setPropertyInternal('env.' . $name, $value); + } + return true; + } + + + /** + * Adds a task definition. + * @param string $name Name of tag. + * @param string $class The class path to use. + * @param string $classpath The classpat to use. + */ + public function addTaskDefinition($name, $class, $classpath = null) { + $name = $name; + $class = $class; + if ($class === "") { + $this->log("Task $name has no class defined.", Project::MSG_ERR); + } elseif (!isset($this->taskdefs[$name])) { + Phing::import($class, $classpath); + $this->taskdefs[$name] = $class; + $this->log(" +Task definiton: $name ($class)", Project::MSG_DEBUG); + } else { + $this->log("Task $name ($class) already registerd, skipping", Project::MSG_VERBOSE); + } + } + + /** + * Returns the task definitions + * @return array + */ + public function getTaskDefinitions() { + return $this->taskdefs; + } + + /** + * Adds a data type definition. + * @param string $typeName Name of the type. + * @param string $typeClass The class to use. + * @param string $classpath The classpath to use. + */ + public function addDataTypeDefinition($typeName, $typeClass, $classpath = null) { + if (!isset($this->typedefs[$typeName])) { + Phing::import($typeClass, $classpath); + $this->typedefs[$typeName] = $typeClass; + $this->log(" +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG); + } else { + $this->log("Type $typeName ($typeClass) already registerd, skipping", Project::MSG_VERBOSE); + } + } + + /** + * Returns the data type definitions + * @return array + */ + public function getDataTypeDefinitions() { + return $this->typedefs; + } + + /** + * Add a new target to the project + * @param string $targetName + * @param Target $target + */ + public function addTarget($targetName, &$target) { + if (isset($this->targets[$targetName])) { + throw new BuildException("Duplicate target: $targetName"); + } + $this->addOrReplaceTarget($targetName, $target); + } + + /** + * Adds or replaces a target in the project + * @param string $targetName + * @param Target $target + */ + public function addOrReplaceTarget($targetName, &$target) { + $this->log(" +Target: $targetName", Project::MSG_DEBUG); + $target->setProject($this); + $this->targets[$targetName] = $target; + + $ctx = $this->getReference("phing.parsing.context"); + $current = $ctx->getConfigurator()->getCurrentTargets(); + $current[$targetName] = $target; + } + + /** + * Returns the available targets + * @return array + */ + public function getTargets() { + return $this->targets; + } + + /** + * Create a new task instance and return reference to it. This method is + * sorta factory like. A _local_ instance is created and a reference returned to + * that instance. Usually PHP destroys local variables when the function call + * ends. But not if you return a reference to that variable. + * This is kinda error prone, because if no reference exists to the variable + * it is destroyed just like leaving the local scope with primitive vars. There's no + * central place where the instance is stored as in other OOP like languages. + * + * [HL] Well, ZE2 is here now, and this is still working. We'll leave this alone + * unless there's any good reason not to. + * + * @param string $taskType Task name + * @return Task A task object + * @throws BuildException + * Exception + */ + public function createTask($taskType) { + try { + $classname = ""; + $tasklwr = strtolower($taskType); + foreach ($this->taskdefs as $name => $class) { + if (strtolower($name) === $tasklwr) { + $classname = $class; + break; + } + } + + if ($classname === "") { + return null; + } + + $cls = Phing::import($classname); + + if (!class_exists($cls)) { + throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)"); + } + + $o = new $cls(); + + if ($o instanceof Task) { + $task = $o; + } else { + $this->log (" (Using TaskAdapter for: $taskType)", Project::MSG_DEBUG); + // not a real task, try adapter + $taskA = new TaskAdapter(); + $taskA->setProxy($o); + $task = $taskA; + } + $task->setProject($this); + $task->setTaskType($taskType); + // set default value, can be changed by the user + $task->setTaskName($taskType); + $this->log (" +Task: " . $taskType, Project::MSG_DEBUG); + } catch (Exception $t) { + throw new BuildException("Could not create task of type: " . $taskType, $t); + } + // everything fine return reference + return $task; + } + + /** + * Create a datatype instance and return reference to it + * See createTask() for explanation how this works + * + * @param string $typeName Type name + * @return object A datatype object + * @throws BuildException + * Exception + */ + public function createDataType($typeName) { + try { + $cls = ""; + $typelwr = strtolower($typeName); + foreach ($this->typedefs as $name => $class) { + if (strtolower($name) === $typelwr) { + $cls = StringHelper::unqualify($class); + break; + } + } + + if ($cls === "") { + return null; + } + + if (!class_exists($cls)) { + throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)"); + } + + $type = new $cls(); + $this->log(" +Type: $typeName", Project::MSG_DEBUG); + if (!($type instanceof DataType)) { + throw new Exception("$class is not an instance of phing.types.DataType"); + } + if ($type instanceof ProjectComponent) { + $type->setProject($this); + } + } catch (Exception $t) { + throw new BuildException("Could not create type: $typeName", $t); + } + // everything fine return reference + return $type; + } + + /** + * Executes a list of targets + * + * @param array $targetNames List of target names to execute + * @return void + * @throws BuildException + */ + public function executeTargets($targetNames) { + foreach($targetNames as $tname) { + $this->executeTarget($tname); + } + } + + /** + * Executes a target + * + * @param string $targetName Name of Target to execute + * @return void + * @throws BuildException + */ + public function executeTarget($targetName) { + + // complain about executing void + if ($targetName === null) { + throw new BuildException("No target specified"); + } + + // invoke topological sort of the target tree and run all targets + // until targetName occurs. + $sortedTargets = $this->_topoSort($targetName, $this->targets); + + $curIndex = (int) 0; + $curTarget = null; + do { + try { + $curTarget = $sortedTargets[$curIndex++]; + $curTarget->performTasks(); + } catch (BuildException $exc) { + $this->log("Execution of target \"".$curTarget->getName()."\" failed for the following reason: ".$exc->getMessage(), Project::MSG_ERR); + throw $exc; + } + } while ($curTarget->getName() !== $targetName); + } + + /** + * Helper function + */ + public function resolveFile($fileName, $rootDir = null) { + if ($rootDir === null) { + return $this->fileUtils->resolveFile($this->basedir, $fileName); + } else { + return $this->fileUtils->resolveFile($rootDir, $fileName); + } + } + + /** + * Topologically sort a set of Targets. + * @param string $root is the (String) name of the root Target. The sort is + * created in such a way that the sequence of Targets until the root + * target is the minimum possible such sequence. + * @param array $targets is a array representing a "name to Target" mapping + * @return An array of Strings with the names of the targets in + * sorted order. + */ + public function _topoSort($root, &$targets) { + + $root = (string) $root; + $ret = array(); + $state = array(); + $visiting = array(); + + // We first run a DFS based sort using the root as the starting node. + // This creates the minimum sequence of Targets to the root node. + // We then do a sort on any remaining unVISITED targets. + // This is unnecessary for doing our build, but it catches + // circular dependencies or missing Targets on the entire + // dependency tree, not just on the Targets that depend on the + // build Target. + + $this->_tsort($root, $targets, $state, $visiting, $ret); + + $retHuman = ""; + for ($i=0, $_i=count($ret); $i < $_i; $i++) { + $retHuman .= $ret[$i]->toString()." "; + } + $this->log("Build sequence for target '$root' is: $retHuman", Project::MSG_VERBOSE); + + $keys = array_keys($targets); + while($keys) { + $curTargetName = (string) array_shift($keys); + if (!isset($state[$curTargetName])) { + $st = null; + } else { + $st = (string) $state[$curTargetName]; + } + + if ($st === null) { + $this->_tsort($curTargetName, $targets, $state, $visiting, $ret); + } elseif ($st === "VISITING") { + throw new Exception("Unexpected node in visiting state: $curTargetName"); + } + } + + $retHuman = ""; + for ($i=0,$_i=count($ret); $i < $_i; $i++) { + $retHuman .= $ret[$i]->toString()." "; + } + $this->log("Complete build sequence is: $retHuman", Project::MSG_VERBOSE); + + return $ret; + } + + // one step in a recursive DFS traversal of the target dependency tree. + // - The array "state" contains the state (VISITED or VISITING or null) + // of all the target names. + // - The stack "visiting" contains a stack of target names that are + // currently on the DFS stack. (NB: the target names in "visiting" are + // exactly the target names in "state" that are in the VISITING state.) + // 1. Set the current target to the VISITING state, and push it onto + // the "visiting" stack. + // 2. Throw a BuildException if any child of the current node is + // in the VISITING state (implies there is a cycle.) It uses the + // "visiting" Stack to construct the cycle. + // 3. If any children have not been VISITED, tsort() the child. + // 4. Add the current target to the Vector "ret" after the children + // have been visited. Move the current target to the VISITED state. + // "ret" now contains the sorted sequence of Targets upto the current + // Target. + + public function _tsort($root, &$targets, &$state, &$visiting, &$ret) { + $state[$root] = "VISITING"; + $visiting[] = $root; + + if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) { + $target = null; + } else { + $target = $targets[$root]; + } + + // make sure we exist + if ($target === null) { + $sb = "Target '$root' does not exist in this project."; + array_pop($visiting); + if (!empty($visiting)) { + $parent = (string) $visiting[count($visiting)-1]; + $sb .= " It is a dependency of target '$parent'."; + } + throw new BuildException($sb); + } + + $deps = $target->getDependencies(); + + while($deps) { + $cur = (string) array_shift($deps); + if (!isset($state[$cur])) { + $m = null; + } else { + $m = (string) $state[$cur]; + } + if ($m === null) { + // not been visited + $this->_tsort($cur, $targets, $state, $visiting, $ret); + } elseif ($m == "VISITING") { + // currently visiting this node, so have a cycle + throw $this->_makeCircularException($cur, $visiting); + } + } + + $p = (string) array_pop($visiting); + if ($root !== $p) { + throw new Exception("Unexpected internal error: expected to pop $root but got $p"); + } + + $state[$root] = "VISITED"; + $ret[] = $target; + } + + public function _makeCircularException($end, $stk) { + $sb = "Circular dependency: $end"; + do { + $c = (string) array_pop($stk); + $sb .= " <- ".$c; + } while($c != $end); + return new BuildException($sb); + } + + /** + * Adds a reference to an object. This method is called when the parser + * detects a id="foo" attribute. It passes the id as $name and a reference + * to the object assigned to this id as $value + * @param string $name + * @param object $object + */ + public function addReference($name, $object) { + if (isset($this->references[$name])) { + $this->log("Overriding previous definition of reference to $name", Project::MSG_WARN); + } + $this->log("Adding reference: $name -> ".get_class($object), Project::MSG_DEBUG); + $this->references[$name] = $object; + } + + /** + * Returns the references array. + * @return array + */ + public function getReferences() { + return $this->references; + } + + /** + * Returns a specific reference. + * @param string $key The reference id/key. + * @return object Reference or null if not defined + */ + public function getReference($key) + { + if (isset($this->references[$key])) { + return $this->references[$key]; + } + return null; // just to be explicit + } + + /** + * Abstracting and simplifyling Logger calls for project messages + * @param string $msg + * @param int $level + */ + public function log($msg, $level = Project::MSG_INFO) { + $this->logObject($this, $msg, $level); + } + + function logObject($obj, $msg, $level) { + $this->fireMessageLogged($obj, $msg, $level); + } + + function addBuildListener(BuildListener $listener) { + $this->listeners[] = $listener; + } + + function removeBuildListener(BuildListener $listener) { + $newarray = array(); + for ($i=0, $size=count($this->listeners); $i < $size; $i++) { + if ($this->listeners[$i] !== $listener) { + $newarray[] = $this->listeners[$i]; + } + } + $this->listeners = $newarray; + } + + function getBuildListeners() { + return $this->listeners; + } + + function fireBuildStarted() { + $event = new BuildEvent($this); + foreach($this->listeners as $listener) { + $listener->buildStarted($event); + } + } + + function fireBuildFinished($exception) { + $event = new BuildEvent($this); + $event->setException($exception); + foreach($this->listeners as $listener) { + $listener->buildFinished($event); + } + } + + function fireTargetStarted($target) { + $event = new BuildEvent($target); + foreach($this->listeners as $listener) { + $listener->targetStarted($event); + } + } + + function fireTargetFinished($target, $exception) { + $event = new BuildEvent($target); + $event->setException($exception); + foreach($this->listeners as $listener) { + $listener->targetFinished($event); + } + } + + function fireTaskStarted($task) { + $event = new BuildEvent($task); + foreach($this->listeners as $listener) { + $listener->taskStarted($event); + } + } + + function fireTaskFinished($task, $exception) { + $event = new BuildEvent($task); + $event->setException($exception); + foreach($this->listeners as $listener) { + $listener->taskFinished($event); + } + } + + function fireMessageLoggedEvent($event, $message, $priority) { + $event->setMessage($message, $priority); + foreach($this->listeners as $listener) { + $listener->messageLogged($event); + } + } + + function fireMessageLogged($object, $message, $priority) { + $this->fireMessageLoggedEvent(new BuildEvent($object), $message, $priority); + } +} diff --git a/buildscripts/phing/classes/phing/ProjectComponent.php b/buildscripts/phing/classes/phing/ProjectComponent.php new file mode 100755 index 00000000..343e3a6c --- /dev/null +++ b/buildscripts/phing/classes/phing/ProjectComponent.php @@ -0,0 +1,70 @@ +<?php +/* + * $Id: 9b2bbe8e58b0a7de3d426b1dcc88b8cac4bc69e2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Abstract class providing properties and methods common to all + * the project components + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing + */ +abstract class ProjectComponent { + + /** + * Holds a reference to the project that a project component + * (a task, a target, etc.) belongs to + * + * @var Project A reference to the current project instance + */ + protected $project = null; + + /** + * References the project to the current component. + * + * @param Project $project The reference to the current project + */ + public function setProject($project) { + $this->project = $project; + } + + /** + * Returns a reference to current project + * + * @return Project Reference to current porject object + */ + public function getProject() { + return $this->project; + } + + /** + * Logs a message with the given priority. + * + * @param string $msg The message to be logged. + * @param integer $level The message's priority at this message should have + */ + public function log($msg, $level = Project::MSG_INFO) { + if ($this->project !== null) { + $this->project->log($msg, $level); + } + } +} diff --git a/buildscripts/phing/classes/phing/RuntimeConfigurable.php b/buildscripts/phing/classes/phing/RuntimeConfigurable.php new file mode 100755 index 00000000..305a35f3 --- /dev/null +++ b/buildscripts/phing/classes/phing/RuntimeConfigurable.php @@ -0,0 +1,116 @@ +<?php +/* + * $Id: 5dd4f1cae5f6da32a48370cf9cfaa39e1124c91c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Wrapper class that holds the attributes of a Task (or elements + * nested below that level) and takes care of configuring that element + * at runtime. + * + * <strong>SMART-UP INLINE DOCS</strong> + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing + */ +class RuntimeConfigurable { + + private $elementTag = null; + private $children = array(); + private $wrappedObject = null; + private $attributes = array(); + private $characters = ""; + + + /** @param proxy The element to wrap. */ + function __construct($proxy, $elementTag) { + $this->wrappedObject = $proxy; + $this->elementTag = $elementTag; + } + + function setProxy($proxy) { + $this->wrappedObject = $proxy; + } + + /** Set's the attributes for the wrapped element. */ + function setAttributes($attributes) { + $this->attributes = $attributes; + } + + /** Returns the AttributeList of the wrapped element. */ + function getAttributes() { + return $this->attributes; + } + + /** Adds child elements to the wrapped element. */ + function addChild(RuntimeConfigurable $child) { + $this->children[] = $child; + } + + /** Returns the child with index */ + function getChild($index) { + return $this->children[(int)$index]; + } + + /** Add characters from #PCDATA areas to the wrapped element. */ + function addText($data) { + $this->characters .= (string) $data; + } + + function getElementTag() { + return $this->elementTag; + } + + + /** Configure the wrapped element and all children. */ + function maybeConfigure(Project $project) { + $id = null; + + // DataType configured in ProjectConfigurator + // if ( is_a($this->wrappedObject, "DataType") ) + // return; + + if ($this->attributes || $this->characters) { + ProjectConfigurator::configure($this->wrappedObject, $this->attributes, $project); + + if (isset($this->attributes["id"])) { + $id = $this->attributes["id"]; + } + + if ($this->characters) { + ProjectConfigurator::addText($project, $this->wrappedObject, (string) $this->characters); + $this->characters=""; + } + if ($id !== null) { + $project->addReference($id, $this->wrappedObject); + } + } + + if ( is_array($this->children) && !empty($this->children) ) { + // Configure all child of this object ... + foreach($this->children as $child) { + $child->maybeConfigure($project); + ProjectConfigurator::storeChild($project, $this->wrappedObject, $child->wrappedObject, strtolower($child->getElementTag())); + } + } + } +} + diff --git a/buildscripts/phing/classes/phing/Target.php b/buildscripts/phing/classes/phing/Target.php new file mode 100755 index 00000000..342d11cb --- /dev/null +++ b/buildscripts/phing/classes/phing/Target.php @@ -0,0 +1,375 @@ +<?php +/* + * $Id: b6779ff7860ec7a7a84d74a40ffcc9efa75255f7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/TaskContainer.php'; + +/** + * The Target component. Carries all required target data. Implements the + * abstract class {@link TaskContainer} + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: b6779ff7860ec7a7a84d74a40ffcc9efa75255f7 $ + * @access public + * @see TaskContainer + * @package phing + */ + +class Target implements TaskContainer { + + /** + * Name of target + * @var string + */ + private $name; + + /** + * Dependencies + * @var array + */ + private $dependencies = array(); + + /** + * Holds objects of children of this target + * @var array + */ + private $children = array(); + + /** + * The if condition from xml + * @var string + */ + private $ifCondition = ""; + + /** + * The unless condition from xml + * @var string + */ + private $unlessCondition = ""; + + /** + * Description of this target + * @var string + */ + private $description; + + /** + * Whether to hide target in targets list (-list -p switches) + * @var boolean + */ + private $hidden = false; + + /** + * Rreference to project + * @var Project + */ + private $project; + + /** + * References the project to the current component. + * + * @param Project $project The reference to the current project + */ + public function setProject(Project $project) { + $this->project = $project; + } + + /** + * Returns reference to current project + * + * @return Project Reference to current porject object + */ + public function getProject() { + return $this->project; + } + + /** + * Sets the target dependencies from xml + * + * @param string $depends Comma separated list of targetnames that depend on + * this target + * @throws BuildException + */ + public function setDepends($depends) { + // explode should be faster than strtok + $deps = explode(',', $depends); + for ($i=0, $size=count($deps); $i < $size; $i++) { + $trimmed = trim($deps[$i]); + if ($trimmed === "") { + throw new BuildException("Syntax Error: Depend attribute for target ".$this->getName()." is malformed."); + } + $this->addDependency($trimmed); + } + } + + /** + * Adds a singular dependent target name to the list + * + * @param string $dependency The dependency target to add + * @access public + */ + public function addDependency($dependency) { + $this->dependencies[] = (string) $dependency; + } + + /** + * Returns reference to indexed array of the dependencies this target has. + * + * @return array Referece to target dependencoes + */ + public function getDependencies() { + return $this->dependencies; + } + + /** + * Sets the name of the target + * + * @param string $name Name of this target + */ + public function setName($name) { + $this->name = (string) $name; + } + + /** + * Returns name of this target. + * + * @return string The name of the target + * @access public + */ + public function getName() { + return (string) $this->name; + } + + /** + * Set target status. If true, target does not come in phing -list + * + * @param boolean $flag + * @return Target + */ + public function setHidden($flag) + { + $this->hidden = (boolean) $flag; + return $this; + } + + /** + * Get target status. If true, target does not come in phing -list + * + * @return boolean + */ + public function getHidden() + { + return $this->hidden; + } + + /** + * Alias for getHidden() + * + * @return boolean + */ + public function isHidden() + { + return $this->getHidden(); + } + + /** + * Adds a task element to the list of this targets child elements + * + * @param Task $task The task object to add + * @access public + */ + public function addTask(Task $task) { + $this->children[] = $task; + } + + /** + * Adds a runtime configurable element to the list of this targets child + * elements. + * + * @param RuntimeConfigurable $rtc The RuntimeConfigurable object + * @access public + */ + public function addDataType($rtc) { + $this->children[] = $rtc; + } + + /** + * Returns an array of all tasks this target has as childrens. + * + * The task objects are copied here. Don't use this method to modify + * task objects. + * + * @return array Task[] + */ + public function getTasks() { + $tasks = array(); + for ($i=0,$size=count($this->children); $i < $size; $i++) { + $tsk = $this->children[$i]; + if ($tsk instanceof Task) { + // note: we're copying objects here! + $tasks[] = clone $tsk; + } + } + return $tasks; + } + + /** + * Set the if-condition from the XML tag, if any. The property name given + * as parameter must be present so the if condition evaluates to true + * + * @param string $property The property name that has to be present + * @access public + */ + public function setIf($property) { + $this->ifCondition = ($property === null) ? "" : $property; + } + + /** + * Set the unless-condition from the XML tag, if any. The property name + * given as parameter must be present so the unless condition evaluates + * to true + * + * @param string $property The property name that has to be present + * @access public + */ + public function setUnless($property) { + $this->unlessCondition = ($property === null) ? "" : $property; + } + + /** + * Sets a textual description of this target. + * + * @param string $description The description text + */ + public function setDescription($description) { + if ($description !== null && strcmp($description, "") !== 0) { + $this->description = (string) $description; + } else { + $this->description = null; + } + } + + /** + * Returns the description of this target. + * + * @return string The description text of this target + */ + public function getDescription() { + return $this->description; + } + + /** + * Returns a string representation of this target. In our case it + * simply returns the target name field + * + * @return string The string representation of this target + */ + public function toString() { + return (string) $this->name; + } + + /** + * The entry point for this class. Does some checking, then processes and + * performs the tasks for this target. + */ + public function main() { + if ($this->testIfCondition() && $this->testUnlessCondition()) { + foreach($this->children as $o) { + if ($o instanceof Task) { + // child is a task + $o->perform(); + } else { + // child is a RuntimeConfigurable + $o->maybeConfigure($this->project); + } + } + } elseif (!$this->testIfCondition()) { + $this->project->log("Skipped target '".$this->name."' because property '".$this->ifCondition."' not set.", Project::MSG_VERBOSE); + } else { + $this->project->log("Skipped target '".$this->name."' because property '".$this->unlessCondition."' set.", Project::MSG_VERBOSE); + } + } + + /** + * Performs the tasks by calling the main method of this target that + * actually executes the tasks. + * + * This method is for ZE2 and used for proper exception handling of + * task exceptions. + */ + public function performTasks() { + try {// try to execute this target + $this->project->fireTargetStarted($this); + $this->main(); + $this->project->fireTargetFinished($this, $null=null); + } catch (BuildException $exc) { + // log here and rethrow + $this->project->fireTargetFinished($this, $exc); + throw $exc; + } + } + + /** + * Tests if the property set in ifConfiditon exists. + * + * @return boolean <code>true</code> if the property specified + * in <code>$this->ifCondition</code> exists; + * <code>false</code> otherwise + */ + private function testIfCondition() { + if ($this->ifCondition === "") { + return true; + } + + $properties = explode(",", $this->ifCondition); + + $result = true; + foreach ($properties as $property) { + $test = ProjectConfigurator::replaceProperties($this->getProject(), $property, $this->project->getProperties()); + $result = $result && ($this->project->getProperty($test) !== null); + } + + return $result; + } + + /** + * Tests if the property set in unlessCondition exists. + * + * @return boolean <code>true</code> if the property specified + * in <code>$this->unlessCondition</code> exists; + * <code>false</code> otherwise + */ + private function testUnlessCondition() { + if ($this->unlessCondition === "") { + return true; + } + + $properties = explode(",", $this->unlessCondition); + + $result = true; + foreach ($properties as $property) { + $test = ProjectConfigurator::replaceProperties($this->getProject(), $property, $this->project->getProperties()); + $result = $result && ($this->project->getProperty($test) === null); + } + return $result; + } + +} diff --git a/buildscripts/phing/classes/phing/Task.php b/buildscripts/phing/classes/phing/Task.php new file mode 100755 index 00000000..d2490a90 --- /dev/null +++ b/buildscripts/phing/classes/phing/Task.php @@ -0,0 +1,272 @@ +<?php +/* + * $Id: 65e76e7a86e08ce1f1d4ef0f7cda011bc9efee5e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; +include_once 'phing/RuntimeConfigurable.php'; + +/** + * The base class for all Tasks. + * + * Use {@link Project#createTask} to register a new Task. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @see Project#createTask() + * @package phing + */ +abstract class Task extends ProjectComponent { + + /** + * Owning Target object + * @var Target + */ + protected $target; + + /** + * Description of the task + * @var string + */ + protected $description; + + /** + * Internal taskname (req) + * @var string + */ + protected $taskType; + + /** + * Taskname for logger + * @var string + */ + protected $taskName; + + /** + * Stored buildfile location + * @var Location + */ + protected $location; + + /** + * Wrapper of the task + * @var RuntimeConfigurable + */ + protected $wrapper; + + /** + * Sets the owning target this task belongs to. + * + * @param Target Reference to owning target + */ + public function setOwningTarget(Target $target) { + $this->target = $target; + } + + /** + * Returns the owning target of this task. + * + * @return Target The target object that owns this task + */ + public function getOwningTarget() { + return $this->target; + } + + /** + * Returns the name of task, used only for log messages + * + * @return string Name of this task + */ + public function getTaskName() { + if ($this->taskName === null) { + // if no task name is set, then it's possible + // this task was created from within another task. We don't + // therefore know the XML tag name for this task, so we'll just + // use the class name stripped of "task" suffix. This is only + // for log messages, so we don't have to worry much about accuracy. + return preg_replace('/task$/i', '', get_class($this)); + } + return $this->taskName; + } + + /** + * Sets the name of this task for log messages + * + * @param string $name + * @return string A string representing the name of this task for log + */ + public function setTaskName($name) { + $this->taskName = (string) $name; + } + + /** + * Returns the name of the task under which it was invoked, + * usually the XML tagname + * + * @return string The type of this task (XML Tag) + */ + public function getTaskType() { + return $this->taskType; + } + + /** + * Sets the type of the task. Usually this is the name of the XML tag + * + * @param string The type of this task (XML Tag) + */ + public function setTaskType($name) { + $this->taskType = (string) $name; + } + + /** + * Returns a name + * @param string $slotName + */ + protected function getRegisterSlot($slotName) { + return Register::getSlot('task.' . $this->getTaskName() . '.' . $slotName); + } + + /** + * Provides a project level log event to the task. + * + * @param string The message to log + * @param integer The priority of the message + * @see BuildEvent + * @see BuildListener + */ + function log($msg, $level = Project::MSG_INFO) { + $this->project->logObject($this, $msg, $level); + } + + /** + * Sets a textual description of the task + * + * @param string $desc The text describing the task + */ + public function setDescription($desc) { + $this->description = $desc; + } + + /** + * Returns the textual description of the task + * + * @return string The text description of the task + */ + public function getDescription() { + return $this->description; + } + + /** + * Called by the parser to let the task initialize properly. + * Should throw a BuildException if something goes wrong with the build + * + * This is abstract here, but may not be overloaded by subclasses. + * + * @throws BuildException + */ + public function init() { + } + + /** + * Called by the project to let the task do it's work. This method may be + * called more than once, if the task is invoked more than once. For + * example, if target1 and target2 both depend on target3, then running + * <em>phing target1 target2</em> will run all tasks in target3 twice. + * + * Should throw a BuildException if someting goes wrong with the build + * + * This is abstract here. Must be overloaded by real tasks. + */ + abstract public function main(); + + /** + * Returns the location within the buildfile this task occurs. Used + * by {@link BuildException} to give detailed error messages. + * + * @return Location The location object describing the position of this + * task within the buildfile. + */ + function getLocation() { + return $this->location; + } + + /** + * Sets the location within the buildfile this task occurs. Called by + * the parser to set location information. + * + * @param Location $location The location object describing the position of this + * task within the buildfile. + */ + function setLocation(Location $location) { + $this->location = $location; + } + + /** + * Returns the wrapper object for runtime configuration + * + * @return RuntimeConfigurable The wrapper object used by this task + */ + function getRuntimeConfigurableWrapper() { + if ($this->wrapper === null) { + $this->wrapper = new RuntimeConfigurable($this, $this->getTaskName()); + } + return $this->wrapper; + } + + /** + * Sets the wrapper object this task should use for runtime + * configurable elements. + * + * @param RuntimeConfigurable $wrapper The wrapper object this task should use + */ + function setRuntimeConfigurableWrapper(RuntimeConfigurable $wrapper) { + $this->wrapper = $wrapper; + } + + /** + * Configure this task if it hasn't been done already. + */ + public function maybeConfigure() { + if ($this->wrapper !== null) { + $this->wrapper->maybeConfigure($this->project); + } + } + + /** + * Perfrom this task + */ + public function perform() { + + try { // try executing task + $this->project->fireTaskStarted($this); + $this->maybeConfigure(); + $this->main(); + $this->project->fireTaskFinished($this, $null=null); + } catch (Exception $exc) { + if ($exc instanceof BuildException) { + if ($exc->getLocation() === null) { + $exc->setLocation($this->getLocation()); + } + } + $this->project->fireTaskFinished($this, $exc); + throw $exc; + } + } +} diff --git a/buildscripts/phing/classes/phing/TaskAdapter.php b/buildscripts/phing/classes/phing/TaskAdapter.php new file mode 100755 index 00000000..ba323c56 --- /dev/null +++ b/buildscripts/phing/classes/phing/TaskAdapter.php @@ -0,0 +1,85 @@ +<?php +/* + * $Id: 8cd2a3322c659a5de7fcb47e21192730e5cc863d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Use introspection to "adapt" an arbitrary ( not extending Task, but with + * similar patterns). + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing + */ +class TaskAdapter extends Task { + + /** target object */ + private $proxy; + + /** + * Main entry point. + * @return void + */ + function main() { + + if (method_exists($this->proxy, "setProject")) { + try { // try to set project + $this->proxy->setProject($this->project); + } catch (Exception $ex) { + $this->log("Error setting project in " . get_class($this->proxy) . Project::MSG_ERR); + throw new BuildException($ex); + } + } else { + throw new Exception("Error setting project in class " . get_class($this->proxy)); + } + + if (method_exists($this->proxy, "main")) { + try { //try to call main + $this->proxy->main($this->project); + } catch (Exception $ex) { + $this->log("Error in " . get_class($this->proxy), Project::MSG_ERR); + $this->log($ex->getTraceAsString(), Project::MSG_DEBUG); + throw new BuildException($ex->getMessage()); + } + } else { + throw new BuildException("Your task-like class '" . get_class($this->proxy) ."' does not have a main() method"); + } + } + + /** + * Set the target object. + * @param object $o + * @return void + */ + function setProxy($o) { + $this->proxy = $o; + } + + /** + * Gets the target object. + * @return object + */ + function getProxy() { + return $this->proxy; + } + +} diff --git a/buildscripts/phing/classes/phing/TaskContainer.php b/buildscripts/phing/classes/phing/TaskContainer.php new file mode 100755 index 00000000..4be2ef41 --- /dev/null +++ b/buildscripts/phing/classes/phing/TaskContainer.php @@ -0,0 +1,44 @@ +<?php +/** + * $Id: c31c503eb573b2f454071e9a97fa6a2323c128eb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + * + * @package phing + */ + +/** + * Abstract interface for objects which can contain tasks (targets) + * Used to check if a class can contain tasks (via instanceof) + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing + */ +interface TaskContainer { + + /** + * Adds a task to this task container. Must be implemented + * by derived class + * + * @param object The task to be added to the container + * @access public + */ + function addTask(Task $task); +} diff --git a/buildscripts/phing/classes/phing/UnknownElement.php b/buildscripts/phing/classes/phing/UnknownElement.php new file mode 100755 index 00000000..b04365e7 --- /dev/null +++ b/buildscripts/phing/classes/phing/UnknownElement.php @@ -0,0 +1,215 @@ +<?php +/* + * $Id: a4e6c2e3f776c5a353e52fb8518b3533f14a97c4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Wrapper class that holds all information necessary to create a task + * that did not exist when Phing started. + * + * <em> This has something to do with phing encountering an task XML element + * it is not aware of at start time. This is a situation where special steps + * need to be taken so that the element is then known.</em> + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing + */ +class UnknownElement extends Task { + + private $elementName; + private $realThing; + private $children = array(); + + /** + * Constructs a UnknownElement object + * + * @param string The XML element name that is unknown + * @access public + */ + function __construct($elementName) { + $this->elementName = (string) $elementName; + } + + /** + * Return the XML element name that this <code>UnnownElement</code> + * handles. + * + * @return string The XML element name that is unknown + */ + public function getTag() { + return (string) $this->elementName; + } + + /** + * Tries to configure the unknown element + * + * @throws BuildException if the element can not be configured + */ + public function maybeConfigure() { + + $this->realThing = $this->makeObject($this, $this->wrapper); + $this->wrapper->setProxy($this->realThing); + if ($this->realThing instanceof Task) { + $this->realThing->setRuntimeConfigurableWrapper($this->wrapper); + } + + $this->handleChildren($this->realThing, $this->wrapper); + $this->wrapper->maybeConfigure($this->getProject()); + + } + + /** + * Called when the real task has been configured for the first time. + * + * @throws BuildException if the task can not be created + */ + public function main() { + + if ($this->realThing === null) { + // plain impossible to get here, maybeConfigure should + // have thrown an exception. + throw new BuildException("Should not be executing UnknownElement::main() -- task/type: {$this->elementName}"); + } + + if ($this->realThing instanceof Task) { + $this->realThing->main(); + } + + } + + /** + * Add a child element to the unknown element + * + * @param object The object representing the child element + */ + public function addChild(UnknownElement $child) { + $this->children[] = $child; + } + + /** + * Handle child elemets of the unknown element, if any. + * + * @param ProjectComponent The parent object the unkown element belongs to + * @param object The parent wrapper object + */ + function handleChildren(ProjectComponent $parent, $parentWrapper) { + + if ($parent instanceof TaskAdapter) { + $parent = $parent->getProxy(); + } + + $parentClass = get_class($parent); + $ih = IntrospectionHelper::getHelper($parentClass); + + for ($i=0, $childrenCount=count($this->children); $i < $childrenCount; $i++) { + + $childWrapper = $parentWrapper->getChild($i); + $child = $this->children[$i]; + $realChild = null; + if ($parent instanceof TaskContainer) { + $realChild = $this->makeTask($child, $childWrapper, false); + $parent->addTask($realChild); + } else { + $project = $this->project === null ? $parent->project : $this->project; + $realChild = $ih->createElement($project, $parent, $child->getTag()); + } + + $childWrapper->setProxy($realChild); + if ($realChild instanceof Task) { + $realChild->setRuntimeConfigurableWrapper($childWrapper); + } + + if ($realChild instanceof ProjectComponent) { + $child->handleChildren($realChild, $childWrapper); + } + + if ($realChild instanceof Task) { + $realChild->maybeConfigure(); + } + } + } + + /** + * Creates a named task or data type. If the real object is a task, + * it is configured up to the init() stage. + * + * @param UnknownElement $ue The unknown element to create the real object for. + * Must not be <code>null</code>. + * @param RuntimeConfigurable $w Ignored in this implementation. + * @return object The Task or DataType represented by the given unknown element. + */ + protected function makeObject(UnknownElement $ue, RuntimeConfigurable $w) { + $o = $this->makeTask($ue, $w, true); + if ($o === null) { + $o = $this->project->createDataType($ue->getTag()); + } + if ($o === null) { + throw new BuildException("Could not create task/type: '".$ue->getTag()."'. Make sure that this class has been declared using taskdef / typedef."); + } + return $o; + } + + /** + * Create a named task and configure it up to the init() stage. + * + * @param UnknownElement $ue The unknwon element to create a task from + * @param RuntimeConfigurable $w The wrapper object + * @param boolean $onTopLevel Whether to treat this task as if it is top-level. + * @return Task The freshly created task + */ + protected function makeTask(UnknownElement $ue, RuntimeConfigurable $w, $onTopLevel = false) { + + $task = $this->project->createTask($ue->getTag()); + + if ($task === null) { + if (!$onTopLevel) { + throw new BuildException("Could not create task of type: '".$this->elementName."'. Make sure that this class has been declared using taskdef."); + } + return null; + } + + // used to set the location within the xmlfile so that exceptions can + // give detailed messages + + $task->setLocation($this->getLocation()); + $attrs = $w->getAttributes(); + if (isset($attrs['id'])) { + $this->project->addReference($attrs['id'], $task); + } + + // UnknownElement always has an associated target + $task->setOwningTarget($this->target); + + $task->init(); + return $task; + } + + /** + * Get the name of the task to use in logging messages. + * + * @return string The task's name + */ + function getTaskName() { + return $this->realThing === null ? parent::getTaskName() : $this->realThing->getTaskName(); + } +} diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php new file mode 100644 index 00000000..6fe71107 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php @@ -0,0 +1,304 @@ +<?php +/** + * DocBlox + * + * PHP Version 5 + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** + * Manager class for Parallel processes. + * + * This class will manage the workers and make sure all processes are executed + * in parallel and not too many at the same time. + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ +class DocBlox_Parallel_Manager extends ArrayObject +{ + /** @var int The maximum number of processes to run simultaneously */ + protected $process_limit = 2; + + /** @var boolean Tracks whether this manager is currently executing */ + protected $is_running = false; + + /** + * Tries to autodetect the optimal number of process by counting the number + * of processors. + * + * @param array $input Input for the array object. + * @param int $flags flags for the array object. + * @param string $iterator_class Iterator class for this array object. + */ + public function __construct( + $input = array(), $flags = 0, $iterator_class = "ArrayIterator" + ) { + parent::__construct($input, $flags, $iterator_class); + + if (is_readable('/proc/cpuinfo')) { + $processors = 0; + exec("cat /proc/cpuinfo | grep processor | wc -l", $processors); + $this->setProcessLimit(reset($processors)); + } + } + + /** + * Adds a worker to to the queue. + * + * This method will prepare a worker to be executed in parallel once the + * execute method is invoked. + * A fluent interface is provided so that you can chain multiple workers + * in one call. + * + * Example: + * + * $cb1 = function() { var_dump('a'); sleep(1); }; + * $cb2 = function() { var_dump('b'); sleep(1); }; + * + * $mgr = new DocBlox_Parallel_Manager(); + * $mgr->setProcessLimit(2) + * ->addWorker(new DocBlox_Parallel_Worker($cb1)) + * ->addWorker(new DocBlox_Parallel_Worker($cb2)) + * ->execute(); + * + * @param int $index The key for this worker. + * @param DocBlox_Parallel_Worker $newval The worker to add onto the queue. + * + * @see DocBlox_Parallel_Manager::execute() + * + * @throws RuntimeException if this method is invoked while the + * manager is busy executing tasks. + * @throws InvalidArgumentException if the provided element is not of type + * DocBlox_Parallel_Worker. + * + * @return void + */ + public function offsetSet($index, $newval) + { + if (!$newval instanceof DocBlox_Parallel_Worker) { + throw new InvalidArgumentException( + 'Provided element must be of type DocBlox_Parallel_Worker' + ); + } + if ($this->isRunning()) { + throw new RuntimeException( + 'Workers may not be added during execution of the manager' + ); + } + + parent::offsetSet($index, $newval); + } + + /** + * Convenience method to make the addition of workers explicit and allow a + * fluent interface. + * + * @param DocBlox_Parallel_Worker $worker The worker to add onto the queue. + * + * @return self + */ + public function addWorker(DocBlox_Parallel_Worker $worker) + { + $this[] = $worker; + + return $this; + } + + /** + * Sets how many processes at most to execute at the same time. + * + * A fluent interface is provided so that you can chain multiple workers + * in one call. + * + * @param int $process_limit The limit, minimum of 1 + * + * @see DocBlox_Parallel_Manager::addWorker() for an example + * + * @return self + */ + public function setProcessLimit($process_limit) + { + if ($process_limit < 1) { + throw new InvalidArgumentException( + 'Number of simultaneous processes may not be less than 1' + ); + } + + $this->process_limit = $process_limit; + + return $this; + } + + /** + * Returns the current limit on the amount of processes that can be + * executed at the same time. + * + * @return int + */ + public function getProcessLimit() + { + return $this->process_limit; + } + + /** + * Returns whether the manager is executing the workers. + * + * @return boolean + */ + public function isRunning() + { + return $this->is_running; + } + + /** + * Executes each worker. + * + * This method loops through the list of workers and tries to fork as + * many times as the ProcessLimit dictates at the same time. + * + * @return void + */ + public function execute() + { + /** @var int[] $processes */ + $processes = $this->startExecution(); + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($this as $worker) { + + // if requirements are not met, execute workers in series. + if (!$this->checkRequirements()) { + $worker->execute(); + continue; + } + + $this->forkAndRun($worker, $processes); + } + + $this->stopExecution($processes); + } + + /** + * Notifies manager that execution has started, checks requirements and + * returns array for child processes. + * + * If forking is not available because library requirements are not met + * than the list of workers is processed in series and a E_USER_NOTICE is + * triggered. + * + * @return int[] + */ + protected function startExecution() + { + $this->is_running = true; + + // throw a E_USER_NOTICE if the requirements are not met. + if (!$this->checkRequirements()) { + trigger_error( + 'The PCNTL extension is not available, running workers in series ' + . 'instead of parallel', + E_USER_NOTICE + ); + } + + return array(); + } + + /** + * Waits for all processes to have finished and notifies the manager that + * execution has stopped. + * + * @param int[] &$processes List of running processes. + * + * @return void + */ + protected function stopExecution(array &$processes) + { + // starting of processes has ended but some processes might still be + // running wait for them to finish + while (!empty($processes)) { + pcntl_waitpid(array_shift($processes), $status); + } + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($this as $worker) { + $worker->pipe->push(); + } + + $this->is_running = false; + } + + /** + * Forks the current process and calls the Worker's execute method OR + * handles the parent process' execution. + * + * This is the really tricky part of the forking mechanism. Here we invoke + * {@link http://www.php.net/manual/en/function.pcntl-fork.php pcntl_fork} + * and either execute the forked process or deal with the parent's process + * based on in which process we are. + * + * To fully understand what is going on here it is recommended to read the + * PHP manual page on + * {@link http://www.php.net/manual/en/function.pcntl-fork.php pcntl_fork} + * and associated articles. + * + * If there are more workers than may be ran simultaneously then this method + * will wait until a slot becomes available and then starts the next worker. + * + * @param DocBlox_Parallel_Worker $worker The worker to process. + * @param int[] &$processes The list of running processes. + * + * @throws RuntimeException if we are unable to fork. + * + * @return void + */ + protected function forkAndRun( + DocBlox_Parallel_Worker $worker, array &$processes + ) { + $worker->pipe = new DocBlox_Parallel_WorkerPipe($worker); + + // fork the process and register the PID + $pid = pcntl_fork(); + + switch ($pid) { + case -1: + throw new RuntimeException('Unable to establish a fork'); + case 0: // Child process + $worker->execute(); + + $worker->pipe->pull(); + + // Kill -9 this process to prevent closing of shared file handlers. + // Not doing this causes, for example, MySQL connections to be cleaned. + posix_kill(getmypid(), SIGKILL); + default: // Parent process + // Keep track if the worker children + $processes[] = $pid; + + if (count($processes) >= $this->getProcessLimit()) { + pcntl_waitpid(array_shift($processes), $status); + } + break; + } + } + + /** + * Returns true when all requirements are met. + * + * @return bool + */ + protected function checkRequirements() + { + return (bool)(extension_loaded('pcntl')); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md new file mode 100644 index 00000000..272d7191 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/README.md @@ -0,0 +1,106 @@ +Parallel +======== + +This is a library for introducing Parallelization into your project. +See the `example.php` file for an example how to use this library. + +Theory of Operation +------------------- + +This library will enable the developer to execute a given amount of tasks +(workers) in parallel. This is achieved by adding workers onto a manager, +optionally defining how many processes to run simultaneously and then execute +the manager. + +Under Linux this library will try to detect the number of processors and allow +a maximum number of processes to run equal to the number of processors. If this +cannot be determined or the user is running Windows then a default of 2 is used. + +Requirements and graceful degradation +------------------------------------- + +Parallelization has several requirements. But to allow distribution, without +adding several requirements to your application, will this library execute the +given tasks in serie if the requirements are not met. And throw a E_USER_NOTICE +php error that explains to the user that dependencies are missing. + +The requirements for this library are: + +* A *NIX compatible operating system +* Scripts must not run from an apache module +* the PCNTL PHP extension (http://php.net/manual/en/book.pcntl.php) + +Workers +------- + +Workers are basically wrappers around callback functions or methods. As such you +can use anything in your existing project and parallelize it. + +Do note that each parallel process is a duplicate of the original. This means +that, for example, if you pass an object (or other reference) and change that, +that the changes that you have made do not carry over to the caller. + +The return value of the given callback is stored as result on the worker and +can be read using the `getResult()` method. + +Any exception that is thrown will result in an error, where the `getReturnCode()` +method will return the exception code (be warned: this may be 0!) and the +`getError()` method will return the exception message. + +Errors and exceptions +--------------------- + +if a task throws an exception it is caught and registered as an error. The +exception's code is used as error number, where the message is used as error +message. + +By using this, instead of dying, you can continue execution of the other parallel +processes and handle errors yourself after all processes have been executed. + +Examples +-------- + +### Fluent interface + + $mgr = new DocBlox_Parallel_Manager(); + $mgr + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'a'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'b'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'c'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'd'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'e'; })) + ->execute(); + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($mgr as $worker) { + var_dump($worker->getResult()); + } + +### Array interface + + $mgr = new DocBlox_Parallel_Manager(); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'f'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'g'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'h'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'i'; }); + $mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'j'; }); + $mgr->execute(); + + /** @var DocBlox_Parallel_Worker $worker */ + foreach ($mgr as $worker) { + var_dump($worker->getResult()); + } + +TODO +---- + +* Improve docs +* More intelligent process slots; currently only the oldest in a 'set' of slots + is waited on but if this runs for a longer time then the other slots than + those will not be filled as long as the first slot is occupied. +* Last parts of IPC (Inter-Process Communication), to be able to return + information from Workers to the Manager. + + * STDOUT + * STDERR + diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php new file mode 100644 index 00000000..338f4b25 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php @@ -0,0 +1,203 @@ +<?php +/** + * DocBlox + * + * PHP Version 5 + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** + * Class that represents the execution of a single task within a parallelized + * frame. + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ +class DocBlox_Parallel_Worker +{ + /** @var callback the task to execute for this worker */ + protected $task = null; + + /** @var mixed[] A list of argument to pass to the task */ + protected $arguments = array(); + + /** @var int The return code to tell the parent process how it went */ + protected $return_code = -1; + + /** @var mixed The result of the given task */ + protected $result = ''; + + /** @var string The error message, if an error occurred */ + protected $error = ''; + + /** + * Creates the worker and sets the task to execute optionally including + * the arguments that need to be passed to the task. + * + * @param callback $task The task to invoke upon execution. + * @param mixed[] $arguments The arguments to provide to the task. + */ + function __construct($task, array $arguments = array()) + { + $this->setTask($task); + $this->arguments = $arguments; + } + + /** + * Returns the list of arguments as provided int he constructor. + * + * @see DocBlox_Parallel_Worker::__construct() + * + * @return mixed[] + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the task as provided in the constructor. + * + * @see DocBlox_Parallel_Worker::__construct() + * + * @return callback + */ + public function getTask() + { + return $this->task; + } + + /** + * Returns the available return code. + * + * This method may return -1 if no return code is available yet. + * + * @return int + */ + public function getReturnCode() + { + return $this->return_code; + } + + /** + * Sets the return code for this worker. + * + * Recommended is to use the same codes as are used with + * {@link http://www.gnu.org/software/bash/manual/html_node/Exit-Status.html + * exit codes}. + * + * In short: 0 means that the task succeeded and a any other positive value + * indicates an error condition. + * + * @param int $return_code Recommended to be a positive number + * + * @throw InvalidArgumentException if the code is not a number or negative + * + * @return void + */ + public function setReturnCode($return_code) + { + if (!is_numeric($return_code) || ($return_code < 0)) { + throw new InvalidArgumentException( + 'Expected the return code to be a positive number' + ); + } + + $this->return_code = $return_code; + } + + /** + * Returns the error message associated with the return code. + * + * @return string + */ + public function getError() + { + return $this->error; + } + + /** + * Sets the error message. + * + * @param string $error The error message. + * + * @return void + */ + public function setError($error) + { + $this->error = $error; + } + + /** + * Returns the result for this task run. + * + * @return null|mixed + */ + public function getResult() + { + return $this->result; + } + + /** + * Sets the result for this task run. + * + * @param mixed $result The value that is returned by the task; can be anything. + * + * @return void + */ + public function setResult($result) + { + $this->result = $result; + } + + /** + * Invokes the task with the given arguments and processes the output. + * + * @return void. + */ + public function execute() + { + $this->setReturnCode(0); + try { + $this->setResult( + call_user_func_array($this->getTask(), $this->getArguments()) + ); + } catch (Exception $e) { + $this->setError($e->getMessage()); + $this->setReturnCode($e->getCode()); + } + } + + /** + * Sets the task for this worker and validates whether it is callable. + * + * @param callback $task The task to execute when the execute method + * is invoked. + * + * @throws InvalidArgumentException if the given argument is not a callback. + * + * @see DocBlox_Parallel_Worker::__construct() + * @see DocBlox_Parallel_Worker::execute() + * + * @return void + */ + protected function setTask($task) + { + if (!is_callable($task)) { + throw new InvalidArgumentException( + 'Worker task is not a callable object' + ); + } + + $this->task = $task; + } +} diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php new file mode 100644 index 00000000..3b7eb7fe --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php @@ -0,0 +1,127 @@ +<?php +/** + * DocBlox + * + * PHP Version 5 + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** + * Class that represents a named pipe for a Worker. + * + * This class manages the named pipe for a worker and is able to push and pull + * specific data to facilitate IPC (interprocess communication). + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ +class DocBlox_Parallel_WorkerPipe +{ + /** @var DocBlox_Parallel_Worker worker class that is associated */ + protected $worker; + + /** @var string Path to the pipe */ + protected $path; + + /** + * Initializes the named pipe. + * + * @param DocBlox_Parallel_Worker $worker Associated worker. + */ + public function __construct(DocBlox_Parallel_Worker $worker) + { + $this->worker = $worker; + + $this->path = tempnam(sys_get_temp_dir(), 'dpm_'); + posix_mkfifo($this->path, 0750); + } + + /** + * If the named pipe was not cleaned up, do so now. + */ + public function __destruct() + { + if (file_exists($this->path)) { + $this->release(); + } + } + + /** + * Pull the worker data into the named pipe. + * + * @return void + */ + public function pull() + { + $this->writePipeContents(); + } + + /** + * Push the worker data back onto the worker and release the pipe. + * + * @return void + */ + public function push() + { + list($result, $error, $return_code) = $this->readPipeContents(); + $this->release(); + + $this->worker->setResult($result); + $this->worker->setError($error); + $this->worker->setReturnCode($return_code); + } + + /** + * Convenience method to show relation to readPipeContents. + * + * @return void + */ + protected function writePipeContents() + { + // push the gathered data onto a name pipe + $pipe = fopen($this->path, 'w'); + fwrite( + $pipe, serialize( + array( + $this->worker->getResult(), + $this->worker->getError(), + $this->worker->getReturnCode() + ) + ) + ); + fclose($pipe); + } + + /** + * Returns the unserialized contents of the pipe. + * + * @return array + */ + protected function readPipeContents() + { + $pipe = fopen($this->path, 'r+'); + $result = unserialize(fread($pipe, filesize($this->path))); + fclose($pipe); + + return $result; + } + + /** + * Releases the pipe. + * + * @return void + */ + protected function release() + { + unlink($this->path); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php new file mode 100644 index 00000000..3bdcd408 --- /dev/null +++ b/buildscripts/phing/classes/phing/contrib/DocBlox/Parallel/example.php @@ -0,0 +1,57 @@ +<?php +/** + * DocBlox + * + * PHP Version 5 + * + * @category DocBlox + * @package Parallel + * @author Mike van Riel <mike.vanriel@naenius.com> + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://docblox-project.org + */ + +/** Include the manager as we do not autoload */ +require_once 'Manager.php'; + +/** Include the worker as we do not autoload */ +require_once 'Worker.php'; + +/** Include the worker's pipe as we do not autoload */ +require_once 'WorkerPipe.php'; + +// ----------------------------------------------------------------------------- +// method 1: using a fluent interface and the addWorker helper. +// ----------------------------------------------------------------------------- + +$mgr = new DocBlox_Parallel_Manager(); +$mgr + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'a'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'b'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'c'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'd'; })) + ->addWorker(new DocBlox_Parallel_Worker(function() { sleep(1); return 'e'; })) + ->execute(); + +/** @var DocBlox_Parallel_Worker $worker */ +foreach ($mgr as $worker) { + var_dump($worker->getResult()); +} + +// ----------------------------------------------------------------------------- +// method 2: using the manager as worker array +// ----------------------------------------------------------------------------- + +$mgr = new DocBlox_Parallel_Manager(); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'f'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'g'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'h'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'i'; }); +$mgr[] = new DocBlox_Parallel_Worker(function() { sleep(1); return 'j'; }); +$mgr->execute(); + +/** @var DocBlox_Parallel_Worker $worker */ +foreach ($mgr as $worker) { + var_dump($worker->getResult()); +} diff --git a/buildscripts/phing/classes/phing/filters/BaseFilterReader.php b/buildscripts/phing/classes/phing/filters/BaseFilterReader.php new file mode 100755 index 00000000..4489b16e --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/BaseFilterReader.php @@ -0,0 +1,157 @@ +<?php + +/* + * $Id: a1da135c09abf5cf07a53b3a327baf3497cfb697 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/system/io/FilterReader.php'; +include_once 'phing/system/io/StringReader.php'; + + +/** + * Base class for core filter readers. + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class BaseFilterReader extends FilterReader { + + /** Have the parameters passed been interpreted? */ + protected $initialized = false; + + /** The Phing project this filter is part of. */ + protected $project = null; + + /** + * Constructor used by Phing's introspection mechanism. + * The original filter reader is only used for chaining + * purposes, never for filtering purposes (and indeed + * it would be useless for filtering purposes, as it has + * no real data to filter). ChainedReaderHelper uses + * this placeholder instance to create a chain of real filters. + * + * @param Reader $in + */ + function __construct($in = null) { + if ($in === null) { + $dummy = ""; + $in = new StringReader($dummy); + } + parent::__construct($in); + } + + /** + * Returns the initialized status. + * + * @return boolean whether or not the filter is initialized + */ + function getInitialized() { + return $this->initialized; + } + + /** + * Sets the initialized status. + * + * @param boolean $initialized Whether or not the filter is initialized. + */ + function setInitialized($initialized) { + $this->initialized = (boolean) $initialized; + } + + /** + * Sets the project to work with. + * + * @param object $project The project this filter is part of. + * Should not be <code>null</code>. + */ + function setProject(Project $project) { + // type check, error must never occur, bad code of it does + $this->project = $project; + } + + /** + * Returns the project this filter is part of. + * + * @return object The project this filter is part of + */ + function getProject() { + return $this->project; + } + + /** + * Reads characters. + * + * @param off Offset at which to start storing characters. + * @param len Maximum number of characters to read. + * + * @return Characters read, or -1 if the end of the stream + * has been reached + * + * @throws IOException If an I/O error occurs + */ + function read($len = null) { + return $this->in->read($len); + } + + /** + * Reads a line of text ending with '\n' (or until the end of the stream). + * The returned String retains the '\n'. + * + * @return the line read, or <code>null</code> if the end of the + stream has already been reached + * + * @throws IOException if the underlying reader throws one during + * reading + */ + function readLine() { + $line = null; + + while ( ($ch = $this->in->read(1)) !== -1 ) { + $line .= $ch; + if ( $ch === "\n" ) + break; + } + + return $line; + } + + /** + * Returns whether the end of file has been reached with input stream. + * @return boolean + */ + function eof() { + return $this->in->eof(); + } + + /** + * Convenience method to support logging in filters. + * @param string $msg Message to log. + * @param int $level Priority level. + */ + function log($msg, $level = Project::MSG_INFO) { + if ($this->project !== null) { + $this->project->log("[filter:".get_class($this)."] ".$msg, $level); + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php b/buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php new file mode 100755 index 00000000..8d3b0810 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/BaseParamFilterReader.php @@ -0,0 +1,69 @@ +<?php + +/* + * $Id: 412cc012db35b1dcf3545b93d5053e727d66b61f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/types/Parameterizable.php'; +include_once 'phing/types/Parameter.php'; + +/** + * Base class for core filter readers. + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @copyright � 2003 seasonfive. All rights reserved + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class BaseParamFilterReader extends BaseFilterReader implements Parameterizable { + + /** The passed in parameter array. */ + protected $_parameters = array(); + + /* + * Sets the parameters used by this filter, and sets + * the filter to an uninitialized status. + * + * @param array Array of parameters to be used by this filter. + * Should not be <code>null</code>. + */ + function setParameters($parameters) { + // type check, error must never occur, bad code of it does + if ( !is_array($parameters) ) { + throw new Exception("Expected parameters array got something else"); + } + + $this->_parameters = $parameters; + $this->setInitialized(false); + } + + /* + * Returns the parameters to be used by this filter. + * + * @return the parameters to be used by this filter + */ + function &getParameters() { + return $this->_parameters; + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/ChainableReader.php b/buildscripts/phing/classes/phing/filters/ChainableReader.php new file mode 100644 index 00000000..2b773dbe --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ChainableReader.php @@ -0,0 +1,43 @@ +<?php + +/* + * $Id: fb9ebbb44f13ecc3693265e1b793ab17cea543a1 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +/** + * Interface indicating that a reader may be chained to another one. + * + * @author Magesh Umasankar + * @package phing.filters + */ +interface ChainableReader { + + /** + * Returns a reader with the same configuration as this one, + * but filtering input from the specified reader. + * + * @param Reader $rdr the reader which the returned reader should be filtering + * + * @return Reader A reader with the same configuration as this one, but + * filtering input from the specified reader + */ + public function chain(Reader $rdr); +} + + diff --git a/buildscripts/phing/classes/phing/filters/ExpandProperties.php b/buildscripts/phing/classes/phing/filters/ExpandProperties.php new file mode 100755 index 00000000..2517783f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ExpandProperties.php @@ -0,0 +1,99 @@ +<?php + +/* + * $Id: d6bb7717db7cf2b122cbdcb93e5bb0f45d97ec52 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Expands Phing Properties, if any, in the data. + * <p> + * Example:<br> + * <pre><expandproperties/></pre> + * Or: + * <pre><filterreader classname="phing.filters.ExpandProperties'/></pre> + * + * @author Yannick Lecaillez <yl@seasonfive.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: d6bb7717db7cf2b122cbdcb93e5bb0f45d97ec52 $ + * @see BaseFilterReader + * @package phing.filters + */ +class ExpandProperties extends BaseFilterReader implements ChainableReader { + protected $logLevel = Project::MSG_VERBOSE; + + /** + * Set level of log messages generated (default = info) + * @param string $level + */ + public function setLevel($level) + { + switch ($level) + { + case "error": $this->logLevel = Project::MSG_ERR; break; + case "warning": $this->logLevel = Project::MSG_WARN; break; + case "info": $this->logLevel = Project::MSG_INFO; break; + case "verbose": $this->logLevel = Project::MSG_VERBOSE; break; + case "debug": $this->logLevel = Project::MSG_DEBUG; break; + } + } + + /** + * Returns the filtered stream. + * The original stream is first read in fully, and the Phing properties are expanded. + * + * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached. + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + $project = $this->getProject(); + $buffer = ProjectConfigurator::replaceProperties($project, $buffer, $project->getProperties(), $this->logLevel); + + return $buffer; + } + + /** + * Creates a new ExpandProperties filter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ExpandProperties($reader); + $newFilter->setProject($this->getProject()); + return $newFilter; + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/HeadFilter.php b/buildscripts/phing/classes/phing/filters/HeadFilter.php new file mode 100755 index 00000000..3cbcb51b --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/HeadFilter.php @@ -0,0 +1,161 @@ +<?php + +/* + * $Id: e3e9c0a171b4416545e57fe42b45b4eec14914ce $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Reads the first <code>n</code> lines of a stream. + * (Default is first 10 lines.) + * <p> + * Example: + * <pre><headfilter lines="3"/></pre> + * Or: + * <pre><filterreader classname="phing.filters.HeadFilter"> + * <param name="lines" value="3"/> + * </filterreader></pre> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class HeadFilter extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for the number of lines to be returned. + */ + const LINES_KEY = "lines"; + + /** + * Number of lines currently read in. + * @var integer + */ + private $_linesRead = 0; + + /** + * Number of lines to be returned in the filtered stream. + * @var integer + */ + private $_lines = 10; + + /** + * Returns first n lines of stream. + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // note, if buffer contains fewer lines than + // $this->_lines this code will not work. + + if($this->_linesRead < $this->_lines) { + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // now grab first X lines from buffer + + $lines = explode("\n", $buffer); + + $linesCount = count($lines); + + // must account for possibility that the num lines requested could + // involve more than one buffer read. + $len = ($linesCount > $this->_lines ? $this->_lines - $this->_linesRead : $linesCount); + $filtered_buffer = implode("\n", array_slice($lines, 0, $len) ); + $this->_linesRead += $len; + + return $filtered_buffer; + + } + + return -1; // EOF, since the file is "finished" as far as subsequent filters are concerned. + } + + /** + * Sets the number of lines to be returned in the filtered stream. + * + * @param integer $lines the number of lines to be returned in the filtered stream. + */ + function setLines($lines) { + $this->_lines = (int) $lines; + } + + /** + * Returns the number of lines to be returned in the filtered stream. + * + * @return integer The number of lines to be returned in the filtered stream. + */ + function getLines() { + return $this->_lines; + } + + /** + * Creates a new HeadFilter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader. + */ + function chain(Reader $reader) { + $newFilter = new HeadFilter($reader); + $newFilter->setLines($this->getLines()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Scans the parameters list for the "lines" parameter and uses + * it to set the number of lines to be returned in the filtered stream. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0, $_i=count($params) ; $i < $_i; $i++) { + if ( self::LINES_KEY == $params[$i]->getName() ) { + $this->_lines = (int) $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/IconvFilter.php b/buildscripts/phing/classes/phing/filters/IconvFilter.php new file mode 100755 index 00000000..c9883b17 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/IconvFilter.php @@ -0,0 +1,155 @@ +<?php + +/* + * $Id: 9c0d703f08f0160b6a699d328a6025587877e104 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Encode data from <code>in</code> encoding to <code>out</code> encoding. + * + * Example: + * <pre> + * <iconvfilter inputencoding="UTF-8" outputencoding="CP1251" /> + * </pre> + * Or: + * <pre> + * <filterreader classname="phing.filters.IconvFilter"> + * <param name="inputencoding" value="UTF-8" /> + * <param name="outputencoding" value="CP1251" /> + * </filterreader> + * </pre> + * + * @author Alexey Shockov, <alexey@shockov.com> + * @version $Id$ + * @package phing.filters + */ +class IconvFilter + extends BaseParamFilterReader + implements ChainableReader { + + private $_inputEncoding; + + private $_outputEncoding; + + /** + * Returns first n lines of stream. + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + $this->_initialize(); + + // Process whole text at once. + $text = null; + while (($data = $this->in->read($len)) !== -1) { + $text .= $data; + } + + // At the end. + if (null === $text) { + return -1; + } + + $this->log( + "Encoding " . $this->in->getResource() . " from " . $this->getInputEncoding() . " to " . $this->getOutputEncoding(), + Project::MSG_VERBOSE + ); + + return iconv($this->_inputEncoding, $this->_outputEncoding, $text); + } + + /** + * + * @param string $encoding Input encoding. + */ + public function setInputEncoding($encoding) { + $this->_inputEncoding = $encoding; + } + + /** + * + * @return string + */ + public function getInputEncoding() { + return $this->_inputEncoding; + } + + /** + * + * @param string $encoding Output encoding. + */ + public function setOutputEncoding($encoding) { + $this->_outputEncoding = $encoding; + } + + /** + * + * @return string + */ + public function getOutputEncoding() { + return $this->_outputEncoding; + } + + /** + * Creates a new IconvFilter using the passed in Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering the specified reader. + */ + function chain(Reader $reader) { + $filter = new self($reader); + + $filter->setInputEncoding($this->getInputEncoding()); + $filter->setOutputEncoding($this->getOutputEncoding()); + + $filter->setInitialized(true); + $filter->setProject($this->getProject()); + + return $filter; + } + + /** + * Configuring object from the parameters list. + */ + private function _initialize() { + if ($this->getInitialized()) { + return; + } + + $params = $this->getParameters(); + if ($params !== null) { + foreach ($params as $param) { + if ('in' == $param->getName()) { + $this->setInputEncoding($param->getValue()); + } else if ('out' == $param->getName()) { + $this->setOutputEncoding($param->getValue()); + } + } + } + + $this->setInitialized(true); + } +} diff --git a/buildscripts/phing/classes/phing/filters/LineContains.php b/buildscripts/phing/classes/phing/filters/LineContains.php new file mode 100755 index 00000000..b84b62c1 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/LineContains.php @@ -0,0 +1,260 @@ +<?php + +/* + * $Id: 2e783b14ff093df7de21477479a123403d630984 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Filter which includes only those lines that contain all the user-specified + * strings. + * + * Example: + * + * <pre><linecontains> + * <contains value="foo"> + * <contains value="bar"> + * </linecontains></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.LineContains"> + * <param type="contains" value="foo"/> + * <param type="contains" value="bar"/> + * </filterreader></pre> + * + * This will include only those lines that contain <code>foo</code> and + * <code>bar</code>. + * + * @author Yannick Lecaillez <yl@seasonfive.com> + * @author Hans Lellelid <hans@velum.net> + * @version $Id$ + * @see PhingFilterReader + * @package phing.filters +*/ +class LineContains extends BaseParamFilterReader implements ChainableReader { + + /** + * The parameter name for the string to match on. + * @var string + */ + const CONTAINS_KEY = "contains"; + + /** + * Array of Contains objects. + * @var array + */ + private $_contains = array(); + + /** + * [Deprecated] + * @var string + */ + private $_line = null; + + /** + * Returns all lines in a buffer that contain specified strings. + * @return mixed buffer, -1 on EOF + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $matched = array(); + $containsSize = count($this->_contains); + + foreach($lines as $line) { + for($i = 0 ; $i < $containsSize ; $i++) { + $containsStr = $this->_contains[$i]->getValue(); + if ( strstr($line, $containsStr) === false ) { + $line = null; + break; + } + } + if($line !== null) { + $matched[] = $line; + } + } + $filtered_buffer = implode("\n", $matched); + return $filtered_buffer; + } + + /** + * [Deprecated. For reference only, used to be read() method.] + * Returns the next character in the filtered stream, only including + * lines from the original stream which contain all of the specified words. + * + * @return the next character in the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function readChar() { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $ch = -1; + + if ( $this->_line !== null ) { + $ch = substr($this->_line, 0, 1); + if ( strlen($this->_line) === 1 ) + $this->_line = null; + else + $this->_line = substr($this->_line, 1); + } else { + $this->_line = $this->readLine(); + if ( $this->_line === null ) { + $ch = -1; + } else { + $containsSize = count($this->_contains); + for($i = 0 ; $i < $containsSize ; $i++) { + $containsStr = $this->_contains[$i]->getValue(); + if ( strstr($this->_line, $containsStr) === false ) { + $this->_line = null; + break; + } + } + return $this->readChar(); + } + } + + return $ch; + } + + /** + * Adds a <code>contains</code> nested element. + * + * @return Contains The <code>contains</code> element added. + * Must not be <code>null</code>. + */ + function createContains() { + $num = array_push($this->_contains, new Contains()); + return $this->_contains[$num-1]; + } + + /** + * Sets the array of words which must be contained within a line read + * from the original stream in order for it to match this filter. + * + * @param array $contains An array of words which must be contained + * within a line in order for it to match in this filter. + * Must not be <code>null</code>. + */ + function setContains($contains) { + // type check, error must never occur, bad code of it does + if ( !is_array($contains) ) { + throw new Exception("Excpected array got something else"); + } + + $this->_contains = $contains; + } + + /** + * Returns the vector of words which must be contained within a line read + * from the original stream in order for it to match this filter. + * + * @return array The array of words which must be contained within a line read + * from the original stream in order for it to match this filter. The + * returned object is "live" - in other words, changes made to the + * returned object are mirrored in the filter. + */ + function getContains() { + return $this->_contains; + } + + /** + * Creates a new LineContains using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new LineContains($reader); + $newFilter->setContains($this->getContains()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses the parameters to add user-defined contains strings. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + foreach($params as $param) { + if ( self::CONTAINS_KEY == $param->getType() ) { + $cont = new Contains(); + $cont->setValue($param->getValue()); + array_push($this->_contains, $cont); + break; // because we only support a single contains + } + } + } + } +} + +/** + * Holds a contains element. + * + * @package phing.filters + */ +class Contains { + + /** + * @var string + */ + private $_value; + + /** + * Set 'contains' value. + * @param string $contains + */ + function setValue($contains) { + $this->_value = (string) $contains; + } + + /** + * Returns 'contains' value. + * @return string + */ + function getValue() { + return $this->_value; + } +} + diff --git a/buildscripts/phing/classes/phing/filters/LineContainsRegexp.php b/buildscripts/phing/classes/phing/filters/LineContainsRegexp.php new file mode 100755 index 00000000..c603978f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/LineContainsRegexp.php @@ -0,0 +1,179 @@ +<?php +/* + * $Id: 0a881c0b67c96c20345980fd033f006379949dda $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/types/RegularExpression.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Filter which includes only those lines that contain the user-specified + * regular expression matching strings. + * + * Example: + * <pre><linecontainsregexp> + * <regexp pattern="foo*"> + * </linecontainsregexp></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.LineContainsRegExp"> + * <param type="regexp" value="foo*"/> + * </filterreader></pre> + * + * This will fetch all those lines that contain the pattern <code>foo</code> + * + * @author Yannick Lecaillez <yl@seasonfive.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see FilterReader + * @package phing.filters + */ +class LineContainsRegexp extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for regular expression. + * @var string + */ + const REGEXP_KEY = "regexp"; + + /** + * Regular expressions that are applied against lines. + * @var array + */ + private $_regexps = array(); + + /** + * Returns all lines in a buffer that contain specified strings. + * @return mixed buffer, -1 on EOF + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $matched = array(); + + $regexpsSize = count($this->_regexps); + foreach($lines as $line) { + for($i = 0 ; $i<$regexpsSize ; $i++) { + $regexp = $this->_regexps[$i]; + $re = $regexp->getRegexp($this->getProject()); + $matches = $re->matches($line); + if ( !$matches ) { + $line = null; + break; + } + } + if($line !== null) { + $matched[] = $line; + } + } + $filtered_buffer = implode("\n", $matched); + return $filtered_buffer; + } + + /** + * Adds a <code>regexp</code> element. + * + * @return object regExp The <code>regexp</code> element added. + */ + function createRegexp() { + $num = array_push($this->_regexps, new RegularExpression()); + return $this->_regexps[$num-1]; + } + + /** + * Sets the vector of regular expressions which must be contained within + * a line read from the original stream in order for it to match this + * filter. + * + * @param regexps An array of regular expressions which must be contained + * within a line in order for it to match in this filter. Must not be + * <code>null</code>. + */ + function setRegexps($regexps) { + // type check, error must never occur, bad code of it does + if ( !is_array($regexps) ) { + throw new Exception("Excpected an 'array', got something else"); + } + $this->_regexps = $regexps; + } + + /** + * Returns the array of regular expressions which must be contained within + * a line read from the original stream in order for it to match this + * filter. + * + * @return array The array of regular expressions which must be contained within + * a line read from the original stream in order for it to match this + * filter. The returned object is "live" - in other words, changes made to + * the returned object are mirrored in the filter. + */ + function getRegexps() { + return $this->_regexps; + } + + /** + * Creates a new LineContainsRegExp using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new LineContainsRegExp($reader); + $newFilter->setRegexps($this->getRegexps()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses parameters to add user defined regular expressions. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $i<count($params) ; $i++) { + if ( self::REGEXP_KEY === $params[$i]->getType() ) { + $pattern = $params[$i]->getValue(); + $regexp = new RegularExpression(); + $regexp->setPattern($pattern); + array_push($this->_regexps, $regexp); + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/PrefixLines.php b/buildscripts/phing/classes/phing/filters/PrefixLines.php new file mode 100755 index 00000000..9edcc02a --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/PrefixLines.php @@ -0,0 +1,142 @@ +<?php + +/* + * $Id: 744d8766d5aba397c0a8efd61afb8e60bd77b0c3 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Attaches a prefix to every line. + * + * Example: + * <pre><prefixlines prefix="Foo"/></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.PrefixLines"> + * <param name="prefix" value="Foo"/> + * </filterreader></pre> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters +*/ +class PrefixLines extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for the prefix. + * @var string + */ + const PREFIX_KEY = "lines"; + + /** + * The prefix to be used. + * @var string + */ + private $_prefix = null; + + /** + * Adds a prefix to each line of input stream and returns resulting stream. + * + * @return mixed buffer, -1 on EOF + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $filtered = array(); + + foreach($lines as $line) { + $line = $this->_prefix . $line; + $filtered[] = $line; + } + + $filtered_buffer = implode("\n", $filtered); + return $filtered_buffer; + } + + /** + * Sets the prefix to add at the start of each input line. + * + * @param string $prefix The prefix to add at the start of each input line. + * May be <code>null</code>, in which case no prefix + * is added. + */ + function setPrefix($prefix) { + $this->_prefix = (string) $prefix; + } + + /** + * Returns the prefix which will be added at the start of each input line. + * + * @return string The prefix which will be added at the start of each input line + */ + function getPrefix() { + return $this->_prefix; + } + + /** + * Creates a new PrefixLines filter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new PrefixLines($reader); + $newFilter->setPrefix($this->getPrefix()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Initializes the prefix if it is available from the parameters. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0, $_i=count($params) ; $i < $_i ; $i++) { + if ( self::PREFIX_KEY == $params[$i]->getName() ) { + $this->_prefix = (string) $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/ReplaceRegexp.php b/buildscripts/phing/classes/phing/filters/ReplaceRegexp.php new file mode 100755 index 00000000..70e8940f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ReplaceRegexp.php @@ -0,0 +1,129 @@ +<?php + +/* + * $Id: 3ea7f569d0f0b1c4d0f057c9f7f8969cb829f2cb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; +include_once 'phing/types/RegularExpression.php'; + +/** + * Performs a regexp find/replace on stream. + * <p> + * Example:<br> + * <pre> + * <replaceregexp> + * <regexp pattern="\r\n" replace="\n"/> + * <regexp pattern="(\w+)\.xml" replace="\1.php" ignoreCase="true"/> + * </replaceregexp> + * </pre> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.filters + */ +class ReplaceRegexp extends BaseFilterReader implements ChainableReader { + + /** + * @var array RegularExpression[] + */ + private $regexps = array(); + + /** + * Creator method handles nested <regexp> tags. + * @return RegularExpression + */ + function createRegexp() { + $num = array_push($this->regexps, new RegularExpression()); + return $this->regexps[$num-1]; + } + + /** + * Sets the current regexps. + * (Used when, e.g., cloning/chaining the method.) + * @param array RegularExpression[] + */ + function setRegexps($regexps) { + $this->regexps = $regexps; + } + + /** + * Gets the current regexps. + * (Used when, e.g., cloning/chaining the method.) + * @return array RegularExpression[] + */ + function getRegexps() { + return $this->regexps; + } + + /** + * Returns the filtered stream. + * The original stream is first read in fully, and the regex replace is performed. + * + * @param int $len Required $len for Reader compliance. + * + * @return mixed The filtered stream, or -1 if the end of the resulting stream has been reached. + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // perform regex replace here ... + foreach($this->regexps as $exptype) { + $regexp = $exptype->getRegexp($this->project); + try { + $buffer = $regexp->replace($buffer); + $this->log("Performing regexp replace: /".$regexp->getPattern()."/".$regexp->getReplace()."/g".$regexp->getModifiers(), Project::MSG_VERBOSE); + } catch (Exception $e) { + // perhaps mismatch in params (e.g. no replace or pattern specified) + $this->log("Error performing regexp replace: " . $e->getMessage(), Project::MSG_WARN); + } + } + + return $buffer; + } + + /** + * Creates a new ReplaceRegExp filter using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return ReplaceRegExp A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ReplaceRegExp($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setRegexps($this->getRegexps()); + return $newFilter; + } + +} + + diff --git a/buildscripts/phing/classes/phing/filters/ReplaceTokens.php b/buildscripts/phing/classes/phing/filters/ReplaceTokens.php new file mode 100755 index 00000000..a5cd7521 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ReplaceTokens.php @@ -0,0 +1,435 @@ +<?php + +/* + * $Id: 6c5d97f2254de3c08ac34baaabf6119c54a49a7d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/types/TokenSource.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces tokens in the original input with user-supplied values. + * + * Example: + * + * <pre><replacetokens begintoken="#" endtoken="#">; + * <token key="DATE" value="${TODAY}"/> + * </replacetokens></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.ReplaceTokens"> + * <param type="tokenchar" name="begintoken" value="#"/> + * <param type="tokenchar" name="endtoken" value="#"/> + * <param type="token" name="DATE" value="${TODAY}"/> + * </filterreader></pre> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @version $Id: 6c5d97f2254de3c08ac34baaabf6119c54a49a7d $ + * @access public + * @see BaseParamFilterReader + * @package phing.filters + */ +class ReplaceTokens extends BaseParamFilterReader implements ChainableReader { + + /** + * Default "begin token" character. + * @var string + */ + const DEFAULT_BEGIN_TOKEN = "@"; + + /** + * Default "end token" character. + * @var string + */ + const DEFAULT_END_TOKEN = "@"; + + /** + * [Deprecated] Data that must be read from, if not null. + * @var string + */ + private $_queuedData = null; + + /** + * Array to hold the replacee-replacer pairs (String to String). + * @var array + */ + private $_tokens = array(); + + /** + * Array to hold the token sources that make tokens from + * different sources available + * @var array + */ + private $_tokensources = array(); + + /** + * Array holding all tokens given directly to the Filter and + * those passed via a TokenSource. + * @var array + */ + private $_alltokens = null; + + /** + * Character marking the beginning of a token. + * @var string + */ + private $_beginToken = "@"; // self::DEFAULT_BEGIN_TOKEN; + + /** + * Character marking the end of a token. + * @var string + */ + private $_endToken = "@"; //self::DEFAULT_END_TOKEN; + + /** + * Performs lookup on key and returns appropriate replacement string. + * @param array $matches Array of 1 el containing key to search for. + * @return string Text with which to replace key or value of key if none is found. + * @access private + */ + private function replaceTokenCallback($matches) { + + $key = $matches[1]; + + /* Get tokens from tokensource and merge them with the + * tokens given directly via build file. This should be + * done a bit more elegantly + */ + if ($this->_alltokens === null) { + $this->_alltokens = array(); + + $count = count($this->_tokensources); + for ($i = 0; $i < $count; $i++) { + $source = $this->_tokensources[$i]; + $this->_alltokens = array_merge($this->_alltokens, $source->getTokens()); + } + + + $this->_alltokens = array_merge($this->_tokens, $this->_alltokens); + } + + $tokens = $this->_alltokens; + + $replaceWith = null; + $count = count($tokens); + + for ($i = 0; $i < $count; $i++) { + if ($tokens[$i]->getKey() === $key) { + $replaceWith = $tokens[$i]->getValue(); + } + } + + if ($replaceWith === null) { + $replaceWith = $this->_beginToken . $key . $this->_endToken; + $this->log("No token defined for key \"".$this->_beginToken . $key . $this->_endToken."\""); + } else { + $this->log("Replaced \"".$this->_beginToken . $key . $this->_endToken ."\" with \"".$replaceWith."\"", Project::MSG_VERBOSE); + } + + return $replaceWith; + } + + /** + * Returns stream with tokens having been replaced with appropriate values. + * If a replacement value is not found for a token, the token is left in the stream. + * + * @return mixed filtered stream, -1 on EOF. + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // read from next filter up the chain + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // filter buffer + $buffer = preg_replace_callback( + "/".preg_quote($this->_beginToken, '/')."([\w\.\-:]+?)".preg_quote($this->_endToken, '/')."/", + array($this, 'replaceTokenCallback'), $buffer); + + return $buffer; + } + + /** + * Sets the "begin token" character. + * + * @param string $beginToken the character used to denote the beginning of a token. + */ + function setBeginToken($beginToken) { + $this->_beginToken = (string) $beginToken; + } + + /** + * Returns the "begin token" character. + * + * @return string The character used to denote the beginning of a token. + */ + function getBeginToken() { + return $this->_beginToken; + } + + /** + * Sets the "end token" character. + * + * @param string $endToken the character used to denote the end of a token + */ + function setEndToken($endToken) { + $this->_endToken = (string) $endToken; + } + + /** + * Returns the "end token" character. + * + * @return the character used to denote the beginning of a token + */ + function getEndToken() { + return $this->_endToken; + } + + /** + * Adds a token element to the map of tokens to replace. + * + * @return object The token added to the map of replacements. + * Must not be <code>null</code>. + */ + function createToken() { + $num = array_push($this->_tokens, new Token()); + return $this->_tokens[$num-1]; + } + + /** + * Adds a token source to the sources of this filter. + * + * @return object A Reference to the source just added. + */ + function createTokensource() { + $num = array_push($this->_tokensources, new TokenSource()); + return $this->_tokensources[$num-1]; + } + + /** + * Sets the map of tokens to replace. + * ; used by ReplaceTokens::chain() + * + * @param array A map (String->String) of token keys to replacement + * values. Must not be <code>null</code>. + */ + function setTokens($tokens) { + // type check, error must never occur, bad code of it does + if ( !is_array($tokens) ) { + throw new Exception("Excpected 'array', got something else"); + } + + $this->_tokens = $tokens; + } + + /** + * Returns the map of tokens which will be replaced. + * ; used by ReplaceTokens::chain() + * + * @return array A map (String->String) of token keys to replacement values. + */ + function getTokens() { + return $this->_tokens; + } + + /** + * Sets the tokensources to use; used by ReplaceTokens::chain() + * + * @param array An array of token sources. + */ + function setTokensources($sources) { + // type check + if ( !is_array($sources)) { + throw new Exception("Exspected 'array', got something else"); + } + $this->_tokensources = $sources; + } + + /** + * Returns the token sources used by this filter; used by ReplaceTokens::chain() + * + * @return array + */ + function getTokensources() { + return $this->_tokensources; + } + + /** + * Creates a new ReplaceTokens using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ReplaceTokens($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setBeginToken($this->getBeginToken()); + $newFilter->setEndToken($this->getEndToken()); + $newFilter->setTokens($this->getTokens()); + $newFilter->setTokensources($this->getTokensources()); + $newFilter->setInitialized(true); + return $newFilter; + } + + /** + * Initializes tokens and loads the replacee-replacer hashtable. + * This method is only called when this filter is used through + * a <filterreader> tag in build file. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $i<count($params) ; $i++) { + if ( $params[$i] !== null ) { + $type = $params[$i]->getType(); + if ( $type === "tokenchar" ) { + $name = $params[$i]->getName(); + if ( $name === "begintoken" ) { + $this->_beginToken = substr($params[$i]->getValue(), 0, 1); + } else if ( $name === "endtoken" ) { + $this->_endToken = substr($params[$i]->getValue(), 0, 1); + } + } else if ( $type === "token" ) { + $name = $params[$i]->getName(); + $value = $params[$i]->getValue(); + + $tok = new Token(); + $tok->setKey($name); + $tok->setValue($value); + + array_push($this->_tokens, $tok); + } else if ( $type === "tokensource" ) { + // Store data from nested tags in local array + $arr = array(); $subparams = $params[$i]->getParams(); + $count = count($subparams); + for ($i = 0; $i < $count; $i++) { + $arr[$subparams[$i]->getName()] = $subparams[$i]->getValue(); + } + + // Create TokenSource + $tokensource = new TokenSource(); + if (isset($arr["classname"])) + $tokensource->setClassname($arr["classname"]); + + // Copy other parameters 1:1 to freshly created TokenSource + foreach ($arr as $key => $value) { + if (strtolower($key) === "classname") + continue; + $param = $tokensource->createParam(); + $param->setName($key); + $param->setValue($value); + } + + $this->_tokensources[] = $tokensource; + } + } + } + } + } +} + +/** + * Holds a token. + * + * @package phing.filters + */ +class Token { + + /** + * Token key. + * @var string + */ + private $_key; + + /** + * Token value. + * @var string + */ + private $_value; + + /** + * Sets the token key. + * + * @param string $key The key for this token. Must not be <code>null</code>. + */ + function setKey($key) { + $this->_key = (string) $key; + } + + /** + * Sets the token value. + * + * @param string $value The value for this token. Must not be <code>null</code>. + */ + function setValue($value) { + // special case for boolean values + if (is_bool($value)) { + if ($value) { + $this->_value = "true"; + } else { + $this->_value = "false"; + } + } else { + $this->_value = (string) $value; + } + } + + /** + * Returns the key for this token. + * + * @return string The key for this token. + */ + function getKey() { + return $this->_key; + } + + /** + * Returns the value for this token. + * + * @return string The value for this token. + */ + function getValue() { + return $this->_value; + } + + /** + * Sets the token value from text. + * + * @param string $value The value for this token. Must not be <code>null</code>. + */ + function addText($value) { + $this->setValue($value); + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php b/buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php new file mode 100644 index 00000000..580b8d84 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/ReplaceTokensWithFile.php @@ -0,0 +1,361 @@ +<?php + +/* + * $Id: 164a2d9eeba3673653086b32e9fa2045168c992c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces tokens in the original input with the contents of a file. + * The file to be used is controlled by the name of the token which + * corresponds to the basename of the file to be used together with + * the optional pre and postfix strings that is possible to set. + * + * By default all HTML entities in the file is replaced by the + * corresponding HTML entities. This behaviour can be controlled by + * the "translatehtml" parameter. + * + * Supported parameters are: + * <pre> + * prefix string Text to be prefixed to token before using as filename + * postfix string Text to be prefixed to token before using as filename + * dir string The directory where the files should be read from + * translatehtml bool If we should translate all HTML entities in the file. + * </pre> + * Example: + * + * <pre><filterreader classname="phing.filters.ReplaceTokensWithFile"> + * <param name="dir" value="examples/" /> + * <param name="postfix" value=".php" /> + * </filterreader></pre> + * + * @author johan persson, johanp@aditus.nu + * @version $Id: 164a2d9eeba3673653086b32e9fa2045168c992c $ + * @access public + * @see ReplaceTokensWithFile + * @package phing.filters + */ +class ReplaceTokensWithFile extends BaseParamFilterReader implements ChainableReader { + + /** + * Default "begin token" character. + * @var string + */ + const DEFAULT_BEGIN_TOKEN = "#@#"; + + /** + * Default "end token" character. + * @var string + */ + const DEFAULT_END_TOKEN = "#@#"; + + /** + * Array to hold the token sources that make tokens from + * different sources available + * @var array + */ + private $_tokensources = array(); + + /** + * Character marking the beginning of a token. + * @var string + */ + private $_beginToken = ReplaceTokensWithFile::DEFAULT_BEGIN_TOKEN; + + /** + * Character marking the end of a token. + * @var string + */ + private $_endToken = ReplaceTokensWithFile::DEFAULT_END_TOKEN; + + /** + * File prefix to be inserted in front of the token to create the + * file name to be used. + * @var string + */ + private $_prefix = ''; + + /** + * File postfix to be inserted in front of the token to create the + * file name to be used. + * @var string + */ + private $_postfix = ''; + + /** + * Directory where to look for the files. The default is to look in the + * current file. + * + * @var string + */ + private $_dir = './'; + + /** + * Translate all HTML entities in the file to the corresponding HTML + * entities before it is used as replacements. For example all '<' + * will be translated to < before the content is inserted. + * + * @var boolean + */ + private $_translatehtml = true; + + + /** + * Sets the drectory where to look for the files to use for token replacement + * + * @param string $dir + */ + function setTranslateHTML($translate) { + $this->_translatehtml = (bool) $translate; + } + + /** + * Returns the drectory where to look for the files to use for token replacement + */ + function getTranslateHTML() { + return $this->_translatehtml; + } + + /** + * Sets the drectory where to look for the files to use for token replacement + * + * @param string $dir + */ + function setDir($dir) { + $this->_dir = (string) $dir; + } + + /** + * Returns the drectory where to look for the files to use for token replacement + */ + function getDir() { + return $this->_dir; + } + + /** + * Sets the prefix that is prepended to the token in order to create the file + * name. For example if the token is 01 and the prefix is "example" then + * the filename to look for will be "example01" + * + * @param string $prefix + */ + function setPrefix($prefix) { + $this->_prefix = (string) $prefix; + } + + /* + * Returns the prefix that is prepended to the token in order to create the file + * name. For example if the token is 01 and the prefix is "example" then + * the filename to look for will be "example01" + */ + function getPrefix() { + return $this->_prefix; + } + + /** + * Sets the postfix that is added to the token in order to create the file + * name. For example if the token is 01 and the postfix is ".php" then + * the filename to look for will be "01.php" + * + * @param string $postfix + */ + function setPostfix($postfix) { + $this->_postfix = (string) $postfix; + } + + /** + * Returns the postfix that is added to the token in order to create the file + * name. For example if the token is 01 and the postfix is ".php" then + * the filename to look for will be "01.php" + */ + function getPostfix() { + return $this->_postfix; + } + + /** + * Sets the "begin token" character. + * + * @param string $beginToken the character used to denote the beginning of a token. + */ + function setBeginToken($beginToken) { + $this->_beginToken = (string) $beginToken; + } + + /** + * Returns the "begin token" character. + * + * @return string The character used to denote the beginning of a token. + */ + function getBeginToken() { + return $this->_beginToken; + } + + /** + * Sets the "end token" character. + * + * @param string $endToken the character used to denote the end of a token + */ + function setEndToken($endToken) { + $this->_endToken = (string) $endToken; + } + + /** + * Returns the "end token" character. + * + * @return the character used to denote the beginning of a token + */ + function getEndToken() { + return $this->_endToken; + } + + /** + * Replace the token found with the appropriate file contents + * @param array $matches Array of 1 el containing key to search for. + * @return string Text with which to replace key or value of key if none is found. + * @access private + */ + private function replaceTokenCallback($matches) { + + $filetoken = $matches[1]; + + // We look in all specified directories for the named file and use + // the first directory which has the file. + $dirs = explode(';',$this->_dir); + + $ndirs = count($dirs); + $n = 0; + $file = $dirs[$n] . $this->_prefix . $filetoken . $this->_postfix; + + while ( $n < $ndirs && ! is_readable($file) ) { + ++$n; + } + + if( ! is_readable($file) || $n >= $ndirs ) { + $this->log("Can not read or find file \"$file\". Searched in directories: {$this->_dir}", Project::MSG_WARN); + //return $this->_beginToken . $filetoken . $this->_endToken; + return "[Phing::Filters::ReplaceTokensWithFile: Can not find file " . '"' . $filetoken . $this->_postfix . '"' . "]"; + } + + $buffer = file_get_contents($file); + if( $this->_translatehtml ) { + $buffer = htmlentities($buffer); + } + + if ($buffer === null) { + $buffer = $this->_beginToken . $filetoken . $this->_endToken; + $this->log("No corresponding file found for key \"$buffer\"", Project::MSG_WARN); + } else { + $this->log("Replaced \"".$this->_beginToken . $filetoken . $this->_endToken."\" with content from file \"$file\""); + } + + return $buffer; + } + + /** + * Returns stream with tokens having been replaced with appropriate values. + * If a replacement value is not found for a token, the token is left in the stream. + * + * @return mixed filtered stream, -1 on EOF. + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // read from next filter up the chain + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + // filter buffer + $buffer = preg_replace_callback( + "/".preg_quote($this->_beginToken)."([\w\.\-:\/]+?)".preg_quote($this->_endToken)."/", + array($this, 'replaceTokenCallback'), $buffer); + + return $buffer; + } + + /** + * Creates a new ReplaceTokensWithFile using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new ReplaceTokensWithFile($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setTranslateHTML($this->getTranslateHTML()); + $newFilter->setDir($this->getDir()); + $newFilter->setPrefix($this->getPrefix()); + $newFilter->setPostfix($this->getPostfix()); + $newFilter->setBeginToken($this->getBeginToken()); + $newFilter->setEndToken($this->getEndToken()); + $newFilter->setInitialized(true); + return $newFilter; + } + + /** + * Initializes parameters + * This method is only called when this filter is used through + * a <filterreader> tag in build file. + */ + private function _initialize() { + $params = $this->getParameters(); + $n = count($params); + + if ( $params !== null ) { + for($i = 0 ; $i < $n ; $i++) { + if ( $params[$i] !== null ) { + $name = $params[$i]->getName(); + switch( $name ) { + case 'begintoken' : + $this->_beginToken = $params[$i]->getValue(); + break; + case 'endtoken' : + $this->_endToken = $params[$i]->getValue(); + break; + case 'dir': + $this->_dir = $params[$i]->getValue(); + break; + case 'prefix': + $this->_prefix = $params[$i]->getValue(); + break; + case 'postfix': + $this->_postfix = $params[$i]->getValue(); + break; + case 'translatehtml': + $this->_translatehtml = $params[$i]->getValue(); + break; + } + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/StripLineBreaks.php b/buildscripts/phing/classes/phing/filters/StripLineBreaks.php new file mode 100755 index 00000000..08d1aa6b --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripLineBreaks.php @@ -0,0 +1,148 @@ +<?php + +/* + * $Id: 84767114c6fcf8abe5fcc0dce48513faf18d6306 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Filter to flatten the stream to a single line. + * + * Example: + * + * <pre><striplinebreaks/></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.StripLineBreaks"/></pre> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see BaseParamFilterReader + * @package phing.filters + */ +class StripLineBreaks extends BaseParamFilterReader implements ChainableReader { + + /** + * Default line-breaking characters. + * @var string + */ + const DEFAULT_LINE_BREAKS = "\r\n"; + + /** + * Parameter name for the line-breaking characters parameter. + * @var string + */ + const LINES_BREAKS_KEY = "linebreaks"; + + /** + * The characters that are recognized as line breaks. + * @var string + */ + private $_lineBreaks = "\r\n"; // self::DEFAULT_LINE_BREAKS; + + /** + * Returns the filtered stream, only including + * characters not in the set of line-breaking characters. + * + * @return mixed the resulting stream, or -1 + * if the end of the resulting stream has been reached. + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + $buffer = preg_replace("/[".$this->_lineBreaks."]/", '', $buffer); + + return $buffer; + } + + /** + * Sets the line-breaking characters. + * + * @param string $lineBreaks A String containing all the characters to be + * considered as line-breaking. + */ + function setLineBreaks($lineBreaks) { + $this->_lineBreaks = (string) $lineBreaks; + } + + /** + * Gets the line-breaking characters. + * + * @return string A String containing all the characters that are considered as line-breaking. + */ + function getLineBreaks() { + return $this->_lineBreaks; + } + + /** + * Creates a new StripLineBreaks using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new StripLineBreaks($reader); + $newFilter->setLineBreaks($this->getLineBreaks()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses the parameters to set the line-breaking characters. + */ + private function _initialize() { + $userDefinedLineBreaks = null; + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $i<count($params) ; $i++) { + if ( self::LINE_BREAKS_KEY === $params[$i]->getName() ) { + $userDefinedLineBreaks = $params[$i]->getValue(); + break; + } + } + } + + if ( $userDefinedLineBreaks !== null ) { + $this->_lineBreaks = $userDefinedLineBreaks; + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/StripLineComments.php b/buildscripts/phing/classes/phing/filters/StripLineComments.php new file mode 100755 index 00000000..2a19ca25 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripLineComments.php @@ -0,0 +1,207 @@ +<?php + +/* + * $Id: b0b2b1dc67fff8bd5285e43e9d11b15f4ef44ae7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * This filter strips line comments. + * + * Example: + * + * <pre><striplinecomments> + * <comment value="#"/> + * <comment value="--"/> + * <comment value="REM "/> + * <comment value="rem "/> + * <comment value="//"/> + * </striplinecomments></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.StripLineComments"> + * <param type="comment" value="#"/> + * <param type="comment" value="--"/> + * <param type="comment" value="REM "/> + * <param type="comment" value="rem "/> + * <param type="comment" value="//"/> + * </filterreader></pre> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see BaseParamFilterReader + * @package phing.filters + */ +class StripLineComments extends BaseParamFilterReader implements ChainableReader { + + /** Parameter name for the comment prefix. */ + const COMMENTS_KEY = "comment"; + + /** Array that holds the comment prefixes. */ + private $_comments = array(); + + /** + * Returns stream only including + * lines from the original stream which don't start with any of the + * specified comment prefixes. + * + * @return mixed the resulting stream, or -1 + * if the end of the resulting stream has been reached. + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if ($buffer === -1) { + return -1; + } + + $lines = explode("\n", $buffer); + $filtered = array(); + + $commentsSize = count($this->_comments); + + foreach($lines as $line) { + for($i = 0; $i < $commentsSize; $i++) { + $comment = $this->_comments[$i]->getValue(); + if ( StringHelper::startsWith($comment, ltrim($line)) ) { + $line = null; + break; + } + } + if ($line !== null) { + $filtered[] = $line; + } + } + + $filtered_buffer = implode("\n", $filtered); + return $filtered_buffer; + } + + /* + * Adds a <code>comment</code> element to the list of prefixes. + * + * @return comment The <code>comment</code> element added to the + * list of comment prefixes to strip. + */ + function createComment() { + $num = array_push($this->_comments, new Comment()); + return $this->_comments[$num-1]; + } + + /* + * Sets the list of comment prefixes to strip. + * + * @param comments A list of strings, each of which is a prefix + * for a comment line. Must not be <code>null</code>. + */ + function setComments($lineBreaks) { + if (!is_array($lineBreaks)) { + throw new Exception("Excpected 'array', got something else"); + } + $this->_comments = $lineBreaks; + } + + /* + * Returns the list of comment prefixes to strip. + * + * @return array The list of comment prefixes to strip. + */ + function getComments() { + return $this->_comments; + } + + /* + * Creates a new StripLineComments using the passed in + * Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new StripLineComments($reader); + $newFilter->setComments($this->getComments()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /* + * Parses the parameters to set the comment prefixes. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $i<count($params) ; $i++) { + if ( self::COMMENTS_KEY === $params[$i]->getType() ) { + $comment = new Comment(); + $comment->setValue($params[$i]->getValue()); + array_push($this->_comments, $comment); + } + } + } + } +} + +/** + * The class that holds a comment representation. + * + * @package phing.filters + */ +class Comment { + + /** The prefix for a line comment. */ + private $_value; + + /* + * Sets the prefix for this type of line comment. + * + * @param string $value The prefix for a line comment of this type. + * Must not be <code>null</code>. + */ + function setValue($value) { + $this->_value = (string) $value; + } + + /* + * Returns the prefix for this type of line comment. + * + * @return string The prefix for this type of line comment. + */ + function getValue() { + return $this->_value; + } +} + diff --git a/buildscripts/phing/classes/phing/filters/StripPhpComments.php b/buildscripts/phing/classes/phing/filters/StripPhpComments.php new file mode 100755 index 00000000..0abb8a67 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripPhpComments.php @@ -0,0 +1,188 @@ +<?php + +/* + * $Id: 6c68ae0ad1aa7f2f7825087c1c54233bd2462124 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * This is a Php comment and string stripper reader that filters + * those lexical tokens out for purposes of simple Php parsing. + * (if you have more complex Php parsing needs, use a real lexer). + * Since this class heavily relies on the single char read function, + * you are reccomended to make it work on top of a buffered reader. + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.filters + */ +class StripPhpComments extends BaseFilterReader implements ChainableReader { + /** + * The read-ahead character, used for effectively pushing a single + * character back. -1 indicates that no character is in the buffer. + */ + private $_readAheadCh = -1; + + /** + * Whether or not the parser is currently in the middle of a string + * literal. + * @var boolean + */ + private $_inString = false; + + /** + * Returns the stream without Php comments. + * + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + // This regex replace /* */ and // style comments + $buffer = preg_replace('/\/\*[^*]*\*+([^\/*][^*]*\*+)*\/|\/\/[^\n]*|("(\\\\.|[^"\\\\])*"|\'(\\\\.|[^\'\\\\])*\'|.[^\/"\'\\\\]*)/s', "$2", $buffer); + + // The regex above is not identical to, but is based on the expression below: + // + // created by Jeffrey Friedl + // and later modified by Fred Curtis. + // s{ + // /\* ## Start of /* ... */ comment + // [^*]*\*+ ## Non-* followed by 1-or-more *'s + // ( + // [^/*][^*]*\*+ + // )* ## 0-or-more things which don't start with / + // ## but do end with '*' + // / ## End of /* ... */ comment + // + // | ## OR various things which aren't comments: + // + // ( + // " ## Start of " ... " string + // ( + // \\. ## Escaped char + // | ## OR + // [^"\\] ## Non "\ + // )* + // " ## End of " ... " string + // + // | ## OR + // + // ' ## Start of ' ... ' string + // ( + // \\. ## Escaped char + // | ## OR + // [^'\\] ## Non '\ + // )* + // ' ## End of ' ... ' string + // + // | ## OR + // + // . ## Anything other char + // [^/"'\\]* ## Chars which doesn't start a comment, string or escape + // ) + // }{$2}gxs; + + return $buffer; + } + + + /* + * Returns the next character in the filtered stream, not including + * Php comments. + * + * @return the next character in the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + * @deprecated + */ + function readChar() { + $ch = -1; + + if ( $this->_readAheadCh !== -1 ) { + $ch = $this->_readAheadCh; + $this->_readAheadCh = -1; + } else { + $ch = $this->in->readChar(); + if ( $ch === "\"" ) { + $this->_inString = !$this->_inString; + } else { + if ( !$this->_inString ) { + if ( $ch === "/" ) { + $ch = $this->in->readChar(); + if ( $ch === "/" ) { + while ( $ch !== "\n" && $ch !== -1 ) { + $ch = $this->in->readChar(); + } + } else if ( $ch === "*" ) { + while ( $ch !== -1 ) { + $ch = $this->in->readChar(); + while ( $ch === "*" && $ch !== -1 ) { + $ch = $this->in->readChar(); + } + + if ( $ch === "/" ) { + $ch = $this->readChar(); + echo "$ch\n"; + break; + } + } + } else { + $this->_readAheadCh = $ch; + $ch = "/"; + } + } + } + } + } + + return $ch; + } + + /** + * Creates a new StripPhpComments using the passed in + * Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new StripPhpComments($reader); + $newFilter->setProject($this->getProject()); + return $newFilter; + } +} + diff --git a/buildscripts/phing/classes/phing/filters/StripWhitespace.php b/buildscripts/phing/classes/phing/filters/StripWhitespace.php new file mode 100755 index 00000000..d7b6113f --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/StripWhitespace.php @@ -0,0 +1,95 @@ +<?php + +/* + * $Id $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Strips whitespace from [php] files using PHP stripwhitespace() method. + * + * @author Hans Lellelid, hans@velum.net + * @version $Id$ + * @see FilterReader + * @package phing.filters + * @todo -c use new PHP functions to perform this instead of regex. + */ +class StripWhitespace extends BaseFilterReader implements ChainableReader { + + private $processed = false; + + /** + * Returns the stream without Php comments and whitespace. + * + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if ($this->processed === true) { + return -1; // EOF + } + + // Read XML + $php = null; + while ( ($buffer = $this->in->read($len)) !== -1 ) { + $php .= $buffer; + } + + if ($php === null ) { // EOF? + return -1; + } + + if(empty($php)) { + $this->log("PHP file is empty!", Project::MSG_WARN); + return ''; // return empty string, don't attempt to strip whitespace + } + + // write buffer to a temporary file, since php_strip_whitespace() needs a filename + $file = new PhingFile(tempnam(PhingFile::getTempDir(), 'stripwhitespace')); + file_put_contents($file->getAbsolutePath(), $php); + $output = php_strip_whitespace($file->getAbsolutePath()); + unlink($file->getAbsolutePath()); + + $this->processed = true; + + return $output; + } + + /** + * Creates a new StripWhitespace using the passed in + * Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + public function chain(Reader $reader) { + $newFilter = new StripWhitespace($reader); + $newFilter->setProject($this->getProject()); + return $newFilter; + } +} diff --git a/buildscripts/phing/classes/phing/filters/TabToSpaces.php b/buildscripts/phing/classes/phing/filters/TabToSpaces.php new file mode 100755 index 00000000..80d3c215 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TabToSpaces.php @@ -0,0 +1,144 @@ +<?php + +/* + * $Id: 71dc074faa0ed97b47c49fec5449233ea485120c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; +require_once 'phing/filters/ChainableReader.php'; + +/** + * Converts tabs to spaces. + * + * Example: + * + * <pre><tabtospaces tablength="8"></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.TabsToSpaces"> + * <param name="tablength" value="8"> + * </filterreader></pre> + * + * @author Yannick Lecaillez <yl@seasonfive.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see BaseParamFilterReader + * @package phing.filters + */ +class TabToSpaces extends BaseParamFilterReader implements ChainableReader { + + /** + * The default tab length. + * @var int + */ + const DEFAULT_TAB_LENGTH = 8; + + /** + * Parameter name for the length of a tab. + * @var string + */ + const TAB_LENGTH_KEY = "tablength"; + + /** + * Tab length in this filter. + * @var int + */ + private $tabLength = 8; //self::DEFAULT_TAB_LENGTH; + + /** + * Returns stream after converting tabs to the specified number of spaces. + * + * @return the resulting stream, or -1 + * if the end of the resulting stream has been reached + * + * @exception IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + + if($buffer === -1) { + return -1; + } + + $buffer = str_replace("\t", str_repeat(' ', $this->tabLength), $buffer); + + return $buffer; + } + + /** + * Sets the tab length. + * + * @param int $tabLength The number of spaces to be used when converting a tab. + */ + function setTablength($tabLength) { + $this->tabLength = (int) $tabLength; + } + + /** + * Returns the tab length. + * + * @return int The number of spaces used when converting a tab + */ + function getTablength() { + return $this->tabLength; + } + + /** + * Creates a new TabsToSpaces using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return Reader A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new TabToSpaces($reader); + $newFilter->setTablength($this->getTablength()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Parses the parameters to set the tab length. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0 ; $i<count($params) ; $i++) { + if (self::TAB_LENGTH_KEY === $params[$i]->getName()) { + $this->tabLength = $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/TailFilter.php b/buildscripts/phing/classes/phing/filters/TailFilter.php new file mode 100755 index 00000000..95a9c6f5 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TailFilter.php @@ -0,0 +1,157 @@ +<?php + +/* + * $Id: 3c108d45a4b3be6f6b9a395477e7641d8e17c44b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; + +/** + * Reads the last <code>n</code> lines of a stream. (Default is last10 lines.) + * + * Example: + * + * <pre><tailfilter lines="3" /></pre> + * + * Or: + * + * <pre><filterreader classname="phing.filters.TailFilter"> + * <param name="lines" value="3"> + * </filterreader></pre> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @author hans lellelid, hans@velum.net + * @copyright � 2003 seasonfive. All rights reserved + * @version $Id$ + * @see BaseParamFilterReader + * @package phing.filters + */ +class TailFilter extends BaseParamFilterReader implements ChainableReader { + + /** + * Parameter name for the number of lines to be returned. + * @var string + */ + const LINES_KEY = "lines"; + + + /** + * Number of lines to be returned in the filtered stream. + * @var integer + */ + private $_lines = 10; + + /** + * Array to hold lines. + * @var array + */ + private $_lineBuffer = array(); + + /** + * Returns the last n lines of a file. + * @param int $len Num chars to read. + * @return mixed The filtered buffer or -1 if EOF. + */ + function read($len = null) { + + while ( ($buffer = $this->in->read($len)) !== -1 ) { + // Remove the last "\n" from buffer for + // prevent explode to add an empty cell at + // the end of array + $buffer= trim($buffer, "\n"); + + $lines = explode("\n", $buffer); + + if ( count($lines) >= $this->_lines ) { + // Buffer have more (or same) number of lines than needed. + // Fill lineBuffer with the last "$this->_lines" lasts ones. + $off = count($lines)-$this->_lines; + $this->_lineBuffer = array_slice($lines, $off); + } else { + // Some new lines ... + // Prepare space for insert these new ones + $this->_lineBuffer = array_slice($this->_lineBuffer, count($lines)-1); + $this->_lineBuffer = array_merge($this->_lineBuffer, $lines); + } + } + + if ( empty($this->_lineBuffer) ) + $ret = -1; + else { + $ret = implode("\n", $this->_lineBuffer); + $this->_lineBuffer = array(); + } + + return $ret; + } + + /** + * Sets the number of lines to be returned in the filtered stream. + * + * @param integer $lines the number of lines to be returned in the filtered stream. + */ + function setLines($lines) { + $this->_lines = (int) $lines; + } + + /** + * Returns the number of lines to be returned in the filtered stream. + * + * @return integer The number of lines to be returned in the filtered stream. + */ + function getLines() { + return $this->_lines; + } + + /** + * Creates a new TailFilter using the passed in + * Reader for instantiation. + * + * @param object A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return object A new filter based on this configuration, but filtering + * the specified reader. + */ + function chain(Reader $reader) { + $newFilter = new TailFilter($reader); + $newFilter->setLines($this->getLines()); + $newFilter->setInitialized(true); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Scans the parameters list for the "lines" parameter and uses + * it to set the number of lines to be returned in the filtered stream. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i=0, $_i=count($params); $i < $_i; $i++) { + if ( self::LINES_KEY == $params[$i]->getName() ) { + $this->_lines = (int) $params[$i]->getValue(); + break; + } + } + } + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/TidyFilter.php b/buildscripts/phing/classes/phing/filters/TidyFilter.php new file mode 100755 index 00000000..22abcee5 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TidyFilter.php @@ -0,0 +1,162 @@ +<?php +/* + * $Id: a612fea7722441639c6dfdc69b43ad65ec02652f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * This filter uses the bundled-with-PHP Tidy extension to filter input. + * + * <p> + * Example:<br/> + * <pre> + * <tidyfilter encoding="utf8"> + * <config name="indent" value="true"/> + * <config name="output-xhtml" value="true"/> + * </tidyfilter> + * </pre> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.filters + */ +class TidyFilter extends BaseParamFilterReader implements ChainableReader { + + /** @var string Encoding of resulting document. */ + private $encoding = 'utf8'; + + /** @var array Parameter[] */ + private $configParameters = array(); + + /** + * Set the encoding for resulting (X)HTML document. + * @param string $v + */ + public function setEncoding($v) { + $this->encoding = $v; + } + + /** + * Sets the config params. + * @param array Parameter[] + * @see chain() + */ + public function setConfigParameters($params) + { + $this->configParameters = $params; + } + + /** + * Adds a <config> element (which is a Parameter). + * @return Parameter + */ + public function createConfig() { + $num = array_push($this->configParameters, new Parameter()); + return $this->configParameters[$num-1]; + } + + /** + * Converts the Parameter objects being used to store configuration into a simle assoc array. + * @return array + */ + private function getDistilledConfig() { + $config = array(); + foreach($this->configParameters as $p) { + $config[$p->getName()] = $p->getValue(); + } + return $config; + } + + /** + * Reads input and returns Tidy-filtered output. + * + * @return the resulting stream, or -1 if the end of the resulting stream has been reached + * + * @throws IOException if the underlying stream throws an IOException + * during reading + */ + function read($len = null) { + + if (!class_exists('Tidy')) { + throw new BuildException("You must enable the 'tidy' extension in your PHP configuration in order to use the Tidy filter."); + } + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + $config = $this->getDistilledConfig(); + + $tidy = new Tidy(); + $tidy->parseString($buffer, $config, $this->encoding); + $tidy->cleanRepair(); + + return tidy_get_output($tidy); + + } + + + /** + * Creates a new TidyFilter using the passed in Reader for instantiation. + * + * @param reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return a new filter based on this configuration, but filtering + * the specified reader + */ + public function chain(Reader $reader) { + $newFilter = new TidyFilter($reader); + $newFilter->setConfigParameters($this->configParameters); + $newFilter->setEncoding($this->encoding); + $newFilter->setProject($this->getProject()); + return $newFilter; + } + + /** + * Initializes any parameters (e.g. config options). + * This method is only called when this filter is used through a <filterreader> tag in build file. + */ + private function _initialize() { + $params = $this->getParameters(); + if ($params) { + foreach($params as $param) { + if ($param->getType() == "config") { + $this->configParameters[] = $param; + } else { + + if ($param->getName() == "encoding") { + $this->setEncoding($param->getValue()); + } + + } + + } + } + } + +} diff --git a/buildscripts/phing/classes/phing/filters/TranslateGettext.php b/buildscripts/phing/classes/phing/filters/TranslateGettext.php new file mode 100755 index 00000000..b2a4264c --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TranslateGettext.php @@ -0,0 +1,285 @@ +<?php + +/* + * $Id: 7dc28b63ed7f57bcf86d92f2b669bd386c2076a6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces gettext("message id") and _("message id") with the translated string. + * + * Gettext is great for creating multi-lingual sites, but in some cases (e.g. for + * performance reasons) you may wish to replace the gettext calls with the translations + * of the strings; that's what this task is for. Note that this is similar to + * ReplaceTokens, but both the find and the replace aspect is more complicated -- hence + * this is a separate, stand-alone filter. + * + * <p> + * Example:<br> + * <pre> + * <translategettext locale="en_US" domain="messages" dir="${webroot}/local"/> + * </pre> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @access public + * @see BaseFilterReader + * @package phing.filters + */ +class TranslateGettext extends BaseParamFilterReader implements ChainableReader { + + // constants for specifying keys to expect + // when this is called using <filterreader ... /> + const DOMAIN_KEY = "domain"; + const DIR_KEY = "dir"; + const LOCALE_KEY = "locale"; + + /** The domain to use */ + private $domain = 'messages'; + + /** The dir containing LC_MESSAGES */ + private $dir; + + /** The locale to use */ + private $locale; + + /** The system locale before it was changed for this filter. */ + private $storedLocale; + + /** + * Set the text domain to use. + * The text domain must correspond to the name of the compiled .mo files. + * E.g. "messages" ==> $dir/LC_MESSAGES/messages.mo + * "mydomain" ==> $dir/LC_MESSAGES/mydomain.mo + * @param string $domain + */ + function setDomain($domain) { + $this->domain = $domain; + } + + /** + * Get the current domain. + * @return string + */ + function getDomain() { + return $this->domain; + } + + /** + * Sets the root locale directory. + * @param PhingFile $dir + */ + function setDir(PhingFile $dir) { + $this->dir = $dir; + } + + /** + * Gets the root locale directory. + * @return PhingFile + */ + function getDir() { + return $this->dir; + } + + /** + * Sets the locale to use for translation. + * Note that for gettext() to work, you have to make sure this locale + * is specific enough for your system (e.g. some systems may allow an 'en' locale, + * but others will require 'en_US', etc.). + * @param string $locale + */ + function setLocale($locale) { + $this->locale = $locale; + } + + /** + * Gets the locale to use for translation. + * @return string + */ + function getLocale() { + return $this->locale; + } + + /** + * Make sure that required attributes are set. + * @throws BuldException - if any required attribs aren't set. + */ + protected function checkAttributes() { + if (!$this->domain || !$this->locale || !$this->dir) { + throw new BuildException("You must specify values for domain, locale, and dir attributes."); + } + } + + /** + * Initialize the gettext/locale environment. + * This method will change some env vars and locale settings; the + * restoreEnvironment should put them all back :) + * + * @return void + * @throws BuildException - if locale cannot be set. + * @see restoreEnvironment() + */ + protected function initEnvironment() { + $this->storedLocale = getenv("LANG"); + + $this->log("Setting locale to " . $this->locale, Project::MSG_DEBUG); + putenv("LANG=".$this->locale); + $ret = setlocale(LC_ALL, $this->locale); + if ($ret === false) { + $msg = "Could not set locale to " . $this->locale + . ". You may need to use fully qualified name" + . " (e.g. en_US instead of en)."; + throw new BuildException($msg); + } + + $this->log("Binding domain '".$this->domain."' to " . $this->dir, Project::MSG_DEBUG); + bindtextdomain($this->domain, $this->dir->getAbsolutePath()); + textdomain($this->domain); + } + + /** + * Restores environment settings and locale. + * This does _not_ restore any gettext-specific settings + * (e.g. textdomain()). + * + * @return void + */ + protected function restoreEnvironment() { + putenv("LANG=".$this->storedLocale); + setlocale(LC_ALL, $this->storedLocale); + } + + /** + * Performs gettext translation of msgid and returns translated text. + * + * This function simply wraps gettext() call, but provides ability to log + * string replacements. (alternative would be using preg_replace with /e which + * would probably be faster, but no ability to debug/log.) + * + * @param array $matches Array of matches; we're interested in $matches[2]. + * @return string Translated text + */ + private function xlateStringCallback($matches) { + $charbefore = $matches[1]; + $msgid = $matches[2]; + $translated = gettext($msgid); + $this->log("Translating \"$msgid\" => \"$translated\"", Project::MSG_DEBUG); + return $charbefore . '"' . $translated . '"'; + } + + /** + * Returns the filtered stream. + * The original stream is first read in fully, and then translation is performed. + * + * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached. + * + * @throws IOException - if the underlying stream throws an IOException during reading + * @throws BuildException - if the correct params are not supplied + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // Make sure correct params/attribs have been set + $this->checkAttributes(); + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + // Setup the locale/gettext environment + $this->initEnvironment(); + + + // replace any occurrences of _("") or gettext("") with + // the translated value. + // + // ([^\w]|^)_\("((\\"|[^"])*)"\) + // --$1--- -----$2---- + // ---$3-- [match escaped quotes or any char that's not a quote] + // + // also match gettext() -- same as above + + $buffer = preg_replace_callback('/([^\w]|^)_\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer); + $buffer = preg_replace_callback('/([^\w]|^)gettext\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer); + + // Check to see if there are any _('') calls and flag an error + + // Check to see if there are any unmatched gettext() calls -- and flag an error + + $matches = array(); + if (preg_match('/([^\w]|^)(gettext\([^\)]+\))/', $buffer, $matches)) { + $this->log("Unable to perform translation on: " . $matches[2], Project::MSG_WARN); + } + + $this->restoreEnvironment(); + + return $buffer; + } + + /** + * Creates a new TranslateGettext filter using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return TranslateGettext A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new TranslateGettext($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setDomain($this->getDomain()); + $newFilter->setLocale($this->getLocale()); + $newFilter->setDir($this->getDir()); + return $newFilter; + } + + /** + * Parses the parameters if this filter is being used in "generic" mode. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + foreach($params as $param) { + switch($param->getType()) { + case self::DOMAIN_KEY: + $this->setDomain($param->getValue()); + break; + case self::DIR_KEY: + $this->setDir($this->project->resolveFile($param->getValue())); + break; + + case self::LOCALE_KEY: + $this->setLocale($param->getValue()); + break; + } // switch + } + } // if params !== null + } +} + + diff --git a/buildscripts/phing/classes/phing/filters/XincludeFilter.php b/buildscripts/phing/classes/phing/filters/XincludeFilter.php new file mode 100644 index 00000000..e2b3cd00 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/XincludeFilter.php @@ -0,0 +1,176 @@ +<?php + +/* + * $Id: 6c47e03d52cf26c183b05e347dac83735dd8c8dd $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Applies Xinclude parsing to incoming text. + * + * Uses PHP DOM XML support + * + * @author Bill Karwin <bill@karwin.com> + * @version $Id: 6c47e03d52cf26c183b05e347dac83735dd8c8dd $ + * @see FilterReader + * @package phing.filters + */ +class XincludeFilter extends BaseParamFilterReader implements ChainableReader { + + private $basedir = null; + + /** + * @var bool + */ + private $processed = false; + + /** + * Whether to resolve entities. + * + * @var bool + * + * @since 2.4 + */ + private $resolveExternals = false; + + /** + * Whether to resolve entities. + * + * @param $resolveExternals + * + * @since 2.4 + */ + public function setResolveExternals($resolveExternals) + { + $this->resolveExternals = (bool)$resolveExternals; + } + + /** + * @return bool + * + * @since 2.4 + */ + public function getResolveExternals() + { + return $this->resolveExternals; + } + + public function setBasedir(PhingFile $dir) + { + $this->basedir = $dir; + } + + public function getBasedir() + { + return $this->basedir; + } + + /** + * Reads stream, applies XSLT and returns resulting stream. + * @return string transformed buffer. + * @throws BuildException - if XSLT support missing, if error in xslt processing + */ + function read($len = null) { + + if (!class_exists('DomDocument')) { + throw new BuildException("Could not find the DomDocument class. Make sure PHP has been compiled/configured to support DOM XML."); + } + + if ($this->processed === true) { + return -1; // EOF + } + + // Read XML + $_xml = null; + while ( ($data = $this->in->read($len)) !== -1 ) + $_xml .= $data; + + if ($_xml === null ) { // EOF? + return -1; + } + + if (empty($_xml)) { + $this->log("XML file is empty!", Project::MSG_WARN); + return ''; + } + + $this->log("Transforming XML " . $this->in->getResource() . " using Xinclude ", Project::MSG_VERBOSE); + + $out = ''; + try { + $out = $this->process($_xml); + $this->processed = true; + } catch (IOException $e) { + throw new BuildException($e); + } + + return $out; + } + + /** + * Try to process the Xinclude transformation + * + * @param string XML to process. + * + * @throws BuildException On errors + */ + protected function process($xml) { + + if ($this->basedir) { + $cwd = getcwd(); + chdir($this->basedir); + } + + // Create and setup document. + $xmlDom = new DomDocument(); + $xmlDom->resolveExternals = $this->resolveExternals; + + $xmlDom->loadXML($xml); + + $xmlDom->xinclude(); + + if ($this->basedir) { + chdir($cwd); + } + + return $xmlDom->saveXML(); + } + + /** + * Creates a new XincludeFilter using the passed in + * Reader for instantiation. + * + * @param Reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return Reader A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new XincludeFilter($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setBasedir($this->getBasedir()); + return $newFilter; + } + +} + + diff --git a/buildscripts/phing/classes/phing/filters/XsltFilter.php b/buildscripts/phing/classes/phing/filters/XsltFilter.php new file mode 100644 index 00000000..8866bff7 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/XsltFilter.php @@ -0,0 +1,408 @@ +<?php + +/* + * $Id: 057af49d450e4c137127acc0f5331368e7a76183 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Applies XSL stylesheet to incoming text. + * + * Uses PHP XSLT support (libxslt). + * + * @author Hans Lellelid <hans@velum.net> + * @author Yannick Lecaillez <yl@seasonfive.com> + * @author Andreas Aderhold <andi@binarycloud.com> + * @version $Id: 057af49d450e4c137127acc0f5331368e7a76183 $ + * @see FilterReader + * @package phing.filters + */ +class XsltFilter extends BaseParamFilterReader implements ChainableReader { + + /** + * Path to XSL stylesheet. + * @var string + */ + private $xslFile = null; + + /** + * Whether XML file has been transformed. + * @var boolean + */ + private $processed = false; + + /** + * XSLT Params. + * @var array + */ + private $xsltParams = array(); + + /** + * Whether to use loadHTML() to parse the input XML file. + */ + private $html = false; + + /** + * Whether to resolve entities in the XML document (see + * {@link http://www.php.net/manual/en/class.domdocument.php#domdocument.props.resolveexternals} + * for more details). + * + * @var bool + * + * @since 2.4 + */ + private $resolveDocumentExternals = false; + + /** + * Whether to resolve entities in the stylesheet. + * + * @var bool + * + * @since 2.4 + */ + private $resolveStylesheetExternals = false; + + /** + * Create new XSLT Param object, to handle the <param/> nested element. + * @return XSLTParam + */ + function createParam() { + $num = array_push($this->xsltParams, new XSLTParam()); + return $this->xsltParams[$num-1]; + } + + /** + * Sets the XSLT params for this class. + * This is used to "clone" this class, in the chain() method. + * @param array $params + */ + function setParams($params) { + $this->xsltParams = $params; + } + + /** + * Returns the XSLT params set for this class. + * This is used to "clone" this class, in the chain() method. + * @return array + */ + function getParams() { + return $this->xsltParams; + } + + /** + * Set the XSLT stylesheet. + * @param mixed $file PhingFile object or path. + */ + function setStyle(PhingFile $file) { + $this->xslFile = $file; + } + + /** + * Whether to use HTML parser for the XML. + * This is supported in libxml2 -- Yay! + * @return boolean + */ + function getHtml() { + return $this->html; + } + + /** + * Whether to use HTML parser for XML. + * @param boolean $b + */ + function setHtml($b) { + $this->html = (boolean) $b; + } + + /** + * Get the path to XSLT stylesheet. + * @return mixed XSLT stylesheet path. + */ + function getStyle() { + return $this->xslFile; + } + + /** + * Whether to resolve entities in document. + * + * @param bool $resolveExternals + * + * @since 2.4 + */ + function setResolveDocumentExternals($resolveExternals) { + $this->resolveDocumentExternals = (bool)$resolveExternals; + } + + /** + * @return bool + * + * @since 2.4 + */ + function getResolveDocumentExternals() { + return $this->resolveDocumentExternals; + } + + /** + * Whether to resolve entities in stylesheet. + * + * @param bool $resolveExternals + * + * @since 2.4 + */ + function setResolveStylesheetExternals($resolveExternals) { + $this->resolveStylesheetExternals = (bool)$resolveExternals; + } + + /** + * @return bool + * + * @since 2.4 + */ + function getResolveStylesheetExternals() { + return $this->resolveStylesheetExternals; + } + + /** + * Reads stream, applies XSLT and returns resulting stream. + * @return string transformed buffer. + * @throws BuildException - if XSLT support missing, if error in xslt processing + */ + function read($len = null) { + + if (!class_exists('XSLTProcessor')) { + throw new BuildException("Could not find the XSLTProcessor class. Make sure PHP has been compiled/configured to support XSLT."); + } + + if ($this->processed === true) { + return -1; // EOF + } + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // Read XML + $_xml = null; + while ( ($data = $this->in->read($len)) !== -1 ) + $_xml .= $data; + + if ($_xml === null ) { // EOF? + return -1; + } + + if(empty($_xml)) { + $this->log("XML file is empty!", Project::MSG_WARN); + return ''; // return empty string, don't attempt to apply XSLT + } + + // Read XSLT + $_xsl = null; + $xslFr = new FileReader($this->xslFile); + $xslFr->readInto($_xsl); + + $this->log("Tranforming XML " . $this->in->getResource() . " using style " . $this->xslFile->getPath(), Project::MSG_VERBOSE); + + $out = ''; + try { + $out = $this->process($_xml, $_xsl); + $this->processed = true; + } catch (IOException $e) { + throw new BuildException($e); + } + + return $out; + } + + // {{{ method _ProcessXsltTransformation($xml, $xslt) throws BuildException + /** + * Try to process the XSLT transformation + * + * @param string XML to process. + * @param string XSLT sheet to use for the processing. + * + * @throws BuildException On XSLT errors + */ + protected function process($xml, $xsl) { + + $processor = new XSLTProcessor(); + + // Create and setup document. + $xmlDom = new DOMDocument(); + $xmlDom->resolveExternals = $this->resolveDocumentExternals; + + // Create and setup stylesheet. + $xslDom = new DOMDocument(); + $xslDom->resolveExternals = $this->resolveStylesheetExternals; + + if ($this->html) { + $xmlDom->loadHTML($xml); + } else { + $xmlDom->loadXML($xml); + } + + $xslDom->loadxml($xsl); + + $processor->importStylesheet($xslDom); + + // ignoring param "type" attrib, because + // we're only supporting direct XSL params right now + foreach($this->xsltParams as $param) { + $this->log("Setting XSLT param: " . $param->getName() . "=>" . $param->getExpression(), Project::MSG_DEBUG); + $processor->setParameter(null, $param->getName(), $param->getExpression()); + } + + $errorlevel = error_reporting(); + error_reporting($errorlevel & ~E_WARNING); + @$result = $processor->transformToXML($xmlDom); + error_reporting($errorlevel); + + if (false === $result) { + //$errno = xslt_errno($processor); + //$err = xslt_error($processor); + throw new BuildException("XSLT Error"); + } else { + return $result; + } + } + + /** + * Creates a new XsltFilter using the passed in + * Reader for instantiation. + * + * @param Reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return Reader A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new XsltFilter($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setStyle($this->getStyle()); + $newFilter->setInitialized(true); + $newFilter->setParams($this->getParams()); + $newFilter->setHtml($this->getHtml()); + return $newFilter; + } + + /** + * Parses the parameters to get stylesheet path. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + for($i = 0, $_i=count($params) ; $i < $_i; $i++) { + if ( $params[$i]->getType() === null ) { + if ($params[$i]->getName() === "style") { + $this->setStyle($params[$i]->getValue()); + } + } elseif ($params[$i]->getType() == "param") { + $xp = new XSLTParam(); + $xp->setName($params[$i]->getName()); + $xp->setExpression($params[$i]->getValue()); + $this->xsltParams[] = $xp; + } + } + } + } + +} + + +/** + * Class that holds an XSLT parameter. + * + * @package phing.filters + */ +class XSLTParam { + + private $name; + + private $expr; + + /** + * Sets param name. + * @param string $name + */ + public function setName($name) { + $this->name = $name; + } + + /** + * Get param name. + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * Sets expression value (alias to the setExpression()) method. + * + * @param string $v + * @see setExpression() + */ + public function setValue($v) + { + $this->setExpression($v); + } + + /** + * Gets expression value (alias to the getExpression()) method. + * + * @param string $v + * @see getExpression() + */ + public function getValue() + { + return $this->getExpression(); + } + + /** + * Sets expression value. + * @param string $expr + */ + public function setExpression($expr) { + $this->expr = $expr; + } + + /** + * Sets expression to dynamic register slot. + * @param RegisterSlot $expr + */ + public function setListeningExpression(RegisterSlot $expr) { + $this->expr = $expr; + } + + /** + * Returns expression value -- performs lookup if expr is registerslot. + * @return string + */ + public function getExpression() { + if ($this->expr instanceof RegisterSlot) { + return $this->expr->getValue(); + } else { + return $this->expr; + } + } +} + diff --git a/buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php b/buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php new file mode 100755 index 00000000..c465d0a1 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/util/ChainReaderHelper.php @@ -0,0 +1,183 @@ +<?php +/* + * $Id: c1709ddb9da44ce62fbe072c61d29dba4814e3f6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/Project.php'; +include_once 'phing/filters/BaseFilterReader.php'; +include_once 'phing/types/PhingFilterReader.php'; +include_once 'phing/types/FilterChain.php'; +include_once 'phing/types/Parameter.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Process a FilterReader chain. + * + * Here, the interesting method is 'getAssembledReader'. + * The purpose of this one is to create a simple Reader object which + * apply all filters on another primary Reader object. + * + * For example : In copyFile (phing.util.FileUtils) the primary Reader + * is a FileReader object (more accuratly, a BufferedReader) previously + * setted for the source file to copy. So, consider this filterchain : + * + * <filterchain> + * <stripphpcomments /> + * <linecontains> + * <contains value="foo"> + * </linecontains> + * <tabtospaces tablength="8" /> + * </filterchain> + * + * getAssembledReader will return a Reader object wich read on each + * of these filters. Something like this : ('->' = 'which read data from') : + * + * [TABTOSPACES] -> [LINECONTAINS] -> [STRIPPHPCOMMENTS] -> [FILEREADER] + * (primary reader) + * + * So, getAssembledReader will return the TABTOSPACES Reader object. Then + * each read done with this Reader object will follow this path. + * + * Hope this explanation is clear :) + * + * TODO: Implement the classPath feature. + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @version $Id$ + * @access public + * @package phing.filters.util +*/ +class ChainReaderHelper { + + /** Primary reader to wich the reader chain is to be attached */ + private $primaryReader = null; + + /** The site of the buffer to be used. */ + private $bufferSize = 8192; + + /** Chain of filters */ + private $filterChains = array(); + + /** The Phing project */ + private $project; + + /* + * Sets the primary reader + */ + function setPrimaryReader(Reader $reader) { + $this->primaryReader = $reader; + } + + /* + * Set the project to work with + */ + function setProject(Project $project) { + $this->project = $project; + } + + /* + * Get the project + */ + function getProject() { + return $this->project; + } + + /* + * Sets the buffer size to be used. Defaults to 8192, + * if this method is not invoked. + */ + function setBufferSize($size) { + $this->bufferSize = $size; + } + + /* + * Sets the collection of filter reader sets + */ + function setFilterChains(&$fchain) { + $this->filterChains = &$fchain; + } + + /* + * Assemble the reader + */ + function getAssembledReader() { + + $instream = $this->primaryReader; + $filterReadersCount = count($this->filterChains); + $finalFilters = array(); + + // Collect all filter readers of all filter chains used ... + for($i = 0 ; $i<$filterReadersCount ; $i++) { + $filterchain = &$this->filterChains[$i]; + $filterReaders = $filterchain->getFilterReaders(); + $readerCount = count($filterReaders); + for($j = 0 ; $j<$readerCount ; $j++) { + $finalFilters[] = $filterReaders[$j]; + } + } + + // ... then chain the filter readers. + $filtersCount = count($finalFilters); + if ( $filtersCount > 0 ) { + for($i = 0 ; $i<$filtersCount ; $i++) { + $filter = $finalFilters[$i]; + + if ( $filter instanceof PhingFilterReader ) { + + // This filter reader is an external class. + $className = $filter->getClassName(); + $classpath = $filter->getClasspath(); + $project = $filter->getProject(); + + if ( $className !== null ) { + $cls = Phing::import($className, $classpath); + $impl = new $cls(); + } + + if ( !($impl instanceof FilterReader) ) { + throw new Exception($className." does not extend phing.system.io.FilterReader"); + } + + $impl->setReader($instream); // chain + $impl->setProject($this->getProject()); // what about $project above ? + + if ( $impl instanceof Parameterizable ) { + $impl->setParameters($filter->getParams()); + } + + $instream = $impl; // now that it's been chained + + } elseif (($filter instanceof ChainableReader) && ($filter instanceof Reader)) { + if ( $this->getProject() !== null && ($filter instanceof BaseFilterReader) ) { + $filter->setProject($this->getProject()); + } + $instream = $filter->chain($instream); + } else { + throw new Exception("Cannot chain invalid filter: " . get_class($filter)); + } + } + } + + return $instream; + } + +} + diff --git a/buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php b/buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php new file mode 100755 index 00000000..f47e155c --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/util/IniFileTokenReader.php @@ -0,0 +1,97 @@ +<?php +/* + * $Id: e709765b4c0c1be330183f462ab527fa8354b555 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/types/TokenReader.php'; +include_once 'phing/system/io/IOException.php'; +include_once 'phing/filters/ReplaceTokens.php'; // For class Token + +/** + * Class that allows reading tokens from INI files. + * + * @author Manuel Holtgewe + * @version $Id$ + * @package phing.filters.util + */ +class IniFileTokenReader extends TokenReader { + + /** + * Holds the path to the INI file that is to be read. + * @var object Reference to a PhingFile Object representing + * the path to the INI file. + */ + private $file = null; + + /** + * @var string Sets the section to load from the INI file. + * if omitted, all sections are loaded. + */ + private $section = null; + + /** + * Reads the next token from the INI file + * + * @throws IOException On error + * @return Token + */ + function readToken() { + if ($this->file === null) { + throw new BuildException("No File set for IniFileTokenReader"); + } + + static $tokens = null; + if ($tokens === null) { + $tokens = array(); + $arr = parse_ini_file($this->file->getAbsolutePath(), true); + if ($this->section === null) { + foreach ($arr as $sec_name => $values) { + foreach($arr[$sec_name] as $key => $value) { + $tok = new Token; + $tok->setKey($key); + $tok->setValue($value); + $tokens[] = $tok; + } + } + } else if (isset($arr[$this->section])) { + foreach ($arr[$this->section] as $key => $value) { + $tok = new Token; + $tok->setKey($key); + $tok->setValue($value); + $tokens[] = $tok; + } + } + } + + if (count($tokens) > 0) { + return array_pop($tokens); + } else + return null; + } + + function setFile(PhingFile $file) { + $this->file = $file; + } + + function setSection($str) { + $this->section = (string) $str; + } +} + + diff --git a/buildscripts/phing/classes/phing/input/DefaultInputHandler.php b/buildscripts/phing/classes/phing/input/DefaultInputHandler.php new file mode 100755 index 00000000..5e53c878 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/DefaultInputHandler.php @@ -0,0 +1,85 @@ +<?php + +/* + * $Id: 5d3d4fb125da3344b5aafec31ba330969bdbdb42 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/input/InputHandler.php'; +include_once 'phing/system/io/ConsoleReader.php'; + +/** + * Prompts using print(); reads input from Console. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @version $Id$ + * @package phing.input + */ +class DefaultInputHandler implements InputHandler { + + /** + * Prompts and requests input. May loop until a valid input has + * been entered. + * @throws BuildException + */ + public function handleInput(InputRequest $request) { + $prompt = $this->getPrompt($request); + $in = new ConsoleReader(); + do { + print $prompt; + try { + $input = $in->readLine(); + if ($input === "" && ($request->getDefaultValue() !== null) ) { + $input = $request->getDefaultValue(); + } + $request->setInput($input); + } catch (Exception $e) { + throw new BuildException("Failed to read input from Console.", $e); + } + } while (!$request->isInputValid()); + } + + /** + * Constructs user prompt from a request. + * + * <p>This implementation adds (choice1,choice2,choice3,...) to the + * prompt for <code>MultipleChoiceInputRequest</code>s.</p> + * + * @param $request the request to construct the prompt for. + * Must not be <code>null</code>. + */ + protected function getPrompt(InputRequest $request) { + $prompt = $request->getPrompt(); + $defaultValue = $request->getDefaultValue(); + + if ($request instanceof YesNoInputRequest) { + $choices = $request->getChoices(); + $defaultValue = $choices[(int) !$request->getDefaultValue()]; + $prompt .= '(' . implode('/', $request->getChoices()) .')'; + } elseif ($request instanceof MultipleChoiceInputRequest) { // (a,b,c,d) + $prompt .= '(' . implode(',', $request->getChoices()) . ')'; + } + + if ($request->getDefaultValue() !== null) { + $prompt .= ' ['.$defaultValue.']'; + } + $pchar = $request->getPromptChar(); + return $prompt . ($pchar ? $pchar . ' ' : ' '); + } +} diff --git a/buildscripts/phing/classes/phing/input/InputHandler.php b/buildscripts/phing/classes/phing/input/InputHandler.php new file mode 100755 index 00000000..9a414fd4 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/InputHandler.php @@ -0,0 +1,45 @@ +<?php + +/* + * $Id: c7412bfab167852910c4dfe935769e4b0c7ec9fe $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Plugin to Phing to handle requests for user input. + * + * @author Stefan Bodewig <stefan.bodewig@epost.de> + * @version $Id$ + * @package phing.input + */ +interface InputHandler { + + /** + * Handle the request encapsulated in the argument. + * + * <p>Precondition: the request.getPrompt will return a non-null + * value.</p> + * + * <p>Postcondition: request.getInput will return a non-null + * value, request.isInputValid will return true.</p> + * @return void + * @throws BuildException + */ + public function handleInput(InputRequest $request); + +} diff --git a/buildscripts/phing/classes/phing/input/InputRequest.php b/buildscripts/phing/classes/phing/input/InputRequest.php new file mode 100755 index 00000000..1d9e156d --- /dev/null +++ b/buildscripts/phing/classes/phing/input/InputRequest.php @@ -0,0 +1,107 @@ +<?php + +/* + * $Id: f74cb3768bf512aeb0156b8de3e3b65c824cd0dc $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Encapsulates an input request. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @version $Id$ + * @package phing.input + */ +class InputRequest { + + protected $prompt; + protected $input; + protected $defaultValue; + protected $promptChar; + + /** + * @param string $prompt The prompt to show to the user. Must not be null. + */ + public function __construct($prompt) { + if ($prompt === null) { + throw new BuildException("prompt must not be null"); + } + $this->prompt = $prompt; + } + + /** + * Retrieves the prompt text. + */ + public function getPrompt() { + return $this->prompt; + } + + /** + * Sets the user provided input. + */ + public function setInput($input) { + $this->input = $input; + } + + /** + * Is the user input valid? + */ + public function isInputValid() { + return true; + } + + /** + * Retrieves the user input. + */ + public function getInput() { + return $this->input; + } + + /** + * Set the default value to use. + * @param mixed $v + */ + public function setDefaultValue($v) { + $this->defaultValue = $v; + } + + /** + * Return the default value to use. + * @return mixed + */ + public function getDefaultValue() { + return $this->defaultValue; + } + + /** + * Set the default value to use. + * @param string $c + */ + public function setPromptChar($c) { + $this->promptChar = $c; + } + + /** + * Return the default value to use. + * @return string + */ + public function getPromptChar() { + return $this->promptChar; + } +} diff --git a/buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php b/buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php new file mode 100755 index 00000000..24e93b58 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/MultipleChoiceInputRequest.php @@ -0,0 +1,58 @@ +<?php +/* + * $Id: 12fcf735b10cae890d51bce8d3aebb637d9b6928 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/input/InputRequest.php'; + +/** + * Encapsulates an input request. + * + * @author Stefan Bodewig <stefan.bodewig@epost.de> + * @version $Id$ + * @package phing.input + */ +class MultipleChoiceInputRequest extends InputRequest { + + protected $choices = array(); + + /** + * @param string $prompt The prompt to show to the user. Must not be null. + * @param array $choices holds all input values that are allowed. + * Must not be null. + */ + public function __construct($prompt, $choices) { + parent::__construct($prompt); + $this->choices = $choices; + } + + /** + * @return The possible values. + */ + public function getChoices() { + return $this->choices; + } + + /** + * @return true if the input is one of the allowed values. + */ + public function isInputValid() { + return in_array($this->getInput(), $this->choices); // not strict (?) + } +} diff --git a/buildscripts/phing/classes/phing/input/YesNoInputRequest.php b/buildscripts/phing/classes/phing/input/YesNoInputRequest.php new file mode 100755 index 00000000..1a712327 --- /dev/null +++ b/buildscripts/phing/classes/phing/input/YesNoInputRequest.php @@ -0,0 +1,47 @@ +<?php +/* + * $Id: 659526fec1ed2e66d5b9308fba48924ea3dda494 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/input/MultipleChoiceInputRequest.php'; + +/** + * Encapsulates an input request that returns a boolean (yes/no). + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: 659526fec1ed2e66d5b9308fba48924ea3dda494 $ + * @package phing.input + */ +class YesNoInputRequest extends MultipleChoiceInputRequest { + + /** + * @return true if the input is one of the allowed values. + */ + public function isInputValid() { + return StringHelper::isBoolean($this->input); + } + + /** + * Converts input to boolean. + * @return boolean + */ + public function getInput() { + return StringHelper::booleanValue($this->input); + } +} diff --git a/buildscripts/phing/classes/phing/lib/Capsule.php b/buildscripts/phing/classes/phing/lib/Capsule.php new file mode 100755 index 00000000..e6885a6a --- /dev/null +++ b/buildscripts/phing/classes/phing/lib/Capsule.php @@ -0,0 +1,267 @@ +<?php + +/** + * Capsule is a simple "template" engine that essentially provides an isolated context + * for PHP scripts. + * + * There is no special templating language, and therefore no limitations to what + * can be accomplished within templates. The main purpose of Capsule is to separate + * the business logic from display / output logic. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.lib + */ +class Capsule { + + /** + * Look for templates here (if relative path provided). + * @var string + */ + protected $templatePath; + + /** + * Where should output files be written? + * (This is named inconsistently to be compatible w/ Texen.) + * @var string + */ + protected $outputDirectory; + + /** + * The variables that can be used by the templates. + * @var array Hash of variables. + */ + public $vars = array(); + + /** + * Has template been initialized. + */ + protected $initialized = false; + + /** + * Stores the pre-parse() include_path. + * @var string + */ + private $old_include_path; + + function __construct() { + } + + /** + * Clears one or several or all variables. + * @param mixed $which String name of var, or array of names. + * @return void + */ + function clear($which = null) { + if ($which === null) { + $this->vars = array(); + } elseif (is_array($which)) { + foreach($which as $var) { + unset($this->vars[$var]); + } + } else { + unset($this->vars[$which]); + } + } + + /** + * Set the basepath to use for template lookups. + * @param string $v + */ + function setTemplatePath($v) { + $this->templatePath = rtrim($v, DIRECTORY_SEPARATOR.'/'); + } + + /** + * Get the basepath to use for template lookups. + * @return string + */ + function getTemplatePath() { + return $this->templatePath; + } + + /** + * Set a basepath to use for output file creation. + * @param string $v + */ + function setOutputDirectory($v) { + $this->outputDirectory = rtrim($v, DIRECTORY_SEPARATOR.'/'); + } + + /** + * Get basepath to use for output file creation. + * @return string + */ + function getOutputDirectory() { + return $this->outputDirectory; + } + + /** + * Low overhead (no output buffering) method to simply dump template + * to buffer. + * + * @param string $__template + * @return void + * @throws Exception - if template cannot be found + */ + function display($__template) { + + // Prepend "private" variable names with $__ in this function + // to keep namespace conflict potential to a minimum. + + // Alias this class to $generator. + $generator = $this; + + if (isset($this->vars['this'])) { + throw new Exception("Assigning a variable named \$this to a context conflicts with class namespace."); + } + + // extract variables into local namespace + extract($this->vars); + + // prepend template path to include path, + // so that include "path/relative/to/templates"; can be used within templates + $__old_inc_path = ini_get('include_path'); + ini_set('include_path', $this->templatePath . PATH_SEPARATOR . $__old_inc_path); + + @ini_set('track_errors', true); + include $__template; + @ini_restore('track_errors'); + + // restore the include path + ini_set('include_path', $__old_inc_path); + + if (!empty($php_errormsg)) { + throw new Exception("Unable to parse template " . $__template . ": " . $php_errormsg); + } + } + + /** + * Fetches the results of a tempalte parse and either returns + * the string or writes results to a specified output file. + * + * @param string $template The template filename (relative to templatePath or absolute). + * @param string $outputFile If specified, contents of template will also be written to this file. + * @param boolean $append Should output be appended to source file? + * @return string The "parsed" template output. + * @throws Exception - if template not found. + */ + function parse($template, $outputFile = null, $append = false) { + + // main work done right here: + // hopefully this works recursively ... fingers crossed. + ob_start(); + + try { + $this->display($template); + } catch (Exception $e) { + ob_end_flush(); // flush the output on error (so we can see up to what point it parsed everything) + throw $e; + } + + $output = ob_get_contents(); + ob_end_clean(); + + if ($outputFile !== null) { + $outputFile = $this->resolvePath($outputFile, $this->outputDirectory); + + $flags = null; + if ($append) $flags = FILE_APPEND; + + if (!file_put_contents($outputFile, $output, $flags) && $output != "") { + throw new Exception("Unable to write output to " . $outputFile); + } + } + + return $output; + } + + /** + * This returns a "best guess" path for the given file. + * + * @param string $file File name or possibly absolute path. + * @param string $basepath The basepath that should be prepended if $file is not absolute. + * @return string "Best guess" path for this file. + */ + protected function resolvePath($file, $basepath) { + if ( !($file{0} == DIRECTORY_SEPARATOR || $file{0} == '/') + // also account for C:\ style path + && !($file{1} == ':' && ($file{2} == DIRECTORY_SEPARATOR || $file{2} == '/'))) { + if ($basepath != null) { + $file = $basepath . DIRECTORY_SEPARATOR . $file; + } + } + return $file; + } + + /** + * Gets value of specified var or NULL if var has not been put(). + * @param string $name Variable name to retrieve. + * @return mixed + */ + function get($name) { + if (!isset($this->vars[$name])) return null; + return $this->vars[$name]; + } + + /** + * Merges in passed hash to vars array. + * + * Given an array like: + * + * array( 'myvar' => 'Hello', + * 'myvar2' => 'Hello') + * + * Resulting template will have access to $myvar and $myvar2. + * + * @param array $vars + * @param boolean $recursiveMerge Should matching keys be recursively merged? + * @return void + */ + function putAll($vars, $recursiveMerge = false) { + if ($recursiveMerge) { + $this->vars = array_merge_recursive($this->vars, $vars); + } else { + $this->vars = array_merge($this->vars, $vars); + } + } + + /** + * Adds a variable to the context. + * + * Resulting template will have access to ${$name$} variable. + * + * @param string $name + * @param mixed $value + */ + function put($name, $value) { + $this->vars[$name] = $value; + } + + /** + * Put a variable into the context, assigning it by reference. + * This means that if the template modifies the variable, then it + * will also be modified in the context. + * + * @param $name + * @param &$value + */ + function putRef($name, &$value) { + $this->vars[$name] = &$value; + } + + /** + * Makes a copy of the value and puts it into the context. + * This is primarily to force copying (cloning) of objects, rather + * than the default behavior which is to assign them by reference. + * @param string $name + * @param mixed $value + */ + function putCopy($name, $value) { + if (is_object($value)) { + $value = clone $value; + } + $this->vars[$name] = $value; + } + +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/listener/AnsiColorLogger.php b/buildscripts/phing/classes/phing/listener/AnsiColorLogger.php new file mode 100755 index 00000000..fc12c9d2 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/AnsiColorLogger.php @@ -0,0 +1,234 @@ +<?php +/* + * $Id: bea608bad3f8bd5733c7eac6451d15b1c937115c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/listener/DefaultLogger.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Uses ANSI Color Code Sequences to colorize messages + * sent to the console. + * + * If used with the -logfile option, the output file + * will contain all the necessary escape codes to + * display the text in colorized mode when displayed + * in the console using applications like cat, more, + * etc. + * + * This is designed to work on terminals that support ANSI + * color codes. It works on XTerm, ETerm, Mindterm, etc. + * It also works on Win9x (with ANSI.SYS loaded.) + * + * NOTE: + * It doesn't work on WinNT's COMMAND.COM even with + * ANSI.SYS loaded. + * + * The default colors used for differentiating + * the message levels can be changed by editing the + * phing/listener/defaults.properties file. + * + * This file contains 5 key/value pairs: + * AnsiColorLogger.ERROR_COLOR=2;31 + * AnsiColorLogger.WARNING_COLOR=2;35 + * AnsiColorLogger.INFO_COLOR=2;36 + * AnsiColorLogger.VERBOSE_COLOR=2;32 + * AnsiColorLogger.DEBUG_COLOR=2;34 + * + * Another option is to pass a system variable named + * ant.logger.defaults, with value set to the path of + * the file that contains user defined Ansi Color + * Codes, to the <B>java</B> command using -D option. + * + * To change these colors use the following chart: + * + * <B>ANSI COLOR LOGGER CONFIGURATION</B> + * + * Format for AnsiColorLogger.*= + * Attribute;Foreground;Background + * + * Attribute is one of the following: + * 0 -> Reset All Attributes (return to normal mode) + * 1 -> Bright (Usually turns on BOLD) + * 2 -> Dim + * 3 -> Underline + * 5 -> link + * 7 -> Reverse + * 8 -> Hidden + * + * Foreground is one of the following: + * 30 -> Black + * 31 -> Red + * 32 -> Green + * 33 -> Yellow + * 34 -> Blue + * 35 -> Magenta + * 36 -> Cyan + * 37 -> White + * + * Background is one of the following: + * 40 -> Black + * 41 -> Red + * 42 -> Green + * 43 -> Yellow + * 44 -> Blue + * 45 -> Magenta + * 46 -> Cyan + * 47 -> White + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Magesh Umasankar (Ant) + * @package phing.listener + * @version $Id$ + */ +class AnsiColorLogger extends DefaultLogger { + + const ATTR_NORMAL = 0; + const ATTR_BRIGHT = 1; + const ATTR_DIM = 2; + const ATTR_UNDERLINE = 3; + const ATTR_BLINK = 5; + const ATTR_REVERSE = 7; + const ATTR_HIDDEN = 8; + + const FG_BLACK = 30; + const FG_RED = 31; + const FG_GREEN = 32; + const FG_YELLOW = 33; + const FG_BLUE = 34; + const FG_MAGENTA = 35; + const FG_CYAN = 36; + const FG_WHITE = 37; + + const BG_BLACK = 40; + const BG_RED = 41; + const BG_GREEN = 42; + const BG_YELLOW = 44; + const BG_BLUE = 44; + const BG_MAGENTA = 45; + const BG_CYAN = 46; + const BG_WHITE = 47; + + const PREFIX = "\x1b["; + const SUFFIX = "m"; + const SEPARATOR = ';'; + const END_COLOR = "\x1b[m"; // self::PREFIX . self::SUFFIX; + + private $errColor; + private $warnColor; + private $infoColor; + private $verboseColor; + private $debugColor; + + private $colorsSet = false; + + /** + * Construct new AnsiColorLogger + * Perform initializations that cannot be done in var declarations. + */ + public function __construct() { + parent::__construct(); + $this->errColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_RED . self::SUFFIX; + $this->warnColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_MAGENTA . self::SUFFIX; + $this->infoColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_CYAN . self::SUFFIX; + $this->verboseColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_GREEN . self::SUFFIX; + $this->debugColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_BLUE . self::SUFFIX; + } + + /** + * Set the colors to use from a property file specified by the + * special ant property ant.logger.defaults + */ + private final function setColors() { + + $userColorFile = Phing::getProperty("phing.logger.defaults"); + $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties")); + + $in = null; + + try { + $prop = new Properties(); + + if ($userColorFile !== null) { + $prop->load($userColorFile); + } else { + $prop->load($systemColorFile); + } + + $err = $prop->getProperty("AnsiColorLogger.ERROR_COLOR"); + $warn = $prop->getProperty("AnsiColorLogger.WARNING_COLOR"); + $info = $prop->getProperty("AnsiColorLogger.INFO_COLOR"); + $verbose = $prop->getProperty("AnsiColorLogger.VERBOSE_COLOR"); + $debug = $prop->getProperty("AnsiColorLogger.DEBUG_COLOR"); + if ($err !== null) { + $this->errColor = self::PREFIX . $err . self::SUFFIX; + } + if ($warn !== null) { + $this->warnColor = self::PREFIX . $warn . self::SUFFIX; + } + if ($info !== null) { + $this->infoColor = self::PREFIX . $info . self::SUFFIX; + } + if ($verbose !== null) { + $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX; + } + if ($debug !== null) { + $this->debugColor = self::PREFIX . $debug . self::SUFFIX; + } + } catch (IOException $ioe) { + //Ignore exception - we will use the defaults. + } + } + + /** + * @see DefaultLogger#printMessage + * @param string $message + * @param OutputStream $stream + * @param int $priority + */ + protected final function printMessage($message, OutputStream $stream, $priority) { + if ($message !== null) { + + if (!$this->colorsSet) { + $this->setColors(); + $this->colorsSet = true; + } + + switch ($priority) { + case Project::MSG_ERR: + $message = $this->errColor . $message . self::END_COLOR; + break; + case Project::MSG_WARN: + $message = $this->warnColor . $message . self::END_COLOR; + break; + case Project::MSG_INFO: + $message = $this->infoColor . $message . self::END_COLOR; + break; + case Project::MSG_VERBOSE: + $message = $this->verboseColor . $message . self::END_COLOR; + break; + case Project::MSG_DEBUG: + $message = $this->debugColor . $message . self::END_COLOR; + break; + } + + $stream->write($message . PHP_EOL); + } + } +} diff --git a/buildscripts/phing/classes/phing/listener/DefaultLogger.php b/buildscripts/phing/classes/phing/listener/DefaultLogger.php new file mode 100755 index 00000000..31051a75 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/DefaultLogger.php @@ -0,0 +1,279 @@ +<?php +/* + * $Id: e7f902228f55a3be17b42eed785137cb97e7a29e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/listener/StreamRequiredBuildLogger.php'; +include_once 'phing/BuildEvent.php'; + +/** + * Writes a build event to the console. + * + * Currently, it only writes which targets are being executed, and + * any messages that get logged. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @see BuildEvent + * @package phing.listener + */ +class DefaultLogger implements StreamRequiredBuildLogger { + + /** + * Size of the left column in output. The default char width is 12. + * @var int + */ + const LEFT_COLUMN_SIZE = 12; + + /** + * The message output level that should be used. The default is + * <code>Project::MSG_VERBOSE</code>. + * @var int + */ + protected $msgOutputLevel = Project::MSG_ERR; + + /** + * Time that the build started + * @var int + */ + protected $startTime; + + /** + * @var OutputStream Stream to use for standard output. + */ + protected $out; + + /** + * @var OutputStream Stream to use for error output. + */ + protected $err; + + /** + * Construct a new default logger. + */ + public function __construct() { + + } + + /** + * Set the msgOutputLevel this logger is to respond to. + * + * Only messages with a message level lower than or equal to the given + * level are output to the log. + * + * <p> Constants for the message levels are in Project.php. The order of + * the levels, from least to most verbose, is: + * + * <ul> + * <li>Project::MSG_ERR</li> + * <li>Project::MSG_WARN</li> + * <li>Project::MSG_INFO</li> + * <li>Project::MSG_VERBOSE</li> + * <li>Project::MSG_DEBUG</li> + * </ul> + * + * The default message level for DefaultLogger is Project::MSG_ERR. + * + * @param int $level The logging level for the logger. + * @see BuildLogger#setMessageOutputLevel() + */ + public function setMessageOutputLevel($level) { + $this->msgOutputLevel = (int) $level; + } + + /** + * Sets the output stream. + * @param OutputStream $output + * @see BuildLogger#setOutputStream() + */ + public function setOutputStream(OutputStream $output) { + $this->out = $output; + } + + /** + * Sets the error stream. + * @param OutputStream $err + * @see BuildLogger#setErrorStream() + */ + public function setErrorStream(OutputStream $err) { + $this->err = $err; + } + + /** + * Sets the start-time when the build started. Used for calculating + * the build-time. + * + * @param object The BuildEvent + * @access public + */ + public function buildStarted(BuildEvent $event) { + $this->startTime = Phing::currentTimeMillis(); + if ($this->msgOutputLevel >= Project::MSG_INFO) { + $this->printMessage("Buildfile: ".$event->getProject()->getProperty("phing.file"), $this->out, Project::MSG_INFO); + } + } + + /** + * Prints whether the build succeeded or failed, and any errors that + * occured during the build. Also outputs the total build-time. + * + * @param object The BuildEvent + * @see BuildEvent::getException() + */ + public function buildFinished(BuildEvent $event) { + $error = $event->getException(); + if ($error === null) { + $msg = PHP_EOL . $this->getBuildSuccessfulMessage() . PHP_EOL; + } else { + $msg = PHP_EOL . $this->getBuildFailedMessage() . PHP_EOL; + if (Project::MSG_VERBOSE <= $this->msgOutputLevel || !($error instanceof BuildException)) { + $msg .= $error->__toString().PHP_EOL; + } else { + $msg .= $error->getMessage(); + } + } + $msg .= PHP_EOL . "Total time: " .self::formatTime(Phing::currentTimeMillis() - $this->startTime) . PHP_EOL; + + if ($error === null) { + $this->printMessage($msg, $this->out, Project::MSG_VERBOSE); + } else { + $this->printMessage($msg, $this->err, Project::MSG_ERR); + } + } + + /** + * Get the message to return when a build failed. + * @return string The classic "BUILD FAILED" + */ + protected function getBuildFailedMessage() { + return "BUILD FAILED"; + } + + /** + * Get the message to return when a build succeeded. + * @return string The classic "BUILD FINISHED" + */ + protected function getBuildSuccessfulMessage() { + return "BUILD FINISHED"; + } + + /** + * Prints the current target name + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getTarget() + */ + public function targetStarted(BuildEvent $event) { + if (Project::MSG_INFO <= $this->msgOutputLevel) { + $showLongTargets = $event->getProject()->getProperty("phing.showlongtargets"); + $msg = PHP_EOL . $event->getProject()->getName() . ' > ' . $event->getTarget()->getName() . ($showLongTargets ? ' [' . $event->getTarget()->getDescription() . ']' : '') . ':' . PHP_EOL; + $this->printMessage($msg, $this->out, $event->getPriority()); + } + } + + /** + * Fired when a target has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param object The BuildEvent + * @see BuildEvent::getException() + */ + public function targetFinished(BuildEvent $event) {} + + /** + * Fired when a task is started. We don't need specific action on this + * event. So the methods are empty. + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getTask() + */ + public function taskStarted(BuildEvent $event) {} + + /** + * Fired when a task has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getException() + */ + public function taskFinished(BuildEvent $event) {} + + /** + * Print a message to the stdout. + * + * @param object The BuildEvent + * @access public + * @see BuildEvent::getMessage() + */ + public function messageLogged(BuildEvent $event) { + $priority = $event->getPriority(); + if ($priority <= $this->msgOutputLevel) { + $msg = ""; + if ($event->getTask() !== null) { + $name = $event->getTask(); + $name = $name->getTaskName(); + $msg = str_pad("[$name] ", self::LEFT_COLUMN_SIZE, " ", STR_PAD_LEFT); + } + + $msg .= $event->getMessage(); + + if ($priority != Project::MSG_ERR) { + $this->printMessage($msg, $this->out, $priority); + } else { + $this->printMessage($msg, $this->err, $priority); + } + } + } + + /** + * Formats a time micro integer to human readable format. + * + * @param integer The time stamp + * @access private + */ + public static function formatTime($micros) { + $seconds = $micros; + $minutes = $seconds / 60; + if ($minutes > 1) { + return sprintf("%1.0f minute%s %0.2f second%s", + $minutes, ($minutes === 1 ? " " : "s "), + $seconds - floor($seconds/60) * 60, ($seconds%60 === 1 ? "" : "s")); + } else { + return sprintf("%0.4f second%s", $seconds, ($seconds%60 === 1 ? "" : "s")); + } + } + + /** + * Prints a message to console. + * + * @param string $message The message to print. + * Should not be <code>null</code>. + * @param resource $stream The stream to use for message printing. + * @param int $priority The priority of the message. + * (Ignored in this implementation.) + * @return void + */ + protected function printMessage($message, OutputStream $stream, $priority) { + $stream->write($message . PHP_EOL); + } +} diff --git a/buildscripts/phing/classes/phing/listener/HtmlColorLogger.php b/buildscripts/phing/classes/phing/listener/HtmlColorLogger.php new file mode 100755 index 00000000..6514f8d1 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/HtmlColorLogger.php @@ -0,0 +1,175 @@ +<?php +/* + * $Id: 4b57f4d435b61b6501688394f1ff8534d4b7e93f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/listener/DefaultLogger.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Uses CSS class that must be defined in the HTML page + * where the Phing output is displayed. + * + * If used with the -logfile option, the output + * will contain the text wrapped in html <span> elements + * with those css classes. + * + * The default classes used for differentiating + * the message levels can be changed by editing the + * phing/listener/defaults.properties file. + * + * This file can contain 5 key/value pairs: + * HtmlColorLogger.ERROR_CLASS=_your_css_class_name_ + * HtmlColorLogger.WARNING_CLASS=_your_css_class_name_ + * HtmlColorLogger.INFO_CLASS=_your_css_class_name_ + * HtmlColorLogger.VERBOSE_CLASS=_your_css_class_name_ + * HtmlColorLogger.DEBUG_CLASS=_your_css_class_name_ + * + * This stems from the Ansi Color Logger done by Hans Lellelid: + * + * @author Anton Stöckl <anton@stoeckl.de> (Phing HTML Color Logger) + * @author Hans Lellelid <hans@xmpl.org> (Phing Ansi Color Logger) + * @author Magesh Umasankar (Ant) + * @package phing.listener + * @version $Id: 4b57f4d435b61b6501688394f1ff8534d4b7e93f $ + */ +class HtmlColorLogger extends DefaultLogger { + + const CLASS_ERR = 'phing_err'; + const CLASS_VERBOSE = 'phing_verbose'; + const CLASS_DEBUG = 'phing_debug'; + const CLASS_WARN = 'phing_warn'; + const CLASS_INFO = 'phing_info'; + + const PREFIX = '<span class="'; + const SUFFIX = '">'; + const END_COLOR = '</span>'; + + private $errColor; + private $warnColor; + private $infoColor; + private $verboseColor; + private $debugColor; + + private $colorsSet = false; + + /** + * Construct new HtmlColorLogger + * Perform initializations that cannot be done in var declarations. + */ + public function __construct() { + parent::__construct(); + $this->errColor = self::PREFIX . self::CLASS_ERR . self::SUFFIX; + $this->warnColor = self::PREFIX . self::CLASS_WARN . self::SUFFIX; + $this->infoColor = self::PREFIX . self::CLASS_INFO . self::SUFFIX; + $this->verboseColor = self::PREFIX . self::CLASS_VERBOSE . self::SUFFIX; + $this->debugColor = self::PREFIX . self::CLASS_DEBUG . self::SUFFIX; + } + + /** + * Set the colors to use from a property file specified in the + * special phing property file "phing/listener/defaults.properties". + */ + private final function setColors() { + + $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties")); + + try { + $prop = new Properties(); + + $prop->load($systemColorFile); + + $err = $prop->getProperty("HtmlColorLogger.ERROR_CLASS"); + $warn = $prop->getProperty("HtmlColorLogger.WARNING_CLASS"); + $info = $prop->getProperty("HtmlColorLogger.INFO_CLASS"); + $verbose = $prop->getProperty("HtmlColorLogger.VERBOSE_CLASS"); + $debug = $prop->getProperty("HtmlColorLogger.DEBUG_CLASS"); + if ($err !== null) { + $this->errColor = self::PREFIX . $err . self::SUFFIX; + } + if ($warn !== null) { + $this->warnColor = self::PREFIX . $warn . self::SUFFIX; + } + if ($info !== null) { + $this->infoColor = self::PREFIX . $info . self::SUFFIX; + } + if ($verbose !== null) { + $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX; + } + if ($debug !== null) { + $this->debugColor = self::PREFIX . $debug . self::SUFFIX; + } + } catch (IOException $ioe) { + //Ignore exception - we will use the defaults. + } + } + + /** + * @see DefaultLogger#printMessage + * @param string $message + * @param OutputStream $stream + * @param int $priority + */ + protected final function printMessage($message, OutputStream $stream, $priority) { + if ($message !== null) { + + if (!$this->colorsSet) { + $this->setColors(); + $this->colorsSet = true; + } + + $search = array('<', '>'); + $replace = array('<', '>'); + $message = str_replace($search, $replace, $message); + + $search = array("\t", "\n", "\r"); + $replace = array(' ', '<br>', ''); + $message = str_replace($search, $replace, $message); + + if (preg_match('@^( +)([^ ].+)@', $message, $matches)) { + $len = strlen($matches[1]); + $space = ' '; + for ($i = 1; $i < $len; $i++) { + $space .= ' '; + } + $message = $space . $matches[2]; + } + + switch ($priority) { + case Project::MSG_ERR: + $message = $this->errColor . $message . self::END_COLOR; + break; + case Project::MSG_WARN: + $message = $this->warnColor . $message . self::END_COLOR; + break; + case Project::MSG_INFO: + $message = $this->infoColor . $message . self::END_COLOR; + break; + case Project::MSG_VERBOSE: + $message = $this->verboseColor . $message . self::END_COLOR; + break; + case Project::MSG_DEBUG: + $message = $this->debugColor . $message . self::END_COLOR; + break; + } + + $stream->write($message . '<br/>'); + } + } +} diff --git a/buildscripts/phing/classes/phing/listener/MailLogger.php b/buildscripts/phing/classes/phing/listener/MailLogger.php new file mode 100755 index 00000000..344f3de4 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/MailLogger.php @@ -0,0 +1,105 @@ +<?php +/* + * $Id: bea608bad3f8bd5733c7eac6451d15b1c937115c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/listener/DefaultLogger.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Uses PEAR Mail package to send the build log to one or + * more recipients. + * + * @author Michiel Rook <mrook@php.net> + * @package phing.listener + * @version $Id$ + */ +class MailLogger extends DefaultLogger +{ + private $_mailMessage = ""; + + private $_from = "phing@phing.info"; + private $_subject = "Phing build result"; + private $_tolist = null; + + /** + * Construct new MailLogger + */ + public function __construct() { + parent::__construct(); + + @require_once 'Mail.php'; + + if (!class_exists('Mail')) { + throw new BuildException('Need the PEAR Mail package to send logs'); + } + + $from = Phing::getDefinedProperty('phing.log.mail.from'); + $subject = Phing::getDefinedProperty('phing.log.mail.subject'); + $tolist = Phing::getDefinedProperty('phing.log.mail.recipients'); + + if (!empty($from)) { + $this->_from = $from; + } + + if (!empty($subject)) { + $this->_subject = $subject; + } + + if (!empty($tolist)) { + $this->_tolist = $tolist; + } + } + + /** + * @see DefaultLogger#printMessage + * @param string $message + * @param OutputStream $stream + * @param int $priority + */ + protected final function printMessage($message, OutputStream $stream, $priority) + { + if ($message !== null) { + $this->_mailMessage .= $message . "\n"; + } + } + + /** + * Sends the mail + * + * @see DefaultLogger#buildFinished + * @param BuildEvent $event + */ + public function buildFinished(BuildEvent $event) + { + parent::buildFinished($event); + + if (empty($this->_tolist)) { + return; + } + + $hdrs = array( + 'From' => $this->_from, + 'Subject' => $this->_subject . (empty($event) ? " (build succesful)" : " (build failed)") + ); + + $mail = Mail::factory('mail'); + $mail->send($this->_tolist, $hdrs, $this->_mailMessage); + } +} diff --git a/buildscripts/phing/classes/phing/listener/NoBannerLogger.php b/buildscripts/phing/classes/phing/listener/NoBannerLogger.php new file mode 100755 index 00000000..887f6678 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/NoBannerLogger.php @@ -0,0 +1,59 @@ +<?php +/* + * $Id: d74783e6edb73c6f11fbb93701f0bb6e7ccf06b1 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/listener/DefaultLogger.php'; + +/** + * Extends DefaultLogger to strip out empty targets. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.listener + */ +class NoBannerLogger extends DefaultLogger { + + private $targetName = null; + + function targetStarted(BuildEvent $event) { + $target = $event->getTarget(); + $this->targetName = $target->getName(); + } + + function targetFinished(BuildEvent $event) { + $this->targetName = null; + } + + function messageLogged(BuildEvent $event) { + + if ($event->getPriority() > $this->msgOutputLevel || null === $event->getMessage() || trim($event->getMessage() === "")) { + return; + } + + if ($this->targetName !== null) { + $msg = PHP_EOL . $event->getProject()->getName() . ' > ' . $this->targetName . ':' . PHP_EOL; + $this->printMessage($msg, $this->out, $event->getPriority()); + $this->targetName = null; + } + + parent::messageLogged($event); + } +} diff --git a/buildscripts/phing/classes/phing/listener/PearLogListener.php b/buildscripts/phing/classes/phing/listener/PearLogListener.php new file mode 100755 index 00000000..76efdbbb --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/PearLogListener.php @@ -0,0 +1,197 @@ +<?php +/* + * $Id: d2d1a761c30c120a07e419622bcbcb9b5e067c24 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/BuildListener.php'; + +/** + * Writes build messages to PEAR Log. + * + * By default it will log to file in current directory w/ name 'phing.log'. You can customize + * this behavior by setting properties: + * - pear.log.type + * - pear.log.name + * - pear.log.ident (note that this class changes ident to project name) + * - pear.log.conf (note that array values are currently unsupported in Phing property files) + * + * <code> + * phing -f build.xml -logger phing.listener.PearLogger -Dpear.log.type=file -Dpear.log.name=/path/to/log.log + * </code> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see BuildEvent + * @package phing.listener + */ +class PearLogListener implements BuildListener { + + /** + * Size of the left column in output. The default char width is 12. + * @var int + */ + const LEFT_COLUMN_SIZE = 12; + + /** + * Time that the build started + * @var int + */ + protected $startTime; + + /** + * Maps Phing Project::MSG_* constants to PEAR_LOG_* constants. + * @var array + */ + protected static $levelMap = array( Project::MSG_DEBUG => PEAR_LOG_DEBUG, + Project::MSG_INFO => PEAR_LOG_INFO, + Project::MSG_VERBOSE => PEAR_LOG_NOTICE, + Project::MSG_WARN => PEAR_LOG_WARNING, + Project::MSG_ERR => PEAR_LOG_ERR + ); + /** + * Whether logging has been configured. + * @var boolean + */ + protected $logConfigured = false; + + /** + * @var Log PEAR Log object. + */ + protected $logger; + + /** + * Configure the logger. + */ + protected function configureLogging() { + + $type = Phing::getDefinedProperty('pear.log.type'); + $name = Phing::getDefinedProperty('pear.log.name'); + $ident = Phing::getDefinedProperty('pear.log.ident'); + $conf = Phing::getDefinedProperty('pear.log.conf'); + + if ($type === null) $type = 'file'; + if ($name === null) $name = 'phing.log'; + if ($ident === null) $ident = 'phing'; + if ($conf === null) $conf = array(); + + include_once 'Log.php'; + if (!class_exists('Log')) { + throw new BuildException("Cannot find PEAR Log class for use by PearLogger."); + } + + $this->logger = Log::singleton($type, $name, $ident, $conf, self::$levelMap[$this->msgOutputLevel]); + } + + /** + * Get the configured PEAR logger to use. + * This method just ensures that logging has been configured and returns the configured logger. + * @return Log + */ + protected function logger() { + if (!$this->logConfigured) { + $this->configureLogging(); + } + return $this->logger; + } + + /** + * Sets the start-time when the build started. Used for calculating + * the build-time. + * + * @param BuildEvent The BuildEvent + */ + public function buildStarted(BuildEvent $event) { + $this->startTime = Phing::currentTimeMillis(); + $this->logger()->setIdent($event->getProject()->getName()); + $this->logger()->info("Starting build with buildfile: ". $event->getProject()->getProperty("phing.file")); + } + + /** + * Logs whether the build succeeded or failed, and any errors that + * occured during the build. Also outputs the total build-time. + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getException() + */ + public function buildFinished(BuildEvent $event) { + $error = $event->getException(); + if ($error === null) { + $msg = "Finished successful build."; + } else { + $msg = "Build failed. [reason: " . $error->getMessage() ."]"; + } + $this->logger()->log($msg . " Total time: " . DefaultLogger::formatTime(Phing::currentTimeMillis() - $this->startTime)); + } + + /** + * Logs the current target name + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getTarget() + */ + public function targetStarted(BuildEvent $event) {} + + /** + * Fired when a target has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param BuildEvent The BuildEvent + * @access public + * @see BuildEvent::getException() + */ + public function targetFinished(BuildEvent $event) {} + + /** + * Fired when a task is started. We don't need specific action on this + * event. So the methods are empty. + * + * @param BuildEvent The BuildEvent + * @access public + * @see BuildEvent::getTask() + */ + public function taskStarted(BuildEvent $event) {} + + /** + * Fired when a task has finished. We don't need specific action on this + * event. So the methods are empty. + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getException() + */ + public function taskFinished(BuildEvent $event) {} + + /** + * Logs a message to the configured PEAR logger. + * + * @param BuildEvent The BuildEvent + * @see BuildEvent::getMessage() + */ + public function messageLogged(BuildEvent $event) { + if ($event->getPriority() <= $this->msgOutputLevel) { + $msg = ""; + if ($event->getTask() !== null) { + $name = $event->getTask(); + $name = $name->getTaskName(); + $msg = str_pad("[$name] ", self::LEFT_COLUMN_SIZE, " ", STR_PAD_LEFT); + } + $msg .= $event->getMessage(); + $this->logger()->log($msg, self::$levelMap[$event->getPriority()]); + } + } +} diff --git a/buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php b/buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php new file mode 100755 index 00000000..c463a59d --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/StreamRequiredBuildLogger.php @@ -0,0 +1,39 @@ +<?php +/* + * $Id: e0daef8de8e5be892b2eaaa7a42fa5d04ad540b5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/BuildLogger.php'; + +/** + * Interface for build loggers that require that out/err streams be set in order to function. + * + * This is just an empty sub-interface to BuildLogger, but is used by Phing to throw
+ * graceful errors when classes like phing.listener.DefaultLogger are being used as
+ * -listener. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see BuildEvent + * @see Project::addBuildListener() + * @package phing + */ +interface StreamRequiredBuildLogger extends BuildLogger { + +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/listener/XmlLogger.php b/buildscripts/phing/classes/phing/listener/XmlLogger.php new file mode 100755 index 00000000..25ff0ba2 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/XmlLogger.php @@ -0,0 +1,354 @@ +<?php +/** + * $Id: aaf7d77e9952319b9598d786c556be005d34c188 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/BuildLogger.php'; +require_once 'phing/listener/DefaultLogger.php'; +require_once 'phing/system/util/Timer.php'; + +/** + * Generates a file in the current directory with + * an XML description of what happened during a build. + * The default filename is "log.xml", but this can be overridden + * with the property <code>XmlLogger.file</code>. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: aaf7d77e9952319b9598d786c556be005d34c188 $ + * @package phing.listener + */ +class XmlLogger implements BuildLogger { + + /** XML element name for a build. */ + const BUILD_TAG = "build"; + + /** XML element name for a target. */ + const TARGET_TAG = "target"; + + /** XML element name for a task. */ + const TASK_TAG = "task"; + + /** XML element name for a message. */ + const MESSAGE_TAG = "message"; + + /** XML attribute name for a name. */ + const NAME_ATTR = "name"; + + /** XML attribute name for a time. */ + const TIME_ATTR = "time"; + + /** XML attribute name for a message priority. */ + const PRIORITY_ATTR = "priority"; + + /** XML attribute name for a file location. */ + const LOCATION_ATTR = "location"; + + /** XML attribute name for an error description. */ + const ERROR_ATTR = "error"; + + /** XML element name for a stack trace. */ + const STACKTRACE_TAG = "stacktrace"; + + /** + * @var DOMDocument The XML document created by this logger. + */ + private $doc; + + /** + * @var int Start time for entire build. + */ + private $buildTimerStart = 0; + + /** + * @var DOMElement Top-level (root) build element + */ + private $buildElement; + + /** + * @var array DOMElement[] The parent of the element being processed. + */ + private $elementStack = array(); + + /** + * @var array int[] Array of millisecond times for the various elements being processed. + */ + private $timesStack = array(); + + /** + * @var int + */ + private $msgOutputLevel = Project::MSG_DEBUG; + + /** + * @var OutputStream Stream to use for standard output. + */ + private $out; + + /** + * @var OutputStream Stream to use for error output. + */ + private $err; + + /** + * @var string Name of filename to create. + */ + private $outFilename; + + /** + * Constructs a new BuildListener that logs build events to an XML file. + */ + public function __construct() { + $this->doc = new DOMDocument("1.0", "UTF-8"); + $this->doc->formatOutput = true; + } + + /** + * Fired when the build starts, this builds the top-level element for the + * document and remembers the time of the start of the build. + * + * @param BuildEvent Ignored. + */ + function buildStarted(BuildEvent $event) { + $this->buildTimerStart = Phing::currentTimeMillis(); + $this->buildElement = $this->doc->createElement(XmlLogger::BUILD_TAG); + array_push($this->elementStack, $this->buildElement); + array_push($this->timesStack, $this->buildTimerStart); + } + + /** + * Fired when the build finishes, this adds the time taken and any + * error stacktrace to the build element and writes the document to disk. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be <code>null</code>. + */ + public function buildFinished(BuildEvent $event) { + + $elapsedTime = Phing::currentTimeMillis() - $this->buildTimerStart; + + $this->buildElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime)); + + if ($event->getException() != null) { + $this->buildElement->setAttribute(XmlLogger::ERROR_ATTR, $event->getException()->getMessage()); + $errText = $this->doc->createCDATASection($event->getException()->getTraceAsString()); + $stacktrace = $this->doc->createElement(XmlLogger::STACKTRACE_TAG); + $stacktrace->appendChild($errText); + $this->buildElement->appendChild($stacktrace); + } + + $this->doc->appendChild($this->buildElement); + + $outFilename = $event->getProject()->getProperty("XmlLogger.file"); + if ($outFilename == null) { + $outFilename = "log.xml"; + } + + try { + $stream = $this->out; + if ($stream === null) { + $stream = new FileOutputStream($outFilename); + } + + // Yes, we could just stream->write() but this will eventually be the better + // way to do this (when we need to worry about charset conversions. + $writer = new OutputStreamWriter($stream); + $writer->write($this->doc->saveXML()); + $writer->close(); + } catch (IOException $exc) { + try { + $stream->close(); // in case there is a stream open still ... + } catch (Exception $x) {} + throw new BuildException("Unable to write log file.", $exc); + } + + // cleanup:remove the buildElement + $this->buildElement = null; + + array_pop($this->elementStack); + array_pop($this->timesStack); + } + + + /** + * Fired when a target starts building, remembers the current time and the name of the target. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be <code>null</code>. + */ + public function targetStarted(BuildEvent $event) { + $target = $event->getTarget(); + + $targetElement = $this->doc->createElement(XmlLogger::TARGET_TAG); + $targetElement->setAttribute(XmlLogger::NAME_ATTR, $target->getName()); + + array_push($this->timesStack, Phing::currentTimeMillis()); + array_push($this->elementStack, $targetElement); + } + + /** + * Fired when a target finishes building, this adds the time taken + * to the appropriate target element in the log. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be <code>null</code>. + */ + public function targetFinished(BuildEvent $event) { + $targetTimerStart = array_pop($this->timesStack); + $targetElement = array_pop($this->elementStack); + + $elapsedTime = Phing::currentTimeMillis() - $targetTimerStart; + $targetElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime)); + + $parentElement = $this->elementStack[ count($this->elementStack) - 1 ]; + $parentElement->appendChild($targetElement); + } + + /** + * Fired when a task starts building, remembers the current time and the name of the task. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be <code>null</code>. + */ + public function taskStarted(BuildEvent $event) { + $task = $event->getTask(); + + $taskElement = $this->doc->createElement(XmlLogger::TASK_TAG); + $taskElement->setAttribute(XmlLogger::NAME_ATTR, $task->getTaskName()); + $taskElement->setAttribute(XmlLogger::LOCATION_ATTR, $task->getLocation()->toString()); + + array_push($this->timesStack, Phing::currentTimeMillis()); + array_push($this->elementStack, $taskElement); + } + + /** + * Fired when a task finishes building, this adds the time taken + * to the appropriate task element in the log. + * + * @param BuildEvent $event An event with any relevant extra information. + * Will not be <code>null</code>. + */ + public function taskFinished(BuildEvent $event) { + $taskTimerStart = array_pop($this->timesStack); + $taskElement = array_pop($this->elementStack); + + $elapsedTime = Phing::currentTimeMillis() - $taskTimerStart; + $taskElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime)); + + $parentElement = $this->elementStack[ count($this->elementStack) - 1 ]; + $parentElement->appendChild($taskElement); + } + + /** + * Fired when a message is logged, this adds a message element to the + * most appropriate parent element (task, target or build) and records + * the priority and text of the message. + * + * @param BuildEvent An event with any relevant extra information. + * Will not be <code>null</code>. + */ + public function messageLogged(BuildEvent $event) + { + $priority = $event->getPriority(); + + if ($priority > $this->msgOutputLevel) { + return; + } + + $messageElement = $this->doc->createElement(XmlLogger::MESSAGE_TAG); + + switch ($priority) { + case Project::MSG_ERR: + $name = "error"; + break; + case Project::MSG_WARN: + $name = "warn"; + break; + case Project::MSG_INFO: + $name = "info"; + break; + default: + $name = "debug"; + break; + } + + $messageElement->setAttribute(XmlLogger::PRIORITY_ATTR, $name); + + if (function_exists('mb_convert_encoding')) + { + $messageConverted = mb_convert_encoding($event->getMessage(), 'UTF-8'); + } + else + { + $messageConverted = utf8_encode($event->getMessage()); + } + + $messageText = $this->doc->createCDATASection($messageConverted); + + $messageElement->appendChild($messageText); + + if (!empty($this->elementStack)) { + $this->elementStack[count($this->elementStack)-1]->appendChild($messageElement); + } + } + + /** + * Set the msgOutputLevel this logger is to respond to. + * + * Only messages with a message level lower than or equal to the given + * level are output to the log. + * + * <p> Constants for the message levels are in Project.php. The order of + * the levels, from least to most verbose, is: + * + * <ul> + * <li>Project::MSG_ERR</li> + * <li>Project::MSG_WARN</li> + * <li>Project::MSG_INFO</li> + * <li>Project::MSG_VERBOSE</li> + * <li>Project::MSG_DEBUG</li> + * </ul> + * + * The default message level for DefaultLogger is Project::MSG_ERR. + * + * @param int $level The logging level for the logger. + * @see BuildLogger#setMessageOutputLevel() + */ + public function setMessageOutputLevel($level) { + $this->msgOutputLevel = (int) $level; + } + + /** + * Sets the output stream. + * @param OutputStream $output + * @see BuildLogger#setOutputStream() + */ + public function setOutputStream(OutputStream $output) { + $this->out = $output; + } + + /** + * Sets the error stream. + * @param OutputStream $err + * @see BuildLogger#setErrorStream() + */ + public function setErrorStream(OutputStream $err) { + $this->err = $err; + } + +} diff --git a/buildscripts/phing/classes/phing/listener/defaults.properties b/buildscripts/phing/classes/phing/listener/defaults.properties new file mode 100644 index 00000000..4a4dec68 --- /dev/null +++ b/buildscripts/phing/classes/phing/listener/defaults.properties @@ -0,0 +1,43 @@ +#################################################### +# +# ANSI COLOR LOGGER CONFIGURATION +# +# Format for AnsiColorLogger.*= +# Attribute;Foreground;Background +# +# Attribute is one of the following: +# 0 -> Reset All Attributes (return to normal mode) +# 1 -> Bright (Usually turns on BOLD) +# 2 -> Dim +# 3 -> Underline +# 5 -> link +# 7 -> Reverse +# 8 -> Hidden +# +# Foreground is one of the following: +# 30 -> Black +# 31 -> Red +# 32 -> Green +# 33 -> Yellow +# 34 -> Blue +# 35 -> Magenta +# 36 -> Cyan +# 37 -> White +# +# Background is one of the following: +# 40 -> Black +# 41 -> Red +# 42 -> Green +# 43 -> Yellow +# 44 -> Blue +# 45 -> Magenta +# 46 -> Cyan +# 47 -> White +# +#################################################### + +AnsiColorLogger.ERROR_COLOR=01;31 +AnsiColorLogger.WARNING_COLOR=01;35 +AnsiColorLogger.INFO_COLOR=00;36 +AnsiColorLogger.VERBOSE_COLOR=00;32 +AnsiColorLogger.DEBUG_COLOR=01;34 diff --git a/buildscripts/phing/classes/phing/mappers/FileNameMapper.php b/buildscripts/phing/classes/phing/mappers/FileNameMapper.php new file mode 100755 index 00000000..f4306b4c --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/FileNameMapper.php @@ -0,0 +1,59 @@ +<?php +/* + * $Id: 24c9367363b11f9ab97509532739bf2353023034 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Interface for filename mapper classes. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.mappers + */ +interface FileNameMapper { + + /** + * The mapper implementation. + * + * @param mixed $sourceFileName The data the mapper works on. + * @return array The data after the mapper has been applied; must be in array format (for some reason). + */ + public function main($sourceFileName); + + /** + * Accessor. Sets the to property. The actual implementation + * depends on the child class. + * + * @param string $to To what this mapper should convert the from string + * @return void + */ + public function setTo($to); + + /** + * Accessor. Sets the from property. What this mapper should + * recognize. The actual implementation is dependent upon the + * child class + * + * @param string $from On what this mapper should work + * @return void + */ + public function setFrom($from); + +} diff --git a/buildscripts/phing/classes/phing/mappers/FlattenMapper.php b/buildscripts/phing/classes/phing/mappers/FlattenMapper.php new file mode 100755 index 00000000..55ed4113 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/FlattenMapper.php @@ -0,0 +1,55 @@ +<?php +/* + * $Id: c18f079545fa2c53e9a129ec1dcf32447b597c09 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/mappers/FileNameMapper.php'; + +/** + * Removes any directory information from the passed path. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @version $Id$ + * @package phing.mappers + */ +class FlattenMapper implements FileNameMapper { + + /** + * The mapper implementation. Returns string with source filename + * but without leading directory information + * + * @param string $sourceFileName The data the mapper works on + * @return array The data after the mapper has been applied + */ + function main($sourceFileName) { + $f = new PhingFile($sourceFileName); + return array($f->getName()); + } + + /** + * Ignored here. + */ + function setTo($to) {} + + /** + * Ignored here. + */ + function setFrom($from) {} + +} diff --git a/buildscripts/phing/classes/phing/mappers/GlobMapper.php b/buildscripts/phing/classes/phing/mappers/GlobMapper.php new file mode 100755 index 00000000..79df94d6 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/GlobMapper.php @@ -0,0 +1,113 @@ +<?php +/* + * $Id: 464f98975210eee84faed74ef4a53c83d23aa079 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/mappers/FileNameMapper.php'; + +/** + * description here + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.mappers + */ +class GlobMapper implements FileNameMapper { + + /** + * Part of "from" pattern before the *. + */ + private $fromPrefix = null; + + /** + * Part of "from" pattern after the *. + */ + private $fromPostfix = null; + + /** + * Length of the prefix ("from" pattern). + */ + private $prefixLength; + + /** + * Length of the postfix ("from" pattern). + */ + private $postfixLength; + + /** + * Part of "to" pattern before the *. + */ + private $toPrefix = null; + + /** + * Part of "to" pattern after the *. + */ + private $toPostfix = null; + + + function main($_sourceFileName) { + if (($this->fromPrefix === null) + || !StringHelper::startsWith($this->fromPrefix, $_sourceFileName) + || !StringHelper::endsWith($this->fromPostfix, $_sourceFileName)) { + return null; + } + $varpart = $this->_extractVariablePart($_sourceFileName); + $substitution = $this->toPrefix.$varpart.$this->toPostfix; + return array($substitution); + } + + + + function setFrom($from) { + $index = strrpos($from, '*'); + + if ($index === false) { + $this->fromPrefix = $from; + $this->fromPostfix = ""; + } else { + $this->fromPrefix = substr($from, 0, $index); + $this->fromPostfix = substr($from, $index+1); + } + $this->prefixLength = strlen($this->fromPrefix); + $this->postfixLength = strlen($this->fromPostfix); + } + + /** + * Sets the "to" pattern. Required. + */ + function setTo($to) { + $index = strrpos($to, '*'); + if ($index === false) { + $this->toPrefix = $to; + $this->toPostfix = ""; + } else { + $this->toPrefix = substr($to, 0, $index); + $this->toPostfix = substr($to, $index+1); + } + } + + private function _extractVariablePart($_name) { + // ergh, i really hate php's string functions .... all but natural + $start = ($this->prefixLength === 0) ? 0 : $this->prefixLength; + $end = ($this->postfixLength === 0) ? strlen($_name) : strlen($_name) - $this->postfixLength; + $len = $end-$start; + return substr($_name, $start, $len); + } + +} diff --git a/buildscripts/phing/classes/phing/mappers/IdentityMapper.php b/buildscripts/phing/classes/phing/mappers/IdentityMapper.php new file mode 100755 index 00000000..e608a71d --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/IdentityMapper.php @@ -0,0 +1,54 @@ +<?php +/* + * $Id: aa11e8f44255c1b191df3230336c57664daf5f4f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/mappers/FileNameMapper.php'; + +/** + * This mapper does nothing ;) + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.mappers + */ +class IdentityMapper implements FileNameMapper { + + /** + * The mapper implementation. Basically does nothing in this case. + * + * @param string $sourceFileName The data the mapper works on. + * @return array The data after the mapper has been applied + */ + function main($sourceFileName) { + return array($sourceFileName); + } + + /** + * Ignored here. + */ + function setTo($to) {} + + /** + * Ignored here. + */ + function setFrom($from) {} + +} diff --git a/buildscripts/phing/classes/phing/mappers/MergeMapper.php b/buildscripts/phing/classes/phing/mappers/MergeMapper.php new file mode 100755 index 00000000..dc60ab34 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/MergeMapper.php @@ -0,0 +1,69 @@ +<?php +/* + * $Id: 9f014a901932e99fd50820d3f3a78387929f06ee $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/mappers/FileNameMapper.php'; + +/** + * For merging files into a single file. In practice just returns whatever value + * was set for "to". + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @version $Id$ + * @package phing.mappers + */ +class MergeMapper implements FileNameMapper { + + /** the merge */ + private $mergedFile; + + /** + * The mapper implementation. Basically does nothing in this case. + * + * @param mixed The data the mapper works on + * @return mixed The data after the mapper has been applied + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + function main($sourceFileName) { + if ($this->mergedFile === null) { + throw new BuildException("MergeMapper error, to attribute not set"); + } + return array($this->mergedFile); + } + + /** + * Accessor. Sets the to property + * + * @param string To what this mapper should convert the from string + * @return boolean True + * @access public + * @author Andreas Aderhold, andi@binarycloud.com + */ + function setTo($to) { + $this->mergedFile = $to; + } + + /** + * Ignored. + */ + function setFrom($from) {} + +} diff --git a/buildscripts/phing/classes/phing/mappers/RegexpMapper.php b/buildscripts/phing/classes/phing/mappers/RegexpMapper.php new file mode 100755 index 00000000..ca387241 --- /dev/null +++ b/buildscripts/phing/classes/phing/mappers/RegexpMapper.php @@ -0,0 +1,97 @@ +<?php +/* + * $Id: 96aee1ffd32ba919d42e1141129068006e709976 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/mappers/FileNameMapper.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/util/regexp/Regexp.php'; + +/** + * Uses regular expressions to perform filename transformations. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@velum.net> + * @version $Id$ + * @package phing.mappers + */ +class RegexpMapper implements FileNameMapper { + + /** + * @var string + */ + private $to; + + /** + * The Regexp engine. + * @var Regexp + */ + private $reg; + + function __construct() { + // instantiage regexp matcher here + $this->reg = new Regexp(); + } + + /** + * Sets the "from" pattern. Required. + */ + function setFrom($from) { + $this->reg->SetPattern($from); + } + + /** + * Sets the "to" pattern. Required. + */ + function setTo($to) { + + // [HL] I'm changing the way this works for now to just use string + //$this->to = StringHelper::toCharArray($to); + + $this->to = $to; + } + + function main($sourceFileName) { + if ($this->reg === null || $this->to === null || !$this->reg->matches((string) $sourceFileName)) { + return null; + } + return array($this->replaceReferences($sourceFileName)); + } + + /** + * Replace all backreferences in the to pattern with the matched groups. + * groups of the source. + * @param string $source The source filename. + */ + private function replaceReferences($source) { + + // FIXME + // Can't we just use engine->replace() to handle this? the Preg engine + // will automatically convert \1 references to $1 + + // the expression has already been processed (when ->matches() was run in Main()) + // so no need to pass $source again to the engine. + $groups = (array) $this->reg->getGroups(); + + // replace \1 with value of $groups[1] and return the modified "to" string + return preg_replace('/\\\([\d]+)/e', "\$groups[$1]", $this->to); + } + +} + diff --git a/buildscripts/phing/classes/phing/parser/AbstractHandler.php b/buildscripts/phing/classes/phing/parser/AbstractHandler.php new file mode 100755 index 00000000..1837bbbf --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/AbstractHandler.php @@ -0,0 +1,98 @@ +<?php + +/* + * $Id: 3d81ac2784333ce8108cf32818b8dfc335993507 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/parser/ExpatParseException.php'; + +/** + * This is an abstract class all SAX handler classes must extend + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.parser + */ +abstract class AbstractHandler { + + public $parentHandler = null; + public $parser = null; + + /** + * Constructs a SAX handler parser. + * + * The constructor must be called by all derived classes. + * + * @param object the parser object + * @param object the parent handler of this handler + */ + protected function __construct($parser, $parentHandler) { + $this->parentHandler = $parentHandler; + $this->parser = $parser; + $this->parser->setHandler($this); + } + + /** + * Gets invoked when a XML open tag occurs + * + * Must be overloaded by the child class. Throws an ExpatParseException + * if there is no handler registered for an element. + * + * @param string the name of the XML element + * @param array the attributes of the XML element + */ + public function startElement($name, $attribs) { + throw new ExpatParseException("Unexpected element $name"); + } + + /** + * Gets invoked when element closes method. + * + */ + protected function finished() {} + + /** + * Gets invoked when a XML element ends. + * + * Can be overloaded by the child class. But should not. It hands + * over control to the parentHandler of this. + * + * @param string the name of the XML element + */ + public function endElement($name) { + $this->finished(); + $this->parser->setHandler($this->parentHandler); + } + + /** + * Invoked by occurance of #PCDATA. + * + * @param string the name of the XML element + * @exception ExpatParserException if there is no CDATA but method + * was called + * @access public + */ + public function characters($data) { + $s = trim($data); + if (strlen($s) > 0) { + throw new ExpatParseException("Unexpected text '$s'", $this->parser->getLocation()); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/AbstractSAXParser.php b/buildscripts/phing/classes/phing/parser/AbstractSAXParser.php new file mode 100755 index 00000000..449386e7 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/AbstractSAXParser.php @@ -0,0 +1,116 @@ +<?php +/* + * $Id: 948cef29e65fb684d99cbddc7c633183740a91a8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * The abstract SAX parser class. + * + * This class represents a SAX parser. It is a abstract calss that must be + * implemented by the real parser that must extend this class + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.parser + */ +abstract class AbstractSAXParser { + + /** The AbstractHandler object. */ + protected $handler; + + /** + * Constructs a SAX parser + */ + function __construct() {} + + /** + * Sets options for PHP interal parser. Must be implemented by the parser + * class if it should be used. + */ + abstract function parserSetOption($opt, $val); + + /** + * Sets the current element handler object for this parser. Usually this + * is an object using extending "AbstractHandler". + * + * @param AbstractHandler $obj The handler object. + */ + function setHandler( $obj) { + $this->handler = $obj; + } + + /** + * Method that gets invoked when the parser runs over a XML start element. + * + * This method is called by PHP's internal parser functions and registered + * in the actual parser implementation. + * It gives control to the current active handler object by calling the + * <code>startElement()</code> method. + * + * @param object the php's internal parser handle + * @param string the open tag name + * @param array the tag's attributes if any + * @throws Exception - Exceptions may be thrown by the Handler + */ + function startElement($parser, $name, $attribs) { + $this->handler->startElement($name, $attribs); + } + + /** + * Method that gets invoked when the parser runs over a XML close element. + * + * This method is called by PHP's internal parser funcitons and registered + * in the actual parser implementation. + * + * It gives control to the current active handler object by calling the + * <code>endElement()</code> method. + * + * @param object the php's internal parser handle + * @param string the closing tag name + * @throws Exception - Exceptions may be thrown by the Handler + */ + function endElement($parser, $name) { + $this->handler->endElement($name); + } + + /** + * Method that gets invoked when the parser runs over CDATA. + * + * This method is called by PHP's internal parser functions and registered + * in the actual parser implementation. + * + * It gives control to the current active handler object by calling the + * <code>characters()</code> method. That processes the given CDATA. + * + * @param resource $parser php's internal parser handle. + * @param string $data the CDATA + * @throws Exception - Exceptions may be thrown by the Handler + */ + function characters($parser, $data) { + $this->handler->characters($data); + } + + /** + * Entrypoint for parser. This method needs to be implemented by the + * child classt that utilizes the concrete parser + */ + abstract function parse(); +} diff --git a/buildscripts/phing/classes/phing/parser/DataTypeHandler.php b/buildscripts/phing/classes/phing/parser/DataTypeHandler.php new file mode 100755 index 00000000..7531baaf --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/DataTypeHandler.php @@ -0,0 +1,144 @@ +<?php +/* + * $Id: dbae47cfbf1ad4a3ac68909118f69175cf1d45f4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/RuntimeConfigurable.php'; + +/** + * Configures a Project (complete with Targets and Tasks) based on + * a XML build file. + * <p> + * Design/ZE2 migration note: + * If PHP would support nested classes. All the phing/parser/*Filter + * classes would be nested within this class + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class DataTypeHandler extends AbstractHandler { + + private $target; + private $element; + private $wrapper; + + /** + * Constructs a new DataTypeHandler and sets up everything. + * + * @param AbstractSAXParser $parser The XML parser (default: ExpatParser) + * @param AbstractHandler $parentHandler The parent handler that invoked this handler. + * @param ProjectConfigurator $configurator The ProjectConfigurator object + * @param Target $target The target object this datatype is contained in (null for top-level datatypes). + */ + function __construct(AbstractSAXParser $parser, AbstractHandler $parentHandler, ProjectConfigurator $configurator, $target = null) { // FIXME b2 typehinting + parent::__construct($parser, $parentHandler); + $this->target = $target; + $this->configurator = $configurator; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + * <p> + * This includes: + * <ul> + * <li>creation of the datatype object</li> + * <li>calling the setters for attributes</li> + * <li>adding the type to the target object if any</li> + * <li>adding a reference to the task (if id attribute is given)</li> + * </ul> + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if attributes are incomplete or invalid + * @access public + */ + function init($propType, $attrs) { + // shorthands + $project = $this->configurator->project; + $configurator = $this->configurator; + + try {//try + $this->element = $project->createDataType($propType); + + if ($this->element === null) { + throw new BuildException("Unknown data type $propType"); + } + + if ($this->target !== null) { + $this->wrapper = new RuntimeConfigurable($this->element, $propType); + $this->wrapper->setAttributes($attrs); + $this->target->addDataType($this->wrapper); + } else { + $configurator->configure($this->element, $attrs, $project); + $configurator->configureId($this->element, $attrs); + } + + } catch (BuildException $exc) { + throw new ExpatParseException($exc, $this->parser->getLocation()); + } + } + + /** + * Handles character data. + * + * @param string the CDATA that comes in + * @access public + */ + function characters($data) { + $project = $this->configurator->project; + try {//try + $this->configurator->addText($project, $this->element, $data); + } catch (BuildException $exc) { + throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation()); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @access public + */ + function startElement($name, $attrs) { + $nef = new NestedElementHandler($this->parser, $this, $this->configurator, $this->element, $this->wrapper, $this->target); + $nef->init($name, $attrs); + } + + /** + * Overrides endElement for data types. Tells the type + * handler that processing the element had been finished so + * handlers know they can perform actions that need to be + * based on the data contained within the element. + * + * @param string the name of the XML element + * @return void + */ + function endElement($name) { + $this->element->parsingComplete(); + parent::endElement($name); + } + +} diff --git a/buildscripts/phing/classes/phing/parser/ExpatParseException.php b/buildscripts/phing/classes/phing/parser/ExpatParseException.php new file mode 100755 index 00000000..65461576 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ExpatParseException.php @@ -0,0 +1,31 @@ +<?php +/* + * $Id: c384bde6ba2bb6902b1f943e35afd9f8b5d4efd8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/BuildException.php'; + +/** + * This class throws errors for Expat, the XML processor. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.parser + */ +class ExpatParseException extends BuildException {} diff --git a/buildscripts/phing/classes/phing/parser/ExpatParser.php b/buildscripts/phing/classes/phing/parser/ExpatParser.php new file mode 100755 index 00000000..ef5348e5 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ExpatParser.php @@ -0,0 +1,140 @@ +<?php +/* + * $Id: 0363c59b524447dc74014b03d913fa5a25ada33a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/parser/AbstractSAXParser.php'; +include_once 'phing/parser/ExpatParseException.php'; +include_once 'phing/system/io/IOException.php'; +include_once 'phing/system/io/FileReader.php'; + +/** + * This class is a wrapper for the PHP's internal expat parser. + * + * It takes an XML file represented by a abstract path name, and starts + * parsing the file and calling the different "trap" methods inherited from + * the AbstractParser class. + * + * Those methods then invoke the represenatative methods in the registered + * handler classes. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class ExpatParser extends AbstractSAXParser { + + /** @var resource */ + private $parser; + + /** @var Reader */ + private $reader; + + private $file; + + private $buffer = 4096; + + private $error_string = ""; + + private $line = 0; + + /** @var Location Current cursor pos in XML file. */ + private $location; + + /** + * Constructs a new ExpatParser object. + * + * The constructor accepts a PhingFile object that represents the filename + * for the file to be parsed. It sets up php's internal expat parser + * and options. + * + * @param Reader $reader The Reader Object that is to be read from. + * @param string $filename Filename to read. + * @throws Exception if the given argument is not a PhingFile object + */ + function __construct(Reader $reader, $filename=null) { + + $this->reader = $reader; + if ($filename !== null) { + $this->file = new PhingFile($filename); + } + $this->parser = xml_parser_create(); + $this->buffer = 4096; + $this->location = new Location(); + xml_set_object($this->parser, $this); + xml_set_element_handler($this->parser, array($this,"startElement"),array($this,"endElement")); + xml_set_character_data_handler($this->parser, array($this, "characters")); + } + + /** + * Override PHP's parser default settings, created in the constructor. + * + * @param string the option to set + * @throws mixed the value to set + * @return boolean true if the option could be set, otherwise false + * @access public + */ + function parserSetOption($opt, $val) { + return xml_parser_set_option($this->parser, $opt, $val); + } + + /** + * Returns the location object of the current parsed element. It describes + * the location of the element within the XML file (line, char) + * + * @return object the location of the current parser + * @access public + */ + function getLocation() { + if ($this->file !== null) { + $path = $this->file->getAbsolutePath(); + } else { + $path = $this->reader->getResource(); + } + $this->location = new Location($path, xml_get_current_line_number($this->parser), xml_get_current_column_number($this->parser)); + return $this->location; + } + + /** + * Starts the parsing process. + * + * @param string the option to set + * @return int 1 if the parsing succeeded + * @throws ExpatParseException if something gone wrong during parsing + * @throws IOException if XML file can not be accessed + * @access public + */ + function parse() { + + while ( ($data = $this->reader->read()) !== -1 ) { + if (!xml_parse($this->parser, $data, $this->reader->eof())) { + $error = xml_error_string(xml_get_error_code($this->parser)); + $e = new ExpatParseException($error, $this->getLocation()); + xml_parser_free($this->parser); + throw $e; + } + } + xml_parser_free($this->parser); + + return 1; + } +} diff --git a/buildscripts/phing/classes/phing/parser/Location.php b/buildscripts/phing/classes/phing/parser/Location.php new file mode 100755 index 00000000..4110bd00 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/Location.php @@ -0,0 +1,76 @@ +<?php +/* + * $Id: ffb9bc98eef0f4e535f60388d52b7495c539f682 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Stores the file name and line number of a XML file + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class Location { + + private $fileName; + private $lineNumber; + private $columnNumber; + + /** + * Constructs the location consisting of a file name and line number + * + * @param string the filename + * @param integer the line number + * @param integer the column number + * @access public + */ + function Location($fileName = null, $lineNumber = null, $columnNumber = null) { + $this->fileName = $fileName; + $this->lineNumber = $lineNumber; + $this->columnNumber = $columnNumber; + } + + /** + * Returns the file name, line number and a trailing space. + * + * An error message can be appended easily. For unknown locations, + * returns empty string. + * + * @return string the string representation of this Location object + * @access public + */ + function toString() { + $buf = ""; + if ($this->fileName !== null) { + $buf.=$this->fileName; + if ($this->lineNumber !== null) { + $buf.= ":".$this->lineNumber; + } + $buf.=":".$this->columnNumber; + } + return (string) $buf; + } + + function __toString () { + return $this->toString(); + } +} diff --git a/buildscripts/phing/classes/phing/parser/NestedElementHandler.php b/buildscripts/phing/classes/phing/parser/NestedElementHandler.php new file mode 100755 index 00000000..15d0e173 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/NestedElementHandler.php @@ -0,0 +1,186 @@ +<?php +/* + * $Id: 94cdf9380aea8a52cb09663489c2d2c5e28740aa $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/IntrospectionHelper.php'; +include_once 'phing/TaskContainer.php'; + +/** + * The nested element handler class. + * + * This class handles the occurance of runtime registered tags like + * datatypes (fileset, patternset, etc) and it's possible nested tags. It + * introspects the implementation of the class and sets up the data structures. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.parser + */ + +class NestedElementHandler extends AbstractHandler { + + /** + * Reference to the parent object that represents the parent tag + * of this nested element + * @var object + */ + private $parent; + + /** + * Reference to the child object that represents the child tag + * of this nested element + * @var object + */ + private $child; + + /** + * Reference to the parent wrapper object + * @var object + */ + private $parentWrapper; + + /** + * Reference to the child wrapper object + * @var object + */ + private $childWrapper; + + /** + * Reference to the related target object + * @var object the target instance + */ + private $target; + + /** + * Constructs a new NestedElement handler and sets up everything. + * + * @param object the ExpatParser object + * @param object the parent handler that invoked this handler + * @param object the ProjectConfigurator object + * @param object the parent object this element is contained in + * @param object the parent wrapper object + * @param object the target object this task is contained in + * @access public + */ + function __construct($parser, $parentHandler, $configurator, $parent, $parentWrapper, $target) { + parent::__construct($parser, $parentHandler); + $this->configurator = $configurator; + if ($parent instanceof TaskAdapter) { + $this->parent = $parent->getProxy(); + } else { + $this->parent = $parent; + } + $this->parentWrapper = $parentWrapper; + $this->target = $target; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + * <p> + * This includes: + * <ul> + * <li>creation of the nested element</li> + * <li>calling the setters for attributes</li> + * <li>adding the element to the container object</li> + * <li>adding a reference to the element (if id attribute is given)</li> + * </ul> + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if the setup process fails + * @access public + */ + function init($propType, $attrs) { + $configurator = $this->configurator; + $project = $this->configurator->project; + + // introspect the parent class that is custom + $parentClass = get_class($this->parent); + $ih = IntrospectionHelper::getHelper($parentClass); + try { + if ($this->parent instanceof UnknownElement) { + $this->child = new UnknownElement(strtolower($propType)); + $this->parent->addChild($this->child); + } else { + $this->child = $ih->createElement($project, $this->parent, strtolower($propType)); + } + + $configurator->configureId($this->child, $attrs); + + if ($this->parentWrapper !== null) { + $this->childWrapper = new RuntimeConfigurable($this->child, $propType); + $this->childWrapper->setAttributes($attrs); + $this->parentWrapper->addChild($this->childWrapper); + } else { + $configurator->configure($this->child, $attrs, $project); + $ih->storeElement($project, $this->parent, $this->child, strtolower($propType)); + } + } catch (BuildException $exc) { + throw new ExpatParseException("Error initializing nested element <$propType>", $exc, $this->parser->getLocation()); + } + } + + /** + * Handles character data. + * + * @param string the CDATA that comes in + * @throws ExpatParseException if the CDATA could not be set-up properly + * @access public + */ + function characters($data) { + + $configurator = $this->configurator; + $project = $this->configurator->project; + + if ($this->parentWrapper === null) { + try { + $configurator->addText($project, $this->child, $data); + } catch (BuildException $exc) { + throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation()); + } + } else { + $this->childWrapper->addText($data); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @access public + */ + function startElement($name, $attrs) { + //print(get_class($this) . " name = $name, attrs = " . implode(",",$attrs) . "\n"); + if ($this->child instanceof TaskContainer) { + // taskcontainer nested element can contain other tasks - no other + // nested elements possible + $tc = new TaskHandler($this->parser, $this, $this->configurator, $this->child, $this->childWrapper, $this->target); + $tc->init($name, $attrs); + } else { + $neh = new NestedElementHandler($this->parser, $this, $this->configurator, $this->child, $this->childWrapper, $this->target); + $neh->init($name, $attrs); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/PhingXMLContext.php b/buildscripts/phing/classes/phing/parser/PhingXMLContext.php new file mode 100755 index 00000000..0e2dd0f4 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/PhingXMLContext.php @@ -0,0 +1,81 @@ +<?php +/* + * $Id: bfc85569ff437f8ee382c9dee6fc8eb55ecb9839 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Track the current state of the Xml parse operation. + * + * @author Bryan Davis <bpd@keynetics.com> + * @version $Id$ + * @access public + * @package phing.parser + */ +class PhingXMLContext { + + /** + * Constructor + * @param $project the project to which this antxml context belongs to + */ + public function __construct ($project) { + $this->project = $project; + } + + /** The project to configure. */ + private $project; + + private $configurators = array(); + + public function startConfigure ($cfg) { + $this->configurators[] = $cfg; + } + + public function endConfigure () { + array_pop($this->configurators); + } + + public function getConfigurator () { + $l = count($this->configurators); + if (0 == $l) { + return null; + } else { + return $this->configurators[$l - 1]; + } + } + + /** Impoerted files */ + private $importStack = array(); + + public function addImport ($file) { + $this->importStack[] = $file; + } + + public function getImportStack () { + return $this->importStack; + } + + /** + * find out the project to which this context belongs + * @return project + */ + public function getProject() { + return $this->project; + } + +} //end PhingXMLContext diff --git a/buildscripts/phing/classes/phing/parser/ProjectConfigurator.php b/buildscripts/phing/classes/phing/parser/ProjectConfigurator.php new file mode 100755 index 00000000..90471336 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ProjectConfigurator.php @@ -0,0 +1,387 @@ +<?php +/* + * $Id: ae9b63bef4c1423d88bb36ddfc5196763828123e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/BufferedReader.php'; +include_once 'phing/system/io/FileReader.php'; +include_once 'phing/BuildException.php'; +include_once 'phing/system/lang/FileNotFoundException.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/parser/PhingXMLContext.php'; +include_once 'phing/IntrospectionHelper.php'; + +/** + * The datatype handler class. + * + * This class handles the occurance of registered datatype tags like + * FileSet + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: ae9b63bef4c1423d88bb36ddfc5196763828123e $ + * @access public + * @package phing.parser + */ +class ProjectConfigurator { + + public $project; + public $locator; + + public $buildFile; + public $buildFileParent; + + /** Targets in current file */ + private $currentTargets; + + /** Synthetic target that will be called at the end to the parse phase */ + private $parseEndTarget; + + /** Name of the current project */ + private $currentProjectName; + + private $isParsing = true; + + /** + * Indicates whether the project tag attributes are to be ignored + * when processing a particular build file. + */ + private $ignoreProjectTag = false; + + /** + * Static call to ProjectConfigurator. Use this to configure a + * project. Do not use the new operator. + * + * @param object the Project instance this configurator should use + * @param object the buildfile object the parser should use + * @access public + */ + public static function configureProject(Project $project, PhingFile $buildFile) { + $pc = new ProjectConfigurator($project, $buildFile); + $pc->parse(); + } + + /** + * Constructs a new ProjectConfigurator object + * This constructor is private. Use a static call to + * <code>configureProject</code> to configure a project. + * + * @param object the Project instance this configurator should use + * @param object the buildfile object the parser should use + * @access private + */ + function __construct(Project $project, PhingFile $buildFile) { + $this->project = $project; + $this->buildFile = new PhingFile($buildFile->getAbsolutePath()); + $this->buildFileParent = new PhingFile($this->buildFile->getParent()); + $this->currentTargets = array(); + $this->parseEndTarget = new Target(); + } + + /** + * find out the build file + * @return the build file to which the xml context belongs + */ + public function getBuildFile() { + return $this->buildFile; + } + + /** + * find out the parent build file of this build file + * @return the parent build file of this build file + */ + public function getBuildFileParent() { + return $this->buildFileParent; + } + + /** + * find out the current project name + * @return current project name + */ + public function getCurrentProjectName() { + return $this->currentProjectName; + } + + /** + * set the name of the current project + * @param name name of the current project + */ + public function setCurrentProjectName($name) { + $this->currentProjectName = $name; + } + + /** + * tells whether the project tag is being ignored + * @return whether the project tag is being ignored + */ + public function isIgnoringProjectTag() { + return $this->ignoreProjectTag; + } + + /** + * sets the flag to ignore the project tag + * @param flag to ignore the project tag + */ + public function setIgnoreProjectTag($flag) { + $this->ignoreProjectTag = $flag; + } + + public function &getCurrentTargets () { + return $this->currentTargets; + } + + public function isParsing () { + return $this->isParsing; + } + + /** + * Creates the ExpatParser, sets root handler and kick off parsing + * process. + * + * @throws BuildException if there is any kind of execption during + * the parsing process + * @access private + */ + protected function parse() { + try { + // get parse context + $ctx = $this->project->getReference("phing.parsing.context"); + if (null == $ctx) { + // make a new context and register it with project + $ctx = new PhingXMLContext($this->project); + $this->project->addReference("phing.parsing.context", $ctx); + } + + //record this parse with context + $ctx->addImport($this->buildFile); + + if (count($ctx->getImportStack()) > 1) { + // this is an imported file + // modify project tag parse behavior + $this->setIgnoreProjectTag(true); + } + // push action onto global stack + $ctx->startConfigure($this); + + $reader = new BufferedReader(new FileReader($this->buildFile)); + $parser = new ExpatParser($reader); + $parser->parserSetOption(XML_OPTION_CASE_FOLDING,0); + $parser->setHandler(new RootHandler($parser, $this)); + $this->project->log("parsing buildfile ".$this->buildFile->getName(), Project::MSG_VERBOSE); + $parser->parse(); + $reader->close(); + + // mark parse phase as completed + $this->isParsing = false; + // execute delayed tasks + $this->parseEndTarget->main(); + // pop this action from the global stack + $ctx->endConfigure(); + } catch (Exception $exc) { + throw new BuildException("Error reading project file", $exc); + } + } + + /** + * Delay execution of a task until after the current parse phase has + * completed. + * + * @param Task $task Task to execute after parse + */ + public function delayTaskUntilParseEnd ($task) { + $this->parseEndTarget->addTask($task); + } + + /** + * Configures an element and resolves eventually given properties. + * + * @param object the element to configure + * @param array the element's attributes + * @param object the project this element belongs to + * @throws Exception if arguments are not valid + * @throws BuildException if attributes can not be configured + * @access public + */ + public static function configure($target, $attrs, Project $project) { + + if ($target instanceof TaskAdapter) { + $target = $target->getProxy(); + } + + // if the target is an UnknownElement, this means that the tag had not been registered + // when the enclosing element (task, target, etc.) was configured. It is possible, however, + // that the tag was registered (e.g. using <taskdef>) after the original configuration. + // ... so, try to load it again: + if ($target instanceof UnknownElement) { + $tryTarget = $project->createTask($target->getTaskType()); + if ($tryTarget) { + $target = $tryTarget; + } + } + + $bean = get_class($target); + $ih = IntrospectionHelper::getHelper($bean); + + foreach ($attrs as $key => $value) { + if ($key == 'id') { + continue; + // throw new BuildException("Id must be set Extermnally"); + } + $value = self::replaceProperties($project, $value, $project->getProperties()); + try { // try to set the attribute + $ih->setAttribute($project, $target, strtolower($key), $value); + } catch (BuildException $be) { + // id attribute must be set externally + if ($key !== "id") { + throw $be; + } + } + } + } + + /** + * Configures the #CDATA of an element. + * + * @param object the project this element belongs to + * @param object the element to configure + * @param string the element's #CDATA + * @access public + */ + public static function addText($project, $target, $text = null) { + if ($text === null || strlen(trim($text)) === 0) { + return; + } + $ih = IntrospectionHelper::getHelper(get_class($target)); + $text = self::replaceProperties($project, $text, $project->getProperties()); + $ih->addText($project, $target, $text); + } + + /** + * Stores a configured child element into its parent object + * + * @param object the project this element belongs to + * @param object the parent element + * @param object the child element + * @param string the XML tagname + * @access public + */ + public static function storeChild($project, $parent, $child, $tag) { + $ih = IntrospectionHelper::getHelper(get_class($parent)); + $ih->storeElement($project, $parent, $child, $tag); + } + + // The following three properties are a sort of hack + // to enable a static function to serve as the callback + // for preg_replace_callback(). Clearly we cannot use object + // variables, since the replaceProperties() is called statically. + // This is IMO better than using global variables in the callback. + + private static $propReplaceProject; + private static $propReplaceProperties; + private static $propReplaceLogLevel = Project::MSG_VERBOSE; + + /** + * Replace ${} style constructions in the given value with the + * string value of the corresponding data types. This method is + * static. + * + * @param object $project the project that should be used for property look-ups + * @param string $value the string to be scanned for property references + * @param array $keys property keys + * @param integer $logLevel the level of generated log messages + * @return string the replaced string or <code>null</code> if the string + * itself was null + */ + public static function replaceProperties(Project $project, $value, $keys, $logLevel = Project::MSG_VERBOSE) { + + if ($value === null) { + return null; + } + + // These are a "hack" to support static callback for preg_replace_callback() + + // make sure these get initialized every time + self::$propReplaceProperties = $keys; + self::$propReplaceProject = $project; + self::$propReplaceLogLevel = $logLevel; + + // Because we're not doing anything special (like multiple passes), + // regex is the simplest / fastest. PropertyTask, though, uses + // the old parsePropertyString() method, since it has more stringent + // requirements. + + $sb = $value; + $iteration = 0; + + // loop to recursively replace tokens + while (strpos($sb, '${') !== false) + { + $sb = preg_replace_callback('/\$\{([^\$}]+)\}/', array('ProjectConfigurator', 'replacePropertyCallback'), $sb); + + // keep track of iterations so we can break out of otherwise infinite loops. + $iteration++; + if ($iteration == 5) + { + return $sb; + } + } + + return $sb; + } + + /** + * Private [static] function for use by preg_replace_callback to replace a single param. + * This method makes use of a static variable to hold the + */ + private static function replacePropertyCallback($matches) + { + $propertyName = $matches[1]; + if (!isset(self::$propReplaceProperties[$propertyName])) { + self::$propReplaceProject->log('Property ${'.$propertyName.'} has not been set.', self::$propReplaceLogLevel); + return $matches[0]; + } else { + self::$propReplaceProject->log('Property ${'.$propertyName.'} => ' . self::$propReplaceProperties[$propertyName], self::$propReplaceLogLevel); + } + + $propertyValue = self::$propReplaceProperties[$propertyName]; + + if (is_bool($propertyValue)) { + if ($propertyValue === true) { + $propertyValue = "true"; + } else { + $propertyValue = "false"; + } + } + + return $propertyValue; + } + + /** + * Scan Attributes for the id attribute and maybe add a reference to + * project. + * + * @param object the element's object + * @param array the element's attributes + */ + public function configureId($target, $attr) { + if (isset($attr['id']) && $attr['id'] !== null) { + $this->project->addReference($attr['id'], $target); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/ProjectHandler.php b/buildscripts/phing/classes/phing/parser/ProjectHandler.php new file mode 100755 index 00000000..a027c1e6 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/ProjectHandler.php @@ -0,0 +1,176 @@ +<?php +/* + * $Id: 393b1f3d8758281af2fd0153ffc6151333457527 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/parser/AbstractHandler.php'; +require_once 'phing/system/io/PhingFile.php'; + +/** + * Handler class for the <project> XML element This class handles all elements + * under the <project> element. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright (c) 2001,2002 THYRELL. All rights reserved + * @version $Id: 393b1f3d8758281af2fd0153ffc6151333457527 $ + * @access public + * @package phing.parser + */ +class ProjectHandler extends AbstractHandler { + + /** + * The phing project configurator object. + * @var ProjectConfigurator + */ + private $configurator; + + /** + * Constructs a new ProjectHandler + * + * @param object the ExpatParser object + * @param object the parent handler that invoked this handler + * @param object the ProjectConfigurator object + * @access public + */ + function __construct($parser, $parentHandler, $configurator) { + $this->configurator = $configurator; + parent::__construct($parser, $parentHandler); + } + + /** + * Executes initialization actions required to setup the project. Usually + * this method handles the attributes of a tag. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @param object the ProjectConfigurator object + * @throws ExpatParseException if attributes are incomplete or invalid + * @access public + */ + function init($tag, $attrs) { + $def = null; + $name = null; + $id = null; + $desc = null; + $baseDir = null; + $ver = null; + + // some shorthands + $project = $this->configurator->project; + $buildFileParent = $this->configurator->buildFileParent; + + foreach ($attrs as $key => $value) { + if ($key === "default") { + $def = $value; + } elseif ($key === "name") { + $name = $value; + } elseif ($key === "id") { + $id = $value; + } elseif ($key === "basedir") { + $baseDir = $value; + } elseif ($key === "description") { + $desc = $value; + } elseif ($key === "phingVersion") { + $ver = $value; + } else { + throw new ExpatParseException("Unexpected attribute '$key'"); + } + } + // these things get done no matter what + if (null != $name) { + $canonicalName = self::canonicalName($name); + $this->configurator->setCurrentProjectName($canonicalName); + $project->setUserProperty("phing.file.{$canonicalName}", + (string) $this->configurator->getBuildFile()); + } + + if (!$this->configurator->isIgnoringProjectTag()) { + if ($def === null) { + throw new ExpatParseException( + "The default attribute of project is required"); + } + $project->setDefaultTarget($def); + + if ($name !== null) { + $project->setName($name); + $project->addReference($name, $project); + + } + + if ($id !== null) { + $project->addReference($id, $project); + } + + if ($desc !== null) { + $project->setDescription($desc); + } + + if($ver !== null) { + $project->setPhingVersion($ver); + } + + if ($project->getProperty("project.basedir") !== null) { + $project->setBasedir($project->getProperty("project.basedir")); + } else { + if ($baseDir === null) { + $project->setBasedir($buildFileParent->getAbsolutePath()); + } else { + // check whether the user has specified an absolute path + $f = new PhingFile($baseDir); + if ($f->isAbsolute()) { + $project->setBasedir($baseDir); + } else { + $project->setBaseDir($project->resolveFile($baseDir, new PhingFile(getcwd()))); + } + } + } + } + } + + /** + * Handles start elements within the <project> tag by creating and + * calling the required handlers for the detected element. + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if a unxepected element occurs + * @access public + */ + function startElement($name, $attrs) { + + $project = $this->configurator->project; + $types = $project->getDataTypeDefinitions(); + + if ($name == "target") { + $tf = new TargetHandler($this->parser, $this, $this->configurator); + $tf->init($name, $attrs); + } elseif (isset($types[$name])) { + $tyf = new DataTypeHandler($this->parser, $this, $this->configurator); + $tyf->init($name, $attrs); + } else { + $tf = new TaskHandler($this->parser, $this, $this->configurator); + $tf->init($name, $attrs); + } + } + + static function canonicalName ($name) { + return preg_replace('/\W/', '_', strtolower($name)); + } +} + diff --git a/buildscripts/phing/classes/phing/parser/RootHandler.php b/buildscripts/phing/classes/phing/parser/RootHandler.php new file mode 100755 index 00000000..435aae40 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/RootHandler.php @@ -0,0 +1,82 @@ +<?php +/* + * $Id: 061d3ffe06257d551822050020d0e64689663334 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/parser/AbstractHandler.php'; +include_once 'phing/parser/ExpatParseException.php'; +include_once 'phing/parser/ProjectHandler.php'; + +/** + * Root filter class for a phing buildfile. + * + * The root filter is called by the parser first. This is where the phing + * specific parsing starts. RootHandler decides what to do next. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.parser + */ +class RootHandler extends AbstractHandler { + + /** + * The phing project configurator object + */ + private $configurator; + + /** + * Constructs a new RootHandler + * + * The root filter is required so the parser knows what to do. It's + * called by the ExpatParser that is instatiated in ProjectConfigurator. + * + * It recieves the expat parse object ref and a reference to the + * configurator + * + * @param AbstractSAXParser $parser The ExpatParser object. + * @param ProjectConfigurator $configurator The ProjectConfigurator object. + */ + function __construct(AbstractSAXParser $parser, ProjectConfigurator $configurator) { + $this->configurator = $configurator; + parent::__construct($parser, $this); + } + + /** + * Kick off a custom action for a start element tag. + * + * The root element of our buildfile is the <project> element. The + * root filter handles this element if it occurs, creates ProjectHandler + * to handle any nested tags & attributes of the <project> tag, + * and calls init. + * + * @param string $tag The xml tagname + * @param array $attrs The attributes of the tag + * @throws ExpatParseException if the first element within our build file + * is not the >project< element + */ + function startElement($tag, $attrs) { + if ($tag === "project") { + $ph = new ProjectHandler($this->parser, $this, $this->configurator); + $ph->init($tag, $attrs); + } else { + throw new ExpatParseException("Unexpected tag <$tag> in top-level of build file.", $this->parser->getLocation()); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/TargetHandler.php b/buildscripts/phing/classes/phing/parser/TargetHandler.php new file mode 100755 index 00000000..a6c4d037 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/TargetHandler.php @@ -0,0 +1,196 @@ +<?php +/* + * $Id: f73d7c67a353cf16f048af3ba013d84ec726a926 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/parser/AbstractHandler.php'; + +/** + * The target handler class. + * + * This class handles the occurance of a <target> tag and it's possible + * nested tags (datatypes and tasks). + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: f73d7c67a353cf16f048af3ba013d84ec726a926 $ + * @package phing.parser + */ +class TargetHandler extends AbstractHandler { + + /** + * Reference to the target object that represents the currently parsed + * target. + * @var object the target instance + */ + private $target; + + /** + * The phing project configurator object + * @var ProjectConfigurator + */ + private $configurator; + + /** + * Constructs a new TargetHandler + * + * @param object the ExpatParser object + * @param object the parent handler that invoked this handler + * @param object the ProjectConfigurator object + */ + function __construct(AbstractSAXParser $parser, AbstractHandler $parentHandler, ProjectConfigurator $configurator) { + parent::__construct($parser, $parentHandler); + $this->configurator = $configurator; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + * <p> + * This includes: + * <ul> + * <li>creation of the target object</li> + * <li>calling the setters for attributes</li> + * <li>adding the target to the project</li> + * <li>adding a reference to the target (if id attribute is given)</li> + * </ul> + * + * @param string the tag that comes in + * @param array attributes the tag carries + * @throws ExpatParseException if attributes are incomplete or invalid + */ + function init($tag, $attrs) { + $name = null; + $depends = ""; + $ifCond = null; + $unlessCond = null; + $id = null; + $description = null; + $isHidden = false; + + foreach($attrs as $key => $value) { + if ($key==="name") { + $name = (string) $value; + } else if ($key==="depends") { + $depends = (string) $value; + } else if ($key==="if") { + $ifCond = (string) $value; + } else if ($key==="unless") { + $unlessCond = (string) $value; + } else if ($key==="id") { + $id = (string) $value; + } else if ($key==="hidden") { + $isHidden = ($value == 'true' || $value == '1') ? true : false; + } else if ($key==="description") { + $description = (string)$value; + } else { + throw new ExpatParseException("Unexpected attribute '$key'", $this->parser->getLocation()); + } + } + + if ($name === null) { + throw new ExpatParseException("target element appears without a name attribute", $this->parser->getLocation()); + } + + // shorthand + $project = $this->configurator->project; + + // check to see if this target is a dup within the same file + if (isset($this->configurator->getCurrentTargets[$name])) { + throw new BuildException("Duplicate target: $targetName", + $this->parser->getLocation()); + } + + $this->target = new Target(); + $this->target->setName($name); + $this->target->setHidden($isHidden); + $this->target->setIf($ifCond); + $this->target->setUnless($unlessCond); + $this->target->setDescription($description); + // take care of dependencies + if (strlen($depends) > 0) { + $this->target->setDepends($depends); + } + + $usedTarget = false; + // check to see if target with same name is already defined + $projectTargets = $project->getTargets(); + if (isset($projectTargets[$name])) { + $project->log("Already defined in main or a previous import, " . + "ignore {$name}", Project::MSG_VERBOSE); + } else { + $project->addTarget($name, $this->target); + if ($id !== null && $id !== "") { + $project->addReference($id, $this->target); + } + $usedTarget = true; + } + + if ($this->configurator->isIgnoringProjectTag() && + $this->configurator->getCurrentProjectName() != null && + strlen($this->configurator->getCurrentProjectName()) != 0) { + // In an impored file (and not completely + // ignoring the project tag) + $newName = $this->configurator->getCurrentProjectName() . "." . $name; + if ($usedTarget) { + // clone needs to make target->children a shared reference + $newTarget = clone $this->target; + } else { + $newTarget = $this->target; + } + $newTarget->setName($newName); + $ct = $this->configurator->getCurrentTargets(); + $ct[$newName] = $newTarget; + $project->addTarget($newName, $newTarget); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string the tag that comes in + * @param array attributes the tag carries + */ + function startElement($name, $attrs) { + // shorthands + $project = $this->configurator->project; + $types = $project->getDataTypeDefinitions(); + + if (isset($types[$name])) { + $th = new DataTypeHandler($this->parser, $this, $this->configurator, $this->target); + $th->init($name, $attrs); + } else { + $tmp = new TaskHandler($this->parser, $this, $this->configurator, $this->target, null, $this->target); + $tmp->init($name, $attrs); + } + } + + /** + * Checks if this target has dependencies and/or nested tasks. + * If the target has neither, show a warning. + */ + protected function finished() + { + if (!count($this->target->getDependencies()) && !count($this->target->getTasks())) { + $this->configurator->project->log("Warning: target '" . $this->target->getName() . + "' has no tasks or dependencies", Project::MSG_WARN); + } + } +} diff --git a/buildscripts/phing/classes/phing/parser/TaskHandler.php b/buildscripts/phing/classes/phing/parser/TaskHandler.php new file mode 100755 index 00000000..53e07a56 --- /dev/null +++ b/buildscripts/phing/classes/phing/parser/TaskHandler.php @@ -0,0 +1,234 @@ +<?php +/* + * $Id: 3b31bb3e2c1ae122411833c67617e7cebf6967b8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/UnknownElement.php'; + +/** + * The task handler class. + * + * This class handles the occurance of a <task> tag and it's possible + * nested tags (datatypes and tasks) that may be unknown off bat and are + * initialized on the fly. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: 3b31bb3e2c1ae122411833c67617e7cebf6967b8 $ + * @package phing.parser + */ +class TaskHandler extends AbstractHandler { + + /** + * Reference to the target object that contains the currently parsed + * task + * @var object the target instance + */ + private $target; + + /** + * Reference to the target object that represents the currently parsed + * target. This must not necessarily be a target, hence extra variable. + * @var object the target instance + */ + private $container; + + /** + * Reference to the task object that represents the currently parsed + * target. + * @var Task + */ + private $task; + + /** + * Wrapper for the parent element, if any. The wrapper for this + * element will be added to this wrapper as a child. + * @var RuntimeConfigurable + */ + private $parentWrapper; + + /** + * Wrapper for this element which takes care of actually configuring + * the element, if this element is contained within a target. + * Otherwise the configuration is performed with the configure method. + * @see ProjectHelper::configure(Object,AttributeList,Project) + */ + private $wrapper; + + /** + * The phing project configurator object + * @var ProjectConfigurator + */ + private $configurator; + + /** + * Constructs a new TaskHandler and sets up everything. + * + * @param AbstractSAXParser The ExpatParser object + * @param object $parentHandler The parent handler that invoked this handler + * @param ProjectConfigurator $configurator + * @param TaskContainer $container The container object this task is contained in (null for top-level tasks). + * @param RuntimeConfigurable $parentWrapper Wrapper for the parent element, if any. + * @param Target $target The target object this task is contained in (null for top-level tasks). + */ + function __construct(AbstractSAXParser $parser, $parentHandler, ProjectConfigurator $configurator, $container = null, $parentWrapper = null, $target = null) { + + parent::__construct($parser, $parentHandler); + + if (($container !== null) && !($container instanceof TaskContainer)) { + throw new Exception("Argument expected to be a TaskContainer, got something else"); + } + if (($parentWrapper !== null) && !($parentWrapper instanceof RuntimeConfigurable)) { + throw new Exception("Argument expected to be a RuntimeConfigurable, got something else."); + } + if (($target !== null) && !($target instanceof Target)) { + throw new Exception("Argument expected to be a Target, got something else"); + } + + $this->configurator = $configurator; + $this->container = $container; + $this->parentWrapper = $parentWrapper; + $this->target = $target; + } + + /** + * Executes initialization actions required to setup the data structures + * related to the tag. + * <p> + * This includes: + * <ul> + * <li>creation of the task object</li> + * <li>calling the setters for attributes</li> + * <li>adding the task to the container object</li> + * <li>adding a reference to the task (if id attribute is given)</li> + * <li>executing the task if the container is the <project> + * element</li> + * </ul> + * + * @param string $tag The tag that comes in + * @param array $attrs Attributes the tag carries + * @throws ExpatParseException if attributes are incomplete or invalid + */ + function init($tag, $attrs) { + // shorthands + try { + $configurator = $this->configurator; + $project = $this->configurator->project; + + $this->task = $project->createTask($tag); + } catch (BuildException $be) { + // swallow here, will be thrown again in + // UnknownElement->maybeConfigure if the problem persists. + print("Swallowing exception: ".$be->getMessage() . "\n"); + } + + // the task is not known of bat, try to load it on thy fly + if ($this->task === null) { + $this->task = new UnknownElement($tag); + $this->task->setProject($project); + $this->task->setTaskType($tag); + $this->task->setTaskName($tag); + } + + // add file position information to the task (from parser) + // should be used in task exceptions to provide details + $this->task->setLocation($this->parser->getLocation()); + $configurator->configureId($this->task, $attrs); + + if ($this->container) { + $this->container->addTask($this->task); + } + + // Top level tasks don't have associated targets + // FIXME: if we do like Ant 1.6 and create an implicitTarget in the projectconfigurator object + // then we don't need to check for null here ... but there's a lot of stuff that will break if we + // do that at this point. + if ($this->target !== null) { + $this->task->setOwningTarget($this->target); + $this->task->init(); + $this->wrapper = $this->task->getRuntimeConfigurableWrapper(); + $this->wrapper->setAttributes($attrs); + /* + Commenting this out as per thread on Premature configurate of ReuntimeConfigurables + with Matthias Pigulla: http://phing.tigris.org/servlets/ReadMsg?list=dev&msgNo=251 + + if ($this->parentWrapper !== null) { // this may not make sense only within this if-block, but it + // seems to address current use cases adequately + $this->parentWrapper->addChild($this->wrapper); + } + */ + } else { + $this->task->init(); + $configurator->configure($this->task, $attrs, $project); + } + } + + /** + * Executes the task at once if it's directly beneath the <project> tag. + */ + protected function finished() { + if ($this->task !== null && $this->target === null && $this->container === null) { + try { + $this->task->perform(); + } catch (Exception $e) { + $this->task->log($e->getMessage(), Project::MSG_ERR); + throw $e; + } + } + } + + /** + * Handles character data. + * + * @param string $data The CDATA that comes in + */ + function characters($data) { + if ($this->wrapper === null) { + $configurator = $this->configurator; + $project = $this->configurator->project; + try { // try + $configurator->addText($project, $this->task, $data); + } catch (BuildException $exc) { + throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation()); + } + } else { + $this->wrapper->addText($data); + } + } + + /** + * Checks for nested tags within the current one. Creates and calls + * handlers respectively. + * + * @param string $name The tag that comes in + * @param array $attrs Attributes the tag carries + */ + function startElement($name, $attrs) { + $project = $this->configurator->project; + if ($this->task instanceof TaskContainer) { + //print("TaskHandler::startElement() (TaskContainer) name = $name, attrs = " . implode(",",$attrs) . "\n"); + $th = new TaskHandler($this->parser, $this, $this->configurator, $this->task, $this->wrapper, $this->target); + $th->init($name, $attrs); + } else { + //print("TaskHandler::startElement() name = $name, attrs = " . implode(",",$attrs) . "\n"); + $tmp = new NestedElementHandler($this->parser, $this, $this->configurator, $this->task, $this->wrapper, $this->target); + $tmp->init($name, $attrs); + } + } +} diff --git a/buildscripts/phing/classes/phing/system/io/BufferedReader.php b/buildscripts/phing/classes/phing/system/io/BufferedReader.php new file mode 100755 index 00000000..a392f5be --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/BufferedReader.php @@ -0,0 +1,168 @@ +<?php +/* + * $Id: 98ea5952d7a41ce47ce95008e336f38758946aaa $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/system/io/Reader.php'; + +/** + * Convenience class for reading files. + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @version $Id$ + * @access public + * @see FilterReader + * @package phing.system.io + */ +class BufferedReader extends Reader { + + private $bufferSize = 0; + private $buffer = null; + private $bufferPos = 0; + + /** + * The Reader we are buffering for. + */ + private $in; + + /** + * + * @param object $reader The reader (e.g. FileReader). + * @param integer $buffsize The size of the buffer we should use for reading files. + * A large buffer ensures that most files (all scripts?) are parsed in 1 buffer. + */ + function __construct(Reader $reader, $buffsize = 65536) { + $this->in = $reader; + $this->bufferSize = $buffsize; + } + + /** + * Reads and returns a chunk of data. + * @param int $len Number of bytes to read. Default is to read configured buffer size number of bytes. + * @return mixed buffer or -1 if EOF. + */ + function read($len = null) { + + // if $len is specified, we'll use that; otherwise, use the configured buffer size. + if ($len === null) $len = $this->bufferSize; + + if ( ($data = $this->in->read($len)) !== -1 ) { + + // not all files end with a newline character, so we also need to check EOF + if (!$this->in->eof()) { + + $notValidPart = strrchr($data, "\n"); + $notValidPartSize = strlen($notValidPart); + + if ( $notValidPartSize > 1 ) { + // Block doesn't finish on a EOL + // Find the last EOL and forget all following stuff + $dataSize = strlen($data); + $validSize = $dataSize - $notValidPartSize + 1; + + $data = substr($data, 0, $validSize); + + // Rewind to the begining of the forgotten stuff. + $this->in->skip(-$notValidPartSize+1); + } + + } // if !EOF + } + return $data; + } + + function skip($n) { + return $this->in->skip($n); + } + + function reset() { + return $this->in->reset(); + } + + function close() { + return $this->in->close(); + } + + function open() { + return $this->in->open(); + } + + /** + * Read a line from input stream. + */ + function readLine() { + $line = null; + while ( ($ch = $this->readChar()) !== -1 ) { + if ( $ch === "\n" ) { + break; + } + $line .= $ch; + } + + // Warning : Not considering an empty line as an EOF + if ( $line === null && $ch !== -1 ) + return ""; + + return $line; + } + + /** + * Reads a single char from the reader. + * @return string single char or -1 if EOF. + */ + function readChar() { + + if ( $this->buffer === null ) { + // Buffer is empty, fill it ... + $read = $this->in->read($this->bufferSize); + if ($read === -1) { + $ch = -1; + } else { + $this->buffer = $read; + return $this->readChar(); // recurse + } + } else { + // Get next buffered char ... + // handle case where buffer is read-in, but is empty. The next readChar() will return -1 EOF, + // so we just return empty string (char) at this point. (Probably could also return -1 ...?) + $ch = ($this->buffer !== "") ? $this->buffer{$this->bufferPos} : ''; + $this->bufferPos++; + if ( $this->bufferPos >= strlen($this->buffer) ) { + $this->buffer = null; + $this->bufferPos = 0; + } + } + + return $ch; + } + + /** + * Returns whether eof has been reached in stream. + * This is important, because filters may want to know if the end of the file (and not just buffer) + * has been reached. + * @return boolean + */ + function eof() { + return $this->in->eof(); + } + + function getResource() { + return $this->in->getResource(); + } +} diff --git a/buildscripts/phing/classes/phing/system/io/BufferedWriter.php b/buildscripts/phing/classes/phing/system/io/BufferedWriter.php new file mode 100755 index 00000000..88520ce9 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/BufferedWriter.php @@ -0,0 +1,71 @@ +<?php +/* + * $Id: 8a155d3b04ca1a938bc22f59aba8509e0910ad33 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/Writer.php'; + +/** + * Convenience class for writing files. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.system.io + */ +class BufferedWriter extends Writer { + + /** + * The size of the buffer in kb. + */ + private $bufferSize = 0; + + /** + * @var Writer The Writer we are buffering output to. + */ + private $out; + + public function __construct(Writer $writer, $buffsize = 8192) { + $this->out = $writer; + $this->bufferSize = $buffsize; + } + + public function write($buf, $off = null, $len = null) { + return $this->out->write($buf, $off, $len); + } + + public function newLine() { + $this->write(PHP_EOL); + } + + public function getResource() { + return $this->out->getResource(); + } + + public function flush() { + $this->out->flush(); + } + + /** + * Close attached stream. + */ + public function close() { + return $this->out->close(); + } + +} diff --git a/buildscripts/phing/classes/phing/system/io/ConsoleReader.php b/buildscripts/phing/classes/phing/system/io/ConsoleReader.php new file mode 100755 index 00000000..048f1866 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/ConsoleReader.php @@ -0,0 +1,84 @@ +<?php +/* + * $Id: 7abe7afba50cc541e695c323da811186549ba1d9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/Reader.php'; + +/** + * Convenience class for reading console input. + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Matthew Hershberger <matthewh@lightsp.com> + * @version $Id$ + * @package phing.system.io + */ +class ConsoleReader extends Reader { + + function readLine() { + + $out = fgets(STDIN); // note: default maxlen is 1kb + $out = rtrim($out); + + return $out; + } + + /** + * + * @param int $len Num chars to read. + * @return string chars read or -1 if eof. + */ + function read($len = null) { + + $out = fread(STDIN, $len); + + + return $out; + // FIXME + // read by chars doesn't work (yet?) with PHP stdin. Maybe + // this is just a language feature, maybe there's a way to get + // ability to read chars w/o <enter> ? + + } + + function close() { + // STDIN is always open + } + + function open() { + // STDIN is always open + } + + /** + * Whether eof has been reached with stream. + * @return boolean + */ + function eof() { + return feof(STDIN); + } + + /** + * Returns path to file we are reading. + * @return string + */ + function getResource() { + return "console"; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileInputStream.php b/buildscripts/phing/classes/phing/system/io/FileInputStream.php new file mode 100644 index 00000000..64778860 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileInputStream.php @@ -0,0 +1,79 @@ +<?php +/* + * $Id: 9ddd30a90f5a934a1294f912ae881a4523b74e18 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/InputStream.php'; +require_once 'phing/system/io/PhingFile.php'; + +/** + * Input stream subclass for file streams. + * + * @package phing.system.io + */ +class FileInputStream extends InputStream { + + /** + * The associated file. + * @var PhingFile + */ + protected $file; + + /** + * Construct a new FileInputStream. + * + * @param PhingFile|string $file Path to the file + * @param boolean $append Whether to append (ignored) + * @throws Exception - if invalid argument specified. + * @throws IOException - if unable to open file. + */ + public function __construct($file, $append = false) { + if ($file instanceof PhingFile) { + $this->file = $file; + } elseif (is_string($file)) { + $this->file = new PhingFile($file); + } else { + throw new Exception("Invalid argument type for \$file."); + } + + $stream = @fopen($this->file->getAbsolutePath(), "rb"); + if ($stream === false) { + throw new IOException("Unable to open " . $this->file->__toString() . " for reading: " . $php_errormsg); + } + + parent::__construct($stream); + } + + /** + * Returns a string representation of the attached file. + * @return string + */ + public function __toString() { + return $this->file->getPath(); + } + + /** + * Mark is supported by FileInputStream. + * @return boolean TRUE + */ + public function markSupported() { + return true; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileOutputStream.php b/buildscripts/phing/classes/phing/system/io/FileOutputStream.php new file mode 100644 index 00000000..35457d17 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileOutputStream.php @@ -0,0 +1,71 @@ +<?php +/* + * $Id: 9c9b6bc291caf11baf9d5eeef38c50bf155b2099 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/OutputStream.php'; +require_once 'phing/system/io/PhingFile.php'; + +/** + * Output stream subclass for file streams. + * + * @package phing.system.io + */ +class FileOutputStream extends OutputStream { + + /** + * @var PhingFile The associated file. + */ + protected $file; + + /** + * Construct a new FileOutputStream. + * @param mixed $file + * @param boolean $append Whether to append bytes to end of file rather than beginning. + * @throws Exception - if invalid argument specified. + * @throws IOException - if unable to open file. + */ + public function __construct($file, $append = false) { + if ($file instanceof PhingFile) { + $this->file = $file; + } elseif (is_string($file)) { + $this->file = new PhingFile($file); + } else { + throw new Exception("Invalid argument type for \$file."); + } + if ($append) { + $stream = @fopen($this->file->getAbsolutePath(), "ab"); + } else { + $stream = @fopen($this->file->getAbsolutePath(), "wb"); + } + if ($stream === false) { + throw new IOException("Unable to open " . $this->file->__toString() . " for writing: " . $php_errormsg); + } + parent::__construct($stream); + } + + /** + * Returns a string representation of the attached file. + * @return string + */ + public function __toString() { + return $this->file->getPath(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileReader.php b/buildscripts/phing/classes/phing/system/io/FileReader.php new file mode 100644 index 00000000..43ebda69 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileReader.php @@ -0,0 +1,41 @@ +<?php +/* + * $Id: e7142ab98e9562743781ba0cc4005e08fd62ae83 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/InputStreamReader.php'; +require_once 'phing/system/io/FileInputStream.php'; + +/** + * Convenience class for reading files. + * @package phing.system.io + */ +class FileReader extends InputStreamReader { + + /** + * Construct a new FileReader. + * @param mixed $file PhingFile or string pathname. + */ + public function __construct($file) { + $in = new FileInputStream($file); + parent::__construct($in); + } + +} + diff --git a/buildscripts/phing/classes/phing/system/io/FileSystem.php b/buildscripts/phing/classes/phing/system/io/FileSystem.php new file mode 100755 index 00000000..284be830 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileSystem.php @@ -0,0 +1,840 @@ +<?php + +/* + * $Id: 235081905c0eafcd98da2fec63404fa2ebee090a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * This is an abstract class for platform specific filesystem implementations + * you have to implement each method in the platform specific filesystem implementation + * classes Your local filesytem implementation must extend this class. + * You should also use this class as a template to write your local implementation + * Some native PHP filesystem specific methods are abstracted here as well. Anyway + * you _must_ always use this methods via a PhingFile object (that by nature uses the + * *FileSystem drivers to access the real filesystem via this class using natives. + * + * FIXME: + * - Error handling reduced to min fallthrough runtime exceptions + * more precise errorhandling is done by the PhingFile class + * + * @author Charlie Killian <charlie@tizac.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.system.io + */ +abstract class FileSystem { + + /** + * @var int + */ + const BA_EXISTS = 0x01; + + /** + * @var int + */ + const BA_REGULAR = 0x02; + + /** + * @var int + */ + const BA_DIRECTORY = 0x04; + + /** + * @var int + */ + const BA_HIDDEN = 0x08; + + /** + * Instance for getFileSystem() method. + * @var FileSystem + */ + private static $fs; + + /** + * Static method to return the FileSystem singelton representing + * this platform's local filesystem driver. + * + * @return FileSystem + * @throws IOException + */ + public static function getFileSystem() { + if (self::$fs === null) { + switch(Phing::getProperty('host.fstype')) { + case 'UNIX': + include_once 'phing/system/io/UnixFileSystem.php'; + self::$fs = new UnixFileSystem(); + break; + case 'WIN32': + include_once 'phing/system/io/Win32FileSystem.php'; + self::$fs = new Win32FileSystem(); + break; + case 'WINNT': + include_once 'phing/system/io/WinNTFileSystem.php'; + self::$fs = new WinNTFileSystem(); + break; + default: + throw new IOException("Host uses unsupported filesystem, unable to proceed"); + } + } + return self::$fs; + } + + /* -- Normalization and construction -- */ + + /** + * Return the local filesystem's name-separator character. + */ + abstract function getSeparator(); + + /** + * Return the local filesystem's path-separator character. + */ + abstract function getPathSeparator(); + + /** + * Convert the given pathname string to normal form. If the string is + * already in normal form then it is simply returned. + * + * @param string $strPath + */ + abstract function normalize($strPath); + + /** + * Compute the length of this pathname string's prefix. The pathname + * string must be in normal form. + * + * @param string $pathname + */ + abstract function prefixLength($pathname); + + /** + * Resolve the child pathname string against the parent. + * Both strings must be in normal form, and the result + * will be a string in normal form. + * + * @param string $parent + * @param string $child + */ + abstract function resolve($parent, $child); + + /** + * Resolve the given abstract pathname into absolute form. Invoked by the + * getAbsolutePath and getCanonicalPath methods in the PhingFile class. + * + * @param PhingFile $f + */ + abstract function resolveFile(PhingFile $f); + + /** + * Return the parent pathname string to be used when the parent-directory + * argument in one of the two-argument PhingFile constructors is the empty + * pathname. + */ + abstract function getDefaultParent(); + + /** + * Post-process the given URI path string if necessary. This is used on + * win32, e.g., to transform "/c:/foo" into "c:/foo". The path string + * still has slash separators; code in the PhingFile class will translate them + * after this method returns. + * + * @param string $path + */ + abstract function fromURIPath($path); + + /* -- Path operations -- */ + + /** + * Tell whether or not the given abstract pathname is absolute. + * + * @param PhingFile $f + */ + abstract function isAbsolute(PhingFile $f); + + /** + * canonicalize filename by checking on disk + * @param string $strPath + * @return mixed Canonical path or false if the file doesn't exist. + */ + function canonicalize($strPath) { + return @realpath($strPath); + } + + /* -- Attribute accessors -- */ + + /** + * Return the simple boolean attributes for the file or directory denoted + * by the given abstract pathname, or zero if it does not exist or some + * other I/O error occurs. + * + * @param PhingFile $f + */ + function getBooleanAttributes($f) { + throw new IOException("getBooleanAttributes() not implemented by fs driver"); + } + + /** + * Check whether the file or directory denoted by the given abstract + * pathname may be accessed by this process. If the second argument is + * false, then a check for read access is made; if the second + * argument is true, then a check for write (not read-write) + * access is made. Return false if access is denied or an I/O error + * occurs. + * + * @param PhingFile $f + * @param boolean $write + */ + function checkAccess(PhingFile $f, $write = false) { + // we clear stat cache, its expensive to look up from scratch, + // but we need to be sure + @clearstatcache(); + + + // Shouldn't this be $f->GetAbsolutePath() ? + // And why doesn't GetAbsolutePath() work? + + $strPath = (string) $f->getPath(); + + // FIXME + // if file object does denote a file that yet not existst + // path rights are checked + if (!@file_exists($strPath) && !is_dir($strPath)) { + $strPath = $f->getParent(); + if ($strPath === null || !is_dir($strPath)) { + $strPath = Phing::getProperty("user.dir"); + } + //$strPath = dirname($strPath); + } + + if (!$write) { + return (boolean) @is_readable($strPath); + } else { + return (boolean) @is_writable($strPath); + } + } + + /** + * Whether file can be deleted. + * @param PhingFile $f + * @return boolean + */ + function canDelete(PhingFile $f) + { + clearstatcache(); + $dir = dirname($f->getAbsolutePath()); + return (bool) @is_writable($dir); + } + + /** + * Return the time at which the file or directory denoted by the given + * abstract pathname was last modified, or zero if it does not exist or + * some other I/O error occurs. + * + * @param PhingFile $f + * @return int + * @throws IOException + */ + function getLastModifiedTime(PhingFile $f) { + + if (!$f->exists()) { + return 0; + } + + @clearstatcache(); + $strPath = (string) $f->getPath(); + + if (@is_link($strPath)) { + $stats = @lstat($strPath); + + if (!isset($stats['mtime'])) { + $mtime = false; + } else { + $mtime = $stats['mtime']; + } + } else { + $mtime = @filemtime($strPath); + } + + if (false === $mtime) { + $msg = "FileSystem::getLastModifiedTime() FAILED. Can not get modified time of $strPath. $php_errormsg"; + throw new IOException($msg); + } + + return (int) $mtime; + } + + /** + * Return the length in bytes of the file denoted by the given abstract + * pathname, or zero if it does not exist, is a directory, or some other + * I/O error occurs. + * + * @param PhingFile $f + * @throws IOException + * @return int + */ + function getLength(PhingFile $f) { + $strPath = (string) $f->getAbsolutePath(); + $fs = filesize((string) $strPath); + if ($fs !== false) { + return $fs; + } else { + $msg = "FileSystem::Read() FAILED. Cannot get filesize of $strPath. $php_errormsg"; + throw new IOException($msg); + } + } + + /* -- File operations -- */ + + /** + * Create a new empty file with the given pathname. Return + * true if the file was created and false if a + * file or directory with the given pathname already exists. Throw an + * IOException if an I/O error occurs. + * + * @param string $strPathname Path of the file to be created. + * @throws IOException + * @return boolean + */ + function createNewFile($strPathname) { + if (@file_exists($strPathname)) + return false; + + // Create new file + $fp = @fopen($strPathname, "w"); + if ($fp === false) { + throw new IOException("The file \"$strPathname\" could not be created"); + } + @fclose($fp); + return true; + } + + /** + * Delete the file or directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. + * + * @param PhingFile $f + * @param boolean $recursive + * @return void + */ + function delete(PhingFile $f, $recursive = false) { + if ($f->isDirectory()) { + return $this->rmdir($f->getPath(), $recursive); + } else { + return $this->unlink($f->getPath()); + } + } + + /** + * Arrange for the file or directory denoted by the given abstract + * pathname to be deleted when Phing::shutdown is called, returning + * true if and only if the operation succeeds. + * + * @param PhingFile $f + * @throws IOException + */ + function deleteOnExit($f) { + throw new IOException("deleteOnExit() not implemented by local fs driver"); + } + + /** + * List the elements of the directory denoted by the given abstract + * pathname. Return an array of strings naming the elements of the + * directory if successful; otherwise, return <code>null</code>. + * + * @param PhingFile $f + */ + function listDir(PhingFile $f) { + $strPath = (string) $f->getAbsolutePath(); + $d = @dir($strPath); + if (!$d) { + return null; + } + $list = array(); + while($entry = $d->read()) { + if ($entry != "." && $entry != "..") { + array_push($list, $entry); + } + } + $d->close(); + unset($d); + return $list; + } + + /** + * Create a new directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. + * + * NOTE: umask() is reset to 0 while executing mkdir(), and restored afterwards + * + * @param PhingFile $f + * @param int $mode + * @return boolean + */ + function createDirectory(&$f, $mode = 0755) { + $old_umask = umask(0); + $return = @mkdir($f->getAbsolutePath(), $mode); + umask($old_umask); + return $return; + } + + /** + * Rename the file or directory denoted by the first abstract pathname to + * the second abstract pathname, returning true if and only if + * the operation succeeds. + * + * @param PhingFile $f1 abstract source file + * @param PhingFile $f2 abstract destination file + * @return void + * @throws IOException if rename cannot be performed + */ + function rename(PhingFile $f1, PhingFile $f2) { + // get the canonical paths of the file to rename + $src = $f1->getAbsolutePath(); + $dest = $f2->getAbsolutePath(); + if (false === @rename($src, $dest)) { + $msg = "Rename FAILED. Cannot rename $src to $dest. $php_errormsg"; + throw new IOException($msg); + } + } + + /** + * Set the last-modified time of the file or directory denoted by the + * given abstract pathname returning true if and only if the + * operation succeeds. + * + * @param PhingFile $f + * @param int $time + * @return void + * @throws IOException + */ + function setLastModifiedTime(PhingFile $f, $time) { + $path = $f->getPath(); + $success = @touch($path, $time); + if (!$success) { + throw new IOException("Could not touch '" . $path . "' due to: $php_errormsg"); + } + } + + /** + * Mark the file or directory denoted by the given abstract pathname as + * read-only, returning <code>true</code> if and only if the operation + * succeeds. + * + * @param PhingFile $f + * @throws IOException + */ + function setReadOnly($f) { + throw new IOException("setReadonly() not implemented by local fs driver"); + } + + /* -- Filesystem interface -- */ + + /** + * List the available filesystem roots, return array of PhingFile objects + * @throws IOException + */ + function listRoots() { + throw new IOException("listRoots() not implemented by local fs driver"); + } + + /* -- Basic infrastructure -- */ + + /** + * Compare two abstract pathnames lexicographically. + * + * @param PhingFile $f1 + * @param PhingFile $f2 + */ + function compare(PhingFile $f1, PhingFile $f2) { + throw new IOException("compare() not implemented by local fs driver"); + } + + /** + * Copy a file. + * + * @param PhingFile $src Source path and name file to copy. + * @param PhingFile $dest Destination path and name of new file. + * + * @return void + * @throws IOException if file cannot be copied. + */ + function copy(PhingFile $src, PhingFile $dest) { + global $php_errormsg; + + // Recursively copy a directory + if($src->isDirectory()) { + return $this->copyr($src->getAbsolutePath(), $dest->getAbsolutePath()); + } + + $srcPath = $src->getAbsolutePath(); + $destPath = $dest->getAbsolutePath(); + + if (false === @copy($srcPath, $destPath)) { // Copy FAILED. Log and return err. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::copy() FAILED. Cannot copy $srcPath to $destPath. $php_errormsg"; + throw new IOException($msg); + } + + try { + $dest->setMode($src->getMode()); + } catch(Exception $exc) { + // [MA] does chmod returns an error on systems that do not support it ? + // eat it up for now. + } + } + + /** + * Copy a file, or recursively copy a folder and its contents + * + * @author Aidan Lister <aidan@php.net> + * @version 1.0.1 + * @link http://aidanlister.com/repos/v/function.copyr.php + * @param string $source Source path + * @param string $dest Destination path + * @return bool Returns TRUE on success, FALSE on failure + */ + function copyr($source, $dest) + { + // Check for symlinks + if (is_link($source)) { + return symlink(readlink($source), $dest); + } + + // Simple copy for a file + if (is_file($source)) { + return copy($source, $dest); + } + + // Make destination directory + if (!is_dir($dest)) { + mkdir($dest); + } + + // Loop through the folder + $dir = dir($source); + while (false !== $entry = $dir->read()) { + // Skip pointers + if ($entry == '.' || $entry == '..') { + continue; + } + + // Deep copy directories + $this->copyr("$source/$entry", "$dest/$entry"); + } + + // Clean up + $dir->close(); + return true; + } + + /** + * Change the ownership on a file or directory. + * + * @param string $pathname Path and name of file or directory. + * @param string $user The user name or number of the file or directory. See http://us.php.net/chown + * + * @return void + * @throws Exception if operation failed. + */ + function chown($pathname, $user) { + if (false === @chown($pathname, $user)) {// FAILED. + $msg = "FileSystem::chown() FAILED. Cannot chown $pathname. User $user." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); + throw new IOException($msg); + } + } + + /** + * Change the group on a file or directory. + * + * @param string $pathname Path and name of file or directory. + * @param string $group The group of the file or directory. See http://us.php.net/chgrp + * + * @return void + * @throws IOException if operation failed. + */ + function chgrp($pathname, $group) { + if (false === @chgrp($pathname, $group)) {// FAILED. + $msg = "FileSystem::chgrp() FAILED. Cannot chown $pathname. Group $group." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); + throw new IOException($msg); + } + } + + /** + * Change the permissions on a file or directory. + * + * @param string $pathname Path and name of file or directory. + * @param int $mode The mode (permissions) of the file or + * directory. If using octal add leading 0. eg. 0777. + * Mode is affected by the umask system setting. + * + * @return void + * @throws IOException if operation failed. + */ + function chmod($pathname, $mode) { + $str_mode = decoct($mode); // Show octal in messages. + if (false === @chmod($pathname, $mode)) {// FAILED. + $msg = "FileSystem::chmod() FAILED. Cannot chmod $pathname. Mode $str_mode." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); + throw new IOException($msg); + } + } + + /** + * Locks a file and throws an Exception if this is not possible. + * + * @param PhingFile $f + * @return void + * @throws Exception + */ + function lock(PhingFile $f) { + $filename = $f->getPath(); + $fp = @fopen($filename, "w"); + $result = @flock($fp, LOCK_EX); + @fclose($fp); + if (!$result) { + throw new IOException("Could not lock file '$filename'"); + } + } + + /** + * Unlocks a file and throws an IO Error if this is not possible. + * + * @param PhingFile $f + * @throws IOException + * @return void + */ + function unlock(PhingFile $f) { + $filename = $f->getPath(); + $fp = @fopen($filename, "w"); + $result = @flock($fp, LOCK_UN); + fclose($fp); + if (!$result) { + throw new Exception("Could not unlock file '$filename'"); + } + } + + /** + * Delete a file. + * + * @param string $file Path and/or name of file to delete. + * + * @return void + * @throws IOException - if an error is encountered. + */ + function unlink($file) { + global $php_errormsg; + if (false === @unlink($file)) { + $msg = "FileSystem::unlink() FAILED. Cannot unlink '$file'. $php_errormsg"; + throw new IOException($msg); + } + } + + /** + * Symbolically link a file to another name. + * + * Currently symlink is not implemented on Windows. Don't use if the application is to be portable. + * + * @param string $target Path and/or name of file to link. + * @param string $link Path and/or name of link to be created. + * @return void + */ + function symlink($target, $link) { + + // If Windows OS then symlink() will report it is not supported in + // the build. Use this error instead of checking for Windows as the OS. + + if (false === @symlink($target, $link)) { + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::Symlink() FAILED. Cannot symlink '$target' to '$link'. $php_errormsg"; + throw new IOException($msg); + } + + } + + /** + * Set the modification and access time on a file to the present time. + * + * @param string $file Path and/or name of file to touch. + * @param int $time + * @return void + */ + function touch($file, $time = null) { + global $php_errormsg; + + if (null === $time) { + $error = @touch($file); + } else { + $error = @touch($file, $time); + } + + if (false === $error) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::touch() FAILED. Cannot touch '$file'. $php_errormsg"; + throw new Exception($msg); + } + } + + /** + * Delete an empty directory OR a directory and all of its contents. + * + * @param dir String. Path and/or name of directory to delete. + * @param children Boolean. False: don't delete directory contents. + * True: delete directory contents. + * + * @return void + */ + function rmdir($dir, $children = false) { + global $php_errormsg; + + // If children=FALSE only delete dir if empty. + if (false === $children) { + + if (false === @rmdir($dir)) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; + throw new Exception($msg); + } + + } else { // delete contents and dir. + + $handle = @opendir($dir); + + if (false === $handle) { // Error. + + $msg = "FileSystem::rmdir() FAILED. Cannot opendir() $dir. $php_errormsg"; + throw new Exception($msg); + + } else { // Read from handle. + + // Don't error on readdir(). + while (false !== ($entry = @readdir($handle))) { + + if ($entry != '.' && $entry != '..') { + + // Only add / if it isn't already the last char. + // This ONLY serves the purpose of making the Logger + // output look nice:) + + if (strpos(strrev($dir), DIRECTORY_SEPARATOR) === 0) {// there is a / + $next_entry = $dir . $entry; + } else { // no / + $next_entry = $dir . DIRECTORY_SEPARATOR . $entry; + } + + // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it + // returns 0. So use == not ===. + + // Don't error on is_dir() + if (false == @is_dir($next_entry)) { // Is file. + + try { + self::unlink($next_entry); // Delete. + } catch (Exception $e) { + $msg = "FileSystem::Rmdir() FAILED. Cannot FileSystem::Unlink() $next_entry. ". $e->getMessage(); + throw new Exception($msg); + } + + } else { // Is directory. + + try { + self::rmdir($next_entry, true); // Delete + } catch (Exception $e) { + $msg = "FileSystem::rmdir() FAILED. Cannot FileSystem::rmdir() $next_entry. ". $e->getMessage(); + throw new Exception($msg); + } + + } // end is_dir else + } // end .. if + } // end while + } // end handle if + + // Don't error on closedir() + @closedir($handle); + + if (false === @rmdir($dir)) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; + throw new Exception($msg); + } + + } + + } + + /** + * Set the umask for file and directory creation. + * + * @param mode Int. Permissions ususally in ocatal. Use leading 0 for + * octal. Number between 0 and 0777. + * + * @return void + * @throws Exception if there is an error performing operation. + */ + function umask($mode) { + global $php_errormsg; + + // CONSIDERME: + // Throw a warning if mode is 0. PHP converts illegal octal numbers to + // 0 so 0 might not be what the user intended. + + $str_mode = decoct($mode); // Show octal in messages. + + if (false === @umask($mode)) { // FAILED. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::Umask() FAILED. Value $mode. $php_errormsg"; + throw new Exception($msg); + } + } + + /** + * Compare the modified time of two files. + * + * @param file1 String. Path and name of file1. + * @param file2 String. Path and name of file2. + * + * @return Int. 1 if file1 is newer. + * -1 if file2 is newer. + * 0 if files have the same time. + * Err object on failure. + * + * @throws Exception - if cannot get modified time of either file. + */ + function compareMTimes($file1, $file2) { + + $mtime1 = filemtime($file1); + $mtime2 = filemtime($file2); + + if ($mtime1 === false) { // FAILED. Log and return err. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file1."; + throw new Exception($msg); + } elseif ($mtime2 === false) { // FAILED. Log and return err. + // Add error from php to end of log message. $php_errormsg. + $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file2."; + throw new Exception($msg); + } else { // Worked. Log and return compare. + // Compare mtimes. + if ($mtime1 == $mtime2) { + return 0; + } else { + return ($mtime1 < $mtime2) ? -1 : 1; + } // end compare + } + } + +} diff --git a/buildscripts/phing/classes/phing/system/io/FileWriter.php b/buildscripts/phing/classes/phing/system/io/FileWriter.php new file mode 100644 index 00000000..d58b0513 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FileWriter.php @@ -0,0 +1,42 @@ +<?php +/* + * $Id: 52ca0f8163c260b3f9f14cd83fa292292674a060 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/OutputStreamWriter.php'; +require_once 'phing/system/io/FileOutputStream.php'; + +/** + * Convenience class for performing file write operations. + * + * @package phing.system.io + */ +class FileWriter extends OutputStreamWriter { + + /** + * Construct a new FileWriter. + * @param mixed $file PhingFile or string pathname. + * @param boolean $append Append to existing file? + */ + function __construct($file, $append = false) { + $out = new FileOutputStream($file, $append); + parent::__construct($out); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/FilterReader.php b/buildscripts/phing/classes/phing/system/io/FilterReader.php new file mode 100644 index 00000000..527ce17f --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/FilterReader.php @@ -0,0 +1,68 @@ +<?php +/* + * $Id: 70652dbec76165f9ab0311daffde790df58eaf8e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/Reader.php'; + +/** + * Wrapper class for readers, which can be used to apply filters. + * @package phing.system.io + */ +class FilterReader extends Reader { + + /** + * @var Reader + */ + protected $in; + + function __construct(Reader $in = null) { + $this->in = $in; + } + + public function setReader(Reader $in) { + $this->in = $in; + } + + public function skip($n) { + return $this->in->skip($n); + } + + /** + * Read data from source. + * FIXME: Clean up this function signature, as it a) params aren't being used + * and b) it doesn't make much sense. + */ + public function read($len = null) { + return $this->in->read($len); + } + + public function reset() { + return $this->in->reset(); + } + + public function close() { + return $this->in->close(); + } + + function getResource() { + return $this->in->getResource(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/IOException.php b/buildscripts/phing/classes/phing/system/io/IOException.php new file mode 100644 index 00000000..8aa1465f --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/IOException.php @@ -0,0 +1,27 @@ +<?php +/* + * $Id: 8a019e4034b2236c19058ea849b12fb88d3e2f43 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Extends Exception to take advantage of methods therein. + * + * @package phing.system.io + */ +class IOException extends Exception {} diff --git a/buildscripts/phing/classes/phing/system/io/InputStream.php b/buildscripts/phing/classes/phing/system/io/InputStream.php new file mode 100644 index 00000000..f25fbe61 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/InputStream.php @@ -0,0 +1,178 @@ +<?php +/* + * $Id: 3ec0be3a0e0c81568513a11cbf4d4b453928a338 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Wrapper class for PHP stream that supports read operations. + * + * @package phing.system.io + */ +class InputStream { + + /** + * @var resource The attached PHP stream. + */ + protected $stream; + + /** + * @var int Position of stream cursor. + */ + protected $currentPosition = 0; + + /** + * @var int Marked position of stream cursor. + */ + protected $mark = 0; + + /** + * Construct a new InputStream. + * @param resource $stream Configured PHP stream for writing. + */ + public function __construct($stream) { + if (!is_resource($stream)) { + throw new IOException("Passed argument is not a valid stream."); + } + $this->stream = $stream; + } + + /** + * Skip over $n bytes. + * @param int $n + */ + public function skip($n) { + $start = $this->currentPosition; + + $ret = @fseek($this->stream, $n, SEEK_CUR); + if ( $ret === -1 ) + return -1; + + $this->currentPosition = ftell($this->stream); + + if ( $start > $this->currentPosition ) + $skipped = $start - $this->currentPosition; + else + $skipped = $this->currentPosition - $start; + + return $skipped; + } + + /** + * Read data from stream until $len chars or EOF. + * @param int $len Num chars to read. If not specified this stream will read until EOF. + * @return string chars read or -1 if eof. + */ + public function read($len = null) { + + if ($this->eof()) { + return -1; + } + + if ($len === null) { // we want to keep reading until we get an eof + $out = ""; + while(!$this->eof()) { + $out .= fread($this->stream, 8192); + $this->currentPosition = ftell($this->stream); + } + } else { + $out = fread($this->stream, $len); // adding 1 seems to ensure that next call to read() will return EOF (-1) + $this->currentPosition = ftell($this->stream); + } + + return $out; + } + + /** + * Marks the current position in this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + public function mark() { + if (!$this->markSupported()) { + throw new IOException(get_class($this) . " does not support mark() and reset() methods."); + } + $this->mark = $this->currentPosition; + } + + /** + * Whether the input stream supports mark and reset methods. + * @return boolean + */ + public function markSupported() { + return false; + } + + /** + * Repositions this stream to the position at the time the mark method was last called on this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + function reset() { + if (!$this->markSupported()) { + throw new IOException(get_class($this) . " does not support mark() and reset() methods."); + } + // goes back to last mark, by default this would be 0 (i.e. rewind file). + fseek($this->stream, SEEK_SET, $this->mark); + $this->mark = 0; + } + + /** + * Closes stream. + * @throws IOException if stream cannot be closed (note that calling close() on an already-closed stream will not raise an exception) + */ + public function close() { + if ($this->stream === null) { + return; + } + if (false === @fclose($this->stream)) { + // FAILED. + $msg = "Cannot fclose " . $this->file->__toString() . " $php_errormsg"; + throw new IOException($msg); + } + $this->stream = null; + } + + /** + * Whether eof has been reached with stream. + * @return boolean + */ + public function eof() { + return feof($this->stream); + } + + /** + * Reads a entire until EOF and places contents in passed-in variable. Stream is closed after read. + * + * @param string &$rBuffer String variable where read contents will be put. + * @return TRUE on success. + * @author Charlie Killian, charlie@tizac.com + * @throws IOException - if there is an error reading from stream. + * @deprecated - Instead, use the read() method or a BufferedReader. + */ + public function readInto(&$rBuffer) { + $rBuffer = $this->read(); + $this->close(); + } + + /** + * Returns string representation of attached stream. + * @return string + */ + public function __toString() { + return (string) $this->stream; + } +} diff --git a/buildscripts/phing/classes/phing/system/io/InputStreamReader.php b/buildscripts/phing/classes/phing/system/io/InputStreamReader.php new file mode 100644 index 00000000..a21f9f05 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/InputStreamReader.php @@ -0,0 +1,127 @@ +<?php +/* + * $Id: 823f584f1834166724cd370f91fa1bd2c66b0e94 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/Reader.php'; + +/** + * Writer class for OutputStream objects. + * + * Unlike the Java counterpart, this class does not (yet) handle + * character set transformations. This will be an important function + * of this class with move to supporting PHP6. + * + * @package phing.system.io + */ +class InputStreamReader extends Reader { + + /** + * @var InputStream + */ + protected $inStream; + + /** + * Construct a new InputStreamReader. + * @param InputStream $$inStream InputStream to read from + */ + public function __construct(InputStream $inStream) { + $this->inStream = $inStream; + } + + /** + * Close the stream. + */ + public function close() { + return $this->inStream->close(); + } + + /** + * Skip over $n bytes. + * @param int $n + */ + public function skip($n) { + return $this->inStream->skip($n); + } + + /** + * Read data from file. + * @param int $len Num chars to read. + * @return string chars read or -1 if eof. + */ + public function read($len = null) { + return $this->inStream->read($len); + } + + /** + * Marks the current position in this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + public function mark() { + $this->inStream->mark(); + } + + /** + * Whether the attached stream supports mark/reset. + * @return boolean + */ + public function markSupported() { + return $this->inStream->markSupported(); + } + + /** + * Repositions this stream to the position at the time the mark method was last called on this input stream. + * @throws IOException - if the underlying stream doesn't support this method. + */ + public function reset() { + $this->inStream->reset(); + } + + /** + * Whether eof has been reached with stream. + * @return boolean + */ + public function eof() { + return $this->inStream->eof(); + } + + /** + * Reads a entire file and stores the data in the variable + * passed by reference. + * + * @param string $file String. Path and/or name of file to read. + * @param object &$rBuffer Reference. Variable of where to put contents. + * + * @return TRUE on success. Err object on failure. + * @author Charlie Killian, charlie@tizac.com + * @deprecated Use read() or BufferedReader instead. + */ + public function readInto(&$rBuffer) { + return $this->inStream->readInto($rBuffer); + } + + /** + * Returns string representation of attached stream. + * @return string + */ + public function getResource() { + return $this->inStream->__toString(); + } +} diff --git a/buildscripts/phing/classes/phing/system/io/OutputStream.php b/buildscripts/phing/classes/phing/system/io/OutputStream.php new file mode 100644 index 00000000..09e15c0e --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/OutputStream.php @@ -0,0 +1,108 @@ +<?php +/* + * $Id: 3ef1bb9c45c0e679debf227c5e4699f88f692943 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Wrapper class for PHP stream that supports write operations. + * + * @package phing.system.io + */ +class OutputStream { + + /** + * @var resource The configured PHP stream. + */ + protected $stream; + + /** + * Construct a new OutputStream. + * @param resource $stream Configured PHP stream for writing. + */ + public function __construct($stream) { + if (!is_resource($stream)) { + throw new IOException("Passed argument is not a valid stream."); + } + $this->stream = $stream; + } + + /** + * Closes attached stream, flushing output first. + * @throws IOException if cannot close stream (note that attempting to close an already closed stream will not raise an IOException) + * @return void + */ + public function close() { + if ($this->stream === null) { + return; + } + $this->flush(); + if (false === @fclose($this->stream)) { + $msg = "Cannot close " . $this->getResource() . ": $php_errormsg"; + throw new IOException($msg); + } + $this->stream = null; + } + + /** + * Flushes stream. + * + * @throws IOException if unable to flush data (e.g. stream is not open). + */ + public function flush() { + if (false === @fflush($this->stream)) { + throw new IOException("Could not flush stream: " . $php_errormsg); + } + } + + /** + * Writes data to stream. + * + * @param string $buf Binary/character data to write. + * @param int $off (Optional) offset. + * @param int $len (Optional) number of bytes/chars to write. + * @return void + * @throws IOException - if there is an error writing to stream + */ + public function write($buf, $off = null, $len = null) { + if ( $off === null && $len === null ) { + $to_write = $buf; + } elseif ($off !== null && $len === null) { + $to_write = substr($buf, $off); + } elseif ($off === null && $len !== null) { + $to_write = substr($buf, 0, $len); + } else { + $to_write = substr($buf, $off, $len); + } + + $result = @fwrite($this->stream, $to_write); + + if ( $result === false ) { + throw new IOException("Error writing to stream."); + } + } + + /** + * Returns a string representation of the attached PHP stream. + * @return string + */ + public function __toString() { + return (string) $this->stream; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php b/buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php new file mode 100644 index 00000000..0b821e67 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/OutputStreamWriter.php @@ -0,0 +1,84 @@ +<?php +/* + * $Id: c1446fc1a19aef45f74766420ec89c2c37b32e01 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; + +/** + * Writer class for OutputStream objects. + * + * Unlike the Java counterpart, this class does not (yet) handle + * character set transformations. This will be an important function + * of this class with move to supporting PHP6. + * + * @package phing.system.io + */ +class OutputStreamWriter extends Writer { + + /** + * @var OutputStream + */ + protected $outStream; + + /** + * Construct a new OutputStreamWriter. + * @param OutputStream $outStream OutputStream to write to + */ + public function __construct(OutputStream $outStream) { + $this->outStream = $outStream; + } + + /** + * Close the stream. + */ + public function close() { + return $this->outStream->close(); + } + + /** + * Write char data to stream. + * + * @param unknown_type $buf + * @param unknown_type $off + * @param unknown_type $len + * @return unknown + */ + public function write($buf, $off = null, $len = null) { + return $this->outStream->write($buf, $off, $len); + } + + /** + * Flush output to the stream. + */ + public function flush() { + $this->outStream->flush(); + } + + /** + * Gets a string representation of attached stream resource. + * + * @return string String representation of output stream + */ + public function getResource() { + return $this->outStream->__toString(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/PhingFile.php b/buildscripts/phing/classes/phing/system/io/PhingFile.php new file mode 100755 index 00000000..871afedd --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/PhingFile.php @@ -0,0 +1,996 @@ +<?php +/* + * $Id: d2cfaf834a2cc605a3aedf37285f547010b8b4f4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/FileSystem.php'; +include_once 'phing/system/lang/NullPointerException.php'; + +/** + * An abstract representation of file and directory pathnames. + * + * @version $Id$ + * @package phing.system.io + */ +class PhingFile { + + /** separator string, static, obtained from FileSystem */ + public static $separator; + + /** path separator string, static, obtained from FileSystem (; or :)*/ + public static $pathSeparator; + + /** + * This abstract pathname's normalized pathname string. A normalized + * pathname string uses the default name-separator character and does not + * contain any duplicate or redundant separators. + */ + private $path = null; + + /** + * The length of this abstract pathname's prefix, or zero if it has no prefix. + * @var int + */ + private $prefixLength = 0; + + /** constructor */ + function __construct($arg1 = null, $arg2 = null) { + + if (self::$separator === null || self::$pathSeparator === null) { + $fs = FileSystem::getFileSystem(); + self::$separator = $fs->getSeparator(); + self::$pathSeparator = $fs->getPathSeparator(); + } + + /* simulate signature identified constructors */ + if ($arg1 instanceof PhingFile && is_string($arg2)) { + $this->_constructFileParentStringChild($arg1, $arg2); + } elseif (is_string($arg1) && ($arg2 === null)) { + $this->_constructPathname($arg1); + } elseif(is_string($arg1) && is_string($arg2)) { + $this->_constructStringParentStringChild($arg1, $arg2); + } else { + if ($arg1 === null) { + throw new NullPointerException("Argument1 to function must not be null"); + } + $this->path = (string) $arg1; + $this->prefixLength = (int) $arg2; + } + } + + /** + * Returns the length of this abstract pathname's prefix. + * + * @return int + */ + function getPrefixLength() { + return (int) $this->prefixLength; + } + + /* -- constructors not called by signature match, so we need some helpers --*/ + + /** + * + * Enter description here ... + * @param unknown_type $pathname + */ + protected function _constructPathname($pathname) { + // obtain ref to the filesystem layer + $fs = FileSystem::getFileSystem(); + + if ($pathname === null) { + throw new NullPointerException("Argument to function must not be null"); + } + + $this->path = (string) $fs->normalize($pathname); + $this->prefixLength = (int) $fs->prefixLength($this->path); + } + + /** + * + * Enter description here ... + * @param unknown_type $parent + * @param unknown_type $child + */ + protected function _constructStringParentStringChild($parent, $child = null) { + // obtain ref to the filesystem layer + $fs = FileSystem::getFileSystem(); + + if ($child === null) { + throw new NullPointerException("Argument to function must not be null"); + } + if ($parent !== null) { + if ($parent === "") { + $this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child)); + } else { + $this->path = $fs->resolve($fs->normalize($parent), $fs->normalize($child)); + } + } else { + $this->path = (string) $fs->normalize($child); + } + $this->prefixLength = (int) $fs->prefixLength($this->path); + } + + /** + * + * Enter description here ... + * @param unknown_type $parent + * @param unknown_type $child + */ + protected function _constructFileParentStringChild($parent, $child = null) { + // obtain ref to the filesystem layer + $fs = FileSystem::getFileSystem(); + + if ($child === null) { + throw new NullPointerException("Argument to function must not be null"); + } + + if ($parent !== null) { + if ($parent->path === "") { + $this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child)); + } else { + $this->path = $fs->resolve($parent->path, $fs->normalize($child)); + } + } else { + $this->path = $fs->normalize($child); + } + $this->prefixLength = $fs->prefixLength($this->path); + } + + /* -- Path-component accessors -- */ + + /** + * Returns the name of the file or directory denoted by this abstract + * pathname. This is just the last name in the pathname's name + * sequence. If the pathname's name sequence is empty, then the empty + * string is returned. + * + * @return The name of the file or directory denoted by this abstract + * pathname, or the empty string if this pathname's name sequence + * is empty + */ + function getName() { + // that's a lastIndexOf + $index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res); + if ($index < $this->prefixLength) { + return substr($this->path, $this->prefixLength); + } + return substr($this->path, $index + 1); + } + + /** + * Returns the pathname string of this abstract pathname's parent, or + * null if this pathname does not name a parent directory. + * + * The parent of an abstract pathname consists of the pathname's prefix, + * if any, and each name in the pathname's name sequence except for the last. + * If the name sequence is empty then the pathname does not name a parent + * directory. + * + * @return The pathname string of the parent directory named by this + * abstract pathname, or null if this pathname does not name a parent + */ + function getParent() { + // that's a lastIndexOf + $index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res); + if ($index < $this->prefixLength) { + if (($this->prefixLength > 0) && (strlen($this->path) > $this->prefixLength)) { + return substr($this->path, 0, $this->prefixLength); + } + return null; + } + return substr($this->path, 0, $index); + } + + /** + * Returns the abstract pathname of this abstract pathname's parent, + * or null if this pathname does not name a parent directory. + * + * The parent of an abstract pathname consists of the pathname's prefix, + * if any, and each name in the pathname's name sequence except for the + * last. If the name sequence is empty then the pathname does not name + * a parent directory. + * + * @return The abstract pathname of the parent directory named by this + * abstract pathname, or null if this pathname + * does not name a parent + */ + function getParentFile() { + $p = $this->getParent(); + if ($p === null) { + return null; + } + return new PhingFile((string) $p, (int) $this->prefixLength); + } + + /** + * Converts this abstract pathname into a pathname string. The resulting + * string uses the default name-separator character to separate the names + * in the name sequence. + * + * @return string The string form of this abstract pathname + */ + function getPath() { + return (string) $this->path; + } + + /** + * Returns path without leading basedir. + * + * @param string $basedir Base directory to strip + * + * @return string Path without basedir + * + * @uses getPath() + */ + function getPathWithoutBase($basedir) + { + if (!StringHelper::endsWith(self::$separator, $basedir)) { + $basedir .= self::$separator; + } + $path = $this->getPath(); + if (!substr($path, 0, strlen($basedir)) == $basedir) { + //path does not begin with basedir, we don't modify it + return $path; + } + return substr($path, strlen($basedir)); + } + + /** + * Tests whether this abstract pathname is absolute. The definition of + * absolute pathname is system dependent. On UNIX systems, a pathname is + * absolute if its prefix is "/". On Win32 systems, a pathname is absolute + * if its prefix is a drive specifier followed by "\\", or if its prefix + * is "\\". + * + * @return boolean true if this abstract pathname is absolute, false otherwise + */ + function isAbsolute() { + return ($this->prefixLength !== 0); + } + + + /** + * Returns the absolute pathname string of this abstract pathname. + * + * If this abstract pathname is already absolute, then the pathname + * string is simply returned as if by the getPath method. + * If this abstract pathname is the empty abstract pathname then + * the pathname string of the current user directory, which is named by the + * system property user.dir, is returned. Otherwise this + * pathname is resolved in a system-dependent way. On UNIX systems, a + * relative pathname is made absolute by resolving it against the current + * user directory. On Win32 systems, a relative pathname is made absolute + * by resolving it against the current directory of the drive named by the + * pathname, if any; if not, it is resolved against the current user + * directory. + * + * @return string The absolute pathname string denoting the same file or + * directory as this abstract pathname + * @see #isAbsolute() + */ + function getAbsolutePath() { + $fs = FileSystem::getFileSystem(); + return $fs->resolveFile($this); + } + + /** + * Returns the absolute form of this abstract pathname. Equivalent to + * getAbsolutePath. + * + * @return string The absolute abstract pathname denoting the same file or + * directory as this abstract pathname + */ + function getAbsoluteFile() { + return new PhingFile((string) $this->getAbsolutePath()); + } + + + /** + * Returns the canonical pathname string of this abstract pathname. + * + * A canonical pathname is both absolute and unique. The precise + * definition of canonical form is system-dependent. This method first + * converts this pathname to absolute form if necessary, as if by invoking the + * getAbsolutePath() method, and then maps it to its unique form in a + * system-dependent way. This typically involves removing redundant names + * such as "." and .. from the pathname, resolving symbolic links + * (on UNIX platforms), and converting drive letters to a standard case + * (on Win32 platforms). + * + * Every pathname that denotes an existing file or directory has a + * unique canonical form. Every pathname that denotes a nonexistent file + * or directory also has a unique canonical form. The canonical form of + * the pathname of a nonexistent file or directory may be different from + * the canonical form of the same pathname after the file or directory is + * created. Similarly, the canonical form of the pathname of an existing + * file or directory may be different from the canonical form of the same + * pathname after the file or directory is deleted. + * + * @return string The canonical pathname string denoting the same file or + * directory as this abstract pathname + */ + function getCanonicalPath() { + $fs = FileSystem::getFileSystem(); + return $fs->canonicalize($this->path); + } + + + /** + * Returns the canonical form of this abstract pathname. Equivalent to + * getCanonicalPath(. + * + * @return PhingFile The canonical pathname string denoting the same file or + * directory as this abstract pathname + */ + function getCanonicalFile() { + return new PhingFile($this->getCanonicalPath()); + } + + /** + * Converts this abstract pathname into a file: URL. The + * exact form of the URL is system-dependent. If it can be determined that + * the file denoted by this abstract pathname is a directory, then the + * resulting URL will end with a slash. + * + * Usage note: This method does not automatically escape + * characters that are illegal in URLs. It is recommended that new code + * convert an abstract pathname into a URL by first converting it into a + * URI, via the toURI() method, and then converting the URI + * into a URL via the URI::toURL() + * + * @return void A URL object representing the equivalent file URL + * @todo Not implemented yet + * + */ + function toURL() { + /* + // URL class not implemented yet + return new URL("file", "", $this->_slashify($this->getAbsolutePath(), $this->isDirectory())); + */ + } + + /** + * Constructs a file: URI that represents this abstract pathname. + * @todo Not implemented yet + * @return void + */ + function toURI() { + /* + $f = $this->getAbsoluteFile(); + $sp = (string) $this->slashify($f->getPath(), $f->isDirectory()); + if (StringHelper::startsWith('//', $sp)) + $sp = '//' + sp; + return new URI('file', null, $sp, null); + */ + } + + /** + * + * Enter description here ... + * @param PhingFile|string $path + * @param boolean $isDirectory + * @return string + */ + function _slashify($path, $isDirectory) { + $p = (string) $path; + + if (self::$separator !== '/') { + $p = str_replace(self::$separator, '/', $p); + } + + if (!StringHelper::startsWith('/', $p)) { + $p = '/'.$p; + } + + if (!StringHelper::endsWith('/', $p) && $isDirectory) { + $p = $p.'/'; + } + + return $p; + } + + /* -- Attribute accessors -- */ + + /** + * Tests whether the application can read the file denoted by this + * abstract pathname. + * + * @return boolean true if and only if the file specified by this + * abstract pathname exists and can be read by the + * application; false otherwise + */ + function canRead() { + $fs = FileSystem::getFileSystem(); + + if ($fs->checkAccess($this)) { + return (boolean) @is_link($this->getAbsolutePath()) || @is_readable($this->getAbsolutePath()); + } + return false; + } + + /** + * Tests whether the application can modify to the file denoted by this + * abstract pathname. + * + * @return boolean true if and only if the file system actually + * contains a file denoted by this abstract pathname and + * the application is allowed to write to the file; + * false otherwise. + */ + function canWrite() { + $fs = FileSystem::getFileSystem(); + return $fs->checkAccess($this, true); + } + + /** + * Tests whether the file denoted by this abstract pathname exists. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists; false otherwise + */ + function exists() { + clearstatcache(); + + if (is_link($this->path)) { + return true; + } else if ($this->isFile()) { + return @file_exists($this->path) || is_link($this->path); + } else { + return @is_dir($this->path); + } + } + + /** + * Tests whether the file denoted by this abstract pathname is a + * directory. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists and is a directory; + * false otherwise + */ + function isDirectory() { + clearstatcache(); + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path); + } + return @is_dir($this->path) && !@is_link($this->path); + } + + /** + * Tests whether the file denoted by this abstract pathname is a normal + * file. A file is normal if it is not a directory and, in + * addition, satisfies other system-dependent criteria. Any non-directory + * file created by a Java application is guaranteed to be a normal file. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists and is a normal file; + * false otherwise + */ + function isFile() { + clearstatcache(); + //$fs = FileSystem::getFileSystem(); + return @is_file($this->path); + } + + /** + * Tests whether the file named by this abstract pathname is a hidden + * file. The exact definition of hidden is system-dependent. On + * UNIX systems, a file is considered to be hidden if its name begins with + * a period character ('.'). On Win32 systems, a file is considered to be + * hidden if it has been marked as such in the filesystem. Currently there + * seems to be no way to dermine isHidden on Win file systems via PHP + * + * @return boolean true if and only if the file denoted by this + * abstract pathname is hidden according to the conventions of the + * underlying platform + */ + function isHidden() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path); + } + return (($fs->getBooleanAttributes($this) & $fs->BA_HIDDEN) !== 0); + } + + /** + * Tests whether the file denoted by this abstract pathname is a symbolic link. + * + * @return boolean true if and only if the file denoted by this + * abstract pathname exists and is a symbolic link; + * false otherwise + */ + public function isLink() + { + clearstatcache(); + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path); + } + return @is_link($this->path); + } + + /** + * Returns the target of the symbolic link denoted by this abstract pathname + * + * @return string the target of the symbolic link denoted by this abstract pathname + */ + public function getLinkTarget() + { + return @readlink($this->path); + } + + /** + * Returns the time that the file denoted by this abstract pathname was + * last modified. + * + * @return int An integer value representing the time the file was + * last modified, measured in milliseconds since the epoch + * (00:00:00 GMT, January 1, 1970), or 0 if the + * file does not exist or if an I/O error occurs + */ + function lastModified() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to " . $this->path); + } + return $fs->getLastModifiedTime($this); + } + + /** + * Returns the length of the file denoted by this abstract pathname. + * The return value is unspecified if this pathname denotes a directory. + * + * @return int The length, in bytes, of the file denoted by this abstract + * pathname, or 0 if the file does not exist + */ + function length() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->path."\n"); + } + return $fs->getLength($this); + } + + /** + * Convenience method for returning the contents of this file as a string. + * This method uses file_get_contents() to read file in an optimized way. + * @return string + * @throws Exception - if file cannot be read + */ + function contents() { + if (!$this->canRead() || !$this->isFile()) { + throw new IOException("Cannot read file contents!"); + } + return file_get_contents($this->getAbsolutePath()); + } + + /* -- File operations -- */ + + /** + * Atomically creates a new, empty file named by this abstract pathname if + * and only if a file with this name does not yet exist. The check for the + * existence of the file and the creation of the file if it does not exist + * are a single operation that is atomic with respect to all other + * filesystem activities that might affect the file. + * + * @return boolean true if the named file does not exist and was + * successfully created; <code>false</code> if the named file + * already exists + * @throws IOException if file can't be created + */ + function createNewFile($parents=true, $mode=0777) { + $file = FileSystem::getFileSystem()->createNewFile($this->path); + return $file; + } + + /** + * Deletes the file or directory denoted by this abstract pathname. If + * this pathname denotes a directory, then the directory must be empty in + * order to be deleted. + * + * @return boolean true if and only if the file or directory is + * successfully deleted; false otherwise + */ + function delete($recursive = false) { + $fs = FileSystem::getFileSystem(); + if ($fs->canDelete($this) !== true) { + throw new IOException("Cannot delete " . $this->path . "\n"); + } + return $fs->delete($this, $recursive); + } + + /** + * Requests that the file or directory denoted by this abstract pathname + * be deleted when php terminates. Deletion will be attempted only for + * normal termination of php and if and if only Phing::shutdown() is + * called. + * + * Once deletion has been requested, it is not possible to cancel the + * request. This method should therefore be used with care. + * + */ + function deleteOnExit() { + $fs = FileSystem::getFileSystem(); + $fs->deleteOnExit($this); + } + + /** + * Returns an array of strings naming the files and directories in the + * directory denoted by this abstract pathname. + * + * If this abstract pathname does not denote a directory, then this + * method returns null Otherwise an array of strings is + * returned, one for each file or directory in the directory. Names + * denoting the directory itself and the directory's parent directory are + * not included in the result. Each string is a file name rather than a + * complete path. + * + * There is no guarantee that the name strings in the resulting array + * will appear in any specific order; they are not, in particular, + * guaranteed to appear in alphabetical order. + * + * @param $filter string + * @return array An array of strings naming the files and directories in the + * directory denoted by this abstract pathname. The array will be + * empty if the directory is empty. Returns null if + * this abstract pathname does not denote a directory, or if an + * I/O error occurs. + */ + function listDir($filter = null) { + $fs = FileSystem::getFileSystem(); + return $fs->lister($this, $filter); + } + + /** + * + * Enter description here ... + * @param PhingFile[] $filter + */ + function listFiles($filter = null) { + $ss = $this->listDir($filter); + if ($ss === null) { + return null; + } + $n = count($ss); + $fs = array(); + for ($i = 0; $i < $n; $i++) { + $fs[$i] = new PhingFile((string)$this->path, (string)$ss[$i]); + } + return $fs; + } + + /** + * Creates the directory named by this abstract pathname, including any + * necessary but nonexistent parent directories. Note that if this + * operation fails it may have succeeded in creating some of the necessary + * parent directories. + * + * @return boolean true if and only if the directory was created, + * along with all necessary parent directories; false + * otherwise + * @throws IOException + */ + function mkdirs($mode = 0755) { + if ($this->exists()) { + return false; + } + try { + if ($this->mkdir($mode)) { + return true; + } + } catch (IOException $ioe) { + // IOException from mkdir() means that directory propbably didn't exist. + } + $parentFile = $this->getParentFile(); + return (($parentFile !== null) && ($parentFile->mkdirs($mode) && $this->mkdir($mode))); + } + + /** + * Creates the directory named by this abstract pathname. + * + * @return boolean true if and only if the directory was created; false otherwise + * @throws IOException + */ + function mkdir($mode = 0755) { + $fs = FileSystem::getFileSystem(); + + if ($fs->checkAccess(new PhingFile($this->path), true) !== true) { + throw new IOException("No write access to " . $this->getPath()); + } + return $fs->createDirectory($this, $mode); + } + + /** + * Renames the file denoted by this abstract pathname. + * + * @param PhingFile $destFile The new abstract pathname for the named file + * @return boolean true if and only if the renaming succeeded; false otherwise + */ + function renameTo(PhingFile $destFile) { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this) !== true) { + throw new IOException("No write access to ".$this->getPath()); + } + return $fs->rename($this, $destFile); + } + + /** + * Simple-copies file denoted by this abstract pathname into another + * PhingFile + * + * @param PhingFile $destFile The new abstract pathname for the named file + * @return boolean true if and only if the renaming succeeded; false otherwise + */ + function copyTo(PhingFile $destFile) { + $fs = FileSystem::getFileSystem(); + + if ($fs->checkAccess($this) !== true) { + throw new IOException("No read access to ".$this->getPath()."\n"); + } + + if ($fs->checkAccess($destFile, true) !== true) { + throw new IOException("File::copyTo() No write access to ".$destFile->getPath()); + } + return $fs->copy($this, $destFile); + } + + /** + * Sets the last-modified time of the file or directory named by this + * abstract pathname. + * + * All platforms support file-modification times to the nearest second, + * but some provide more precision. The argument will be truncated to fit + * the supported precision. If the operation succeeds and no intervening + * operations on the file take place, then the next invocation of the + * lastModified method will return the (possibly truncated) time argument + * that was passed to this method. + * + * @param int $time The new last-modified time, measured in milliseconds since + * the epoch (00:00:00 GMT, January 1, 1970) + * @return boolean true if and only if the operation succeeded; false otherwise + */ + function setLastModified($time) { + $time = (int) $time; + if ($time < 0) { + throw new Exception("IllegalArgumentException, Negative $time\n"); + } + + $fs = FileSystem::getFileSystem(); + return $fs->setLastModifiedTime($this, $time); + } + + /** + * Marks the file or directory named by this abstract pathname so that + * only read operations are allowed. After invoking this method the file + * or directory is guaranteed not to change until it is either deleted or + * marked to allow write access. Whether or not a read-only file or + * directory may be deleted depends upon the underlying system. + * + * @return boolean true if and only if the operation succeeded; false otherwise + */ + function setReadOnly() { + $fs = FileSystem::getFileSystem(); + if ($fs->checkAccess($this, true) !== true) { + // Error, no write access + throw new IOException("No write access to " . $this->getPath()); + } + return $fs->setReadOnly($this); + } + + /** + * Sets the owner of the file. + * @param mixed $user User name or number. + */ + public function setUser($user) { + $fs = FileSystem::getFileSystem(); + return $fs->chown($this->getPath(), $user); + } + + /** + * Retrieve the owner of this file. + * @return int User ID of the owner of this file. + */ + function getUser() { + return @fileowner($this->getPath()); + } + + /** + * Sets the group of the file. + * @param mixed $user User name or number. + */ + public function setGroup($group) { + $fs = FileSystem::getFileSystem(); + return $fs->chgrp($this->getPath(), $group); + } + + /** + * Retrieve the group of this file. + * @return int User ID of the owner of this file. + */ + function getGroup() { + return @filegroup($this->getPath()); + } + + /** + * Sets the mode of the file + * @param int $mode Ocatal mode. + */ + function setMode($mode) { + $fs = FileSystem::getFileSystem(); + return $fs->chmod($this->getPath(), $mode); + } + + /** + * Retrieve the mode of this file. + * @return int + */ + function getMode() { + return @fileperms($this->getPath()); + } + + /* -- Filesystem interface -- */ + + /** + * List the available filesystem roots. + * + * A particular platform may support zero or more hierarchically-organized + * file systems. Each file system has a root directory from which all + * other files in that file system can be reached. + * Windows platforms, for example, have a root directory for each active + * drive; UNIX platforms have a single root directory, namely "/". + * The set of available filesystem roots is affected by various system-level + * operations such the insertion or ejection of removable media and the + * disconnecting or unmounting of physical or virtual disk drives. + * + * This method returns an array of PhingFile objects that + * denote the root directories of the available filesystem roots. It is + * guaranteed that the canonical pathname of any file physically present on + * the local machine will begin with one of the roots returned by this + * method. + * + * The canonical pathname of a file that resides on some other machine + * and is accessed via a remote-filesystem protocol such as SMB or NFS may + * or may not begin with one of the roots returned by this method. If the + * pathname of a remote file is syntactically indistinguishable from the + * pathname of a local file then it will begin with one of the roots + * returned by this method. Thus, for example, PhingFile objects + * denoting the root directories of the mapped network drives of a Windows + * platform will be returned by this method, while PhingFile + * objects containing UNC pathnames will not be returned by this method. + * + * @return array An array of PhingFile objects denoting the available + * filesystem roots, or null if the set of roots + * could not be determined. The array will be empty if there are + * no filesystem roots. + */ + function listRoots() { + $fs = FileSystem::getFileSystem(); + return (array) $fs->listRoots(); + } + + /* -- Tempfile management -- */ + + /** + * Returns the path to the temp directory. + * @return string + */ + public static function getTempDir() { + return Phing::getProperty('php.tmpdir'); + } + + /** + * Static method that creates a unique filename whose name begins with + * $prefix and ends with $suffix in the directory $directory. $directory + * is a reference to a PhingFile Object. + * Then, the file is locked for exclusive reading/writing. + * + * @author manuel holtgrewe, grin@gmx.net + * @throws IOException + * @return PhingFile + */ + public static function createTempFile($prefix, $suffix, PhingFile $directory) { + + // quick but efficient hack to create a unique filename ;-) + $result = null; + do { + $result = new PhingFile($directory, $prefix . substr(md5(time()), 0, 8) . $suffix); + } while (file_exists($result->getPath())); + + $fs = FileSystem::getFileSystem(); + $fs->createNewFile($result->getPath()); + $fs->lock($result); + + return $result; + } + + /** + * If necessary, $File the lock on $File is removed and then the file is + * deleted + * + * @access public + */ + function removeTempFile() { + $fs = FileSystem::getFileSystem(); + // catch IO Exception + $fs->unlock($this); + $this->delete(); + } + + + /* -- Basic infrastructure -- */ + + /** + * Compares two abstract pathnames lexicographically. The ordering + * defined by this method depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Win32 + * systems it is not. + * + * @param PhingFile $file Th file whose pathname sould be compared to the pathname of this file. + * + * @return int Zero if the argument is equal to this abstract pathname, a + * value less than zero if this abstract pathname is + * lexicographically less than the argument, or a value greater + * than zero if this abstract pathname is lexicographically + * greater than the argument + */ + function compareTo(PhingFile $file) { + $fs = FileSystem::getFileSystem(); + return $fs->compare($this, $file); + } + + /** + * Tests this abstract pathname for equality with the given object. + * Returns <code>true</code> if and only if the argument is not + * <code>null</code> and is an abstract pathname that denotes the same file + * or directory as this abstract pathname. Whether or not two abstract + * pathnames are equal depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Win32 + * systems it is not. + * @return boolean + */ + function equals($obj) { + if (($obj !== null) && ($obj instanceof PhingFile)) { + return ($this->compareTo($obj) === 0); + } + return false; + } + + /** + * Backwards compatibility - @see __toString() + * + * @return string + */ + public function toString() + { + return $this->__toString(); + } + + /** + * Return string representation of the object + * + * @return string + */ + public function __toString() + { + return $this->getPath(); + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/Reader.php b/buildscripts/phing/classes/phing/system/io/Reader.php new file mode 100755 index 00000000..92159469 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Reader.php @@ -0,0 +1,91 @@ +<?php +/* + * $Id: c6154b0ec9b7789f9e3f8b961e16e1b1ada091ed $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +/** + * Abstract class for reading character streams. + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Yannick Lecaillez <yl@seasonfive.com> + * @version $Id$ + * @package phing.system.io + */ +abstract class Reader { + + /** + * Read data from source. + * + * If length is specified, then only that number of chars is read, + * otherwise stream is read until EOF. + * + * @param int $len + */ + abstract public function read($len = null); + + /** + * Close stream. + * @throws IOException if there is an error closing stream + */ + abstract public function close(); + + /** + * Returns the filename, url, etc. that is being read from. + * This is critical for, e.g., ExpatParser's ability to know + * the filename that is throwing an ExpatParserException, etc. + * @return string + */ + abstract function getResource(); + + /** + * Move stream position relative to current pos. + * @param int $n + */ + public function skip($n) {} + + /** + * Reset the current position in stream to beginning or last mark (if supported). + */ + public function reset() {} + + /** + * If supported, places a "marker" (like a bookmark) at current stream position. + * A subsequent call to reset() will move stream position back + * to last marker (if supported). + */ + public function mark() {} + + /** + * Whether marking is supported. + * @return boolean + */ + public function markSupported() { + return false; + } + + /** + * Is stream ready for reading. + * @return boolean + */ + public function ready() { + return true; + } + +} + diff --git a/buildscripts/phing/classes/phing/system/io/StringReader.php b/buildscripts/phing/classes/phing/system/io/StringReader.php new file mode 100644 index 00000000..e8b493e9 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/StringReader.php @@ -0,0 +1,84 @@ +<?php +/* + * $Id: 2fba6ccfe1849d1f94b1dd91daf51e64e05cace2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Dummy class for reading from string of characters. + * @package phing.system.io + */ +class StringReader extends Reader { + + /** + * @var string + */ + private $_string; + + /** + * @var int + */ + private $mark = 0; + + /** + * @var int + */ + private $currPos = 0; + + function __construct($string) { + $this->_string = $string; + } + + function skip($n) {} + + function read($len = null) { + if ($len === null) { + return $this->_string; + } else { + if ($this->currPos >= strlen($this->_string)) { + return -1; + } + $out = substr($this->_string, $this->currPos, $len); + $this->currPos += $len; + return $out; + } + } + + function mark() { + $this->mark = $this->currPos; + } + + function reset() { + $this->currPos = $this->mark; + } + + function close() {} + + function open() {} + + function ready() {} + + function markSupported() { + return true; + } + + function getResource() { + return '(string) "'.$this->_string . '"'; + } +} + diff --git a/buildscripts/phing/classes/phing/system/io/UnixFileSystem.php b/buildscripts/phing/classes/phing/system/io/UnixFileSystem.php new file mode 100755 index 00000000..739ff6f6 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/UnixFileSystem.php @@ -0,0 +1,302 @@ +<?php +/* + * $Id: ccfae0e7f76e6a02bfb20fc4b6f30eddf86d169f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/FileSystem.php'; + +/** + * UnixFileSystem class. This class encapsulates the basic file system functions + * for platforms using the unix (posix)-stylish filesystem. It wraps php native + * functions suppressing normal PHP error reporting and instead uses Exception + * to report and error. + * + * This class is part of a oop based filesystem abstraction and targeted to run + * on all supported php platforms. + * + * Note: For debugging turn track_errors on in the php.ini. The error messages + * and log messages from this class will then be clearer because $php_errormsg + * is passed as part of the message. + * + * FIXME: + * - Comments + * - Error handling reduced to min, error are handled by PhingFile mainly + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.system.io + */ +class UnixFileSystem extends FileSystem { + + /** + * returns OS dependant path separator char + */ + function getSeparator() { + return '/'; + } + + /** + * returns OS dependant directory separator char + */ + function getPathSeparator() { + return ':'; + } + + /** + * A normal Unix pathname contains no duplicate slashes and does not end + * with a slash. It may be the empty string. + * + * Check that the given pathname is normal. If not, invoke the real + * normalizer on the part of the pathname that requires normalization. + * This way we iterate through the whole pathname string only once. + */ + function normalize($strPathname) { + + if (!strlen($strPathname)) { + return; + } + + // Resolve home directories. We assume /home is where all home + // directories reside, b/c there is no other way to do this with + // PHP AFAIK. + if ($strPathname{0} === "~") { + if ($strPathname{1} === "/") { // like ~/foo => /home/user/foo + $strPathname = "/home/" . get_current_user() . substr($strPathname, 1); + } else { // like ~foo => /home/foo + $pos = strpos($strPathname, "/"); + $name = substr($strPathname, 1, $pos - 2); + $strPathname = "/home/" . $name . substr($strPathname, $pos); + } + } + + $n = strlen($strPathname); + $prevChar = 0; + for ($i=0; $i < $n; $i++) { + $c = $strPathname{$i}; + if (($prevChar === '/') && ($c === '/')) { + return self::normalizer($strPathname, $n, $i - 1); + } + $prevChar = $c; + } + if ($prevChar === '/') { + return self::normalizer($strPathname, $n, $n - 1); + } + return $strPathname; + } + + /** + * Normalize the given pathname, whose length is $len, starting at the given + * $offset; everything before this offset is already normal. + */ + protected function normalizer($pathname, $len, $offset) { + if ($len === 0) { + return $pathname; + } + $n = (int) $len; + while (($n > 0) && ($pathname{$n-1} === '/')) { + $n--; + } + if ($n === 0) { + return '/'; + } + $sb = ""; + + if ($offset > 0) { + $sb .= substr($pathname, 0, $offset); + } + $prevChar = 0; + for ($i = $offset; $i < $n; $i++) { + $c = $pathname{$i}; + if (($prevChar === '/') && ($c === '/')) { + continue; + } + $sb .= $c; + $prevChar = $c; + } + return $sb; + } + + /** + * Compute the length of the pathname string's prefix. The pathname + * string must be in normal form. + */ + function prefixLength($pathname) { + if (strlen($pathname === 0)) { + return 0; + } + return (($pathname{0} === '/') ? 1 : 0); + } + + /** + * Resolve the child pathname string against the parent. + * Both strings must be in normal form, and the result + * will be in normal form. + */ + function resolve($parent, $child) { + + if ($child === "") { + return $parent; + } + + if ($child{0} === '/') { + if ($parent === '/') { + return $child; + } + return $parent.$child; + } + + if ($parent === '/') { + return $parent.$child; + } + + return $parent.'/'.$child; + } + + function getDefaultParent() { + return '/'; + } + + function isAbsolute(PhingFile $f) { + return ($f->getPrefixLength() !== 0); + } + + /** + * the file resolver + */ + function resolveFile(PhingFile $f) { + // resolve if parent is a file oject only + if ($this->isAbsolute($f)) { + return $f->getPath(); + } else { + return $this->resolve(Phing::getProperty("user.dir"), $f->getPath()); + } + } + + /* -- most of the following is mapped to the php natives wrapped by FileSystem */ + + /* -- Attribute accessors -- */ + function getBooleanAttributes($f) { + //$rv = getBooleanAttributes0($f); + $name = $f->getName(); + $hidden = (strlen($name) > 0) && ($name{0} == '.'); + return ($hidden ? $this->BA_HIDDEN : 0); + } + + /** + * set file readonly on unix + */ + function setReadOnly($f) { + if ($f instanceof File) { + $strPath = (string) $f->getPath(); + $perms = (int) (@fileperms($strPath) & 0444); + return FileSystem::Chmod($strPath, $perms); + } else { + throw new Exception("IllegalArgumentType: Argument is not File"); + } + } + + /** + * compares file paths lexicographically + */ + function compare(PhingFile $f1, PhingFile $f2) { + $f1Path = $f1->getPath(); + $f2Path = $f2->getPath(); + return strcmp((string) $f1Path, (string) $f2Path); + } + + /** + * Copy a file, takes care of symbolic links + * + * @param PhingFile $src Source path and name file to copy. + * @param PhingFile $dest Destination path and name of new file. + * + * @return void + * @throws Exception if file cannot be copied. + */ + function copy(PhingFile $src, PhingFile $dest) { + global $php_errormsg; + + if (!$src->isLink()) + { + return parent::copy($src, $dest); + } + + $srcPath = $src->getAbsolutePath(); + $destPath = $dest->getAbsolutePath(); + + $linkTarget = $src->getLinkTarget(); + if (false === @symlink($linkTarget, $destPath)) + { + $msg = "FileSystem::copy() FAILED. Cannot create symlink from $destPath to $linkTarget."; + throw new Exception($msg); + } + } + + /* -- fs interface --*/ + + function listRoots() { + if (!$this->checkAccess('/', false)) { + die ("Can not access root"); + } + return array(new PhingFile("/")); + } + + /** + * returns the contents of a directory in an array + */ + function lister($f) { + $dir = @opendir($f->getAbsolutePath()); + if (!$dir) { + throw new Exception("Can't open directory " . $f->__toString()); + } + $vv = array(); + while (($file = @readdir($dir)) !== false) { + if ($file == "." || $file == "..") { + continue; + } + $vv[] = (string) $file; + } + @closedir($dir); + return $vv; + } + + function fromURIPath($p) { + if (StringHelper::endsWith("/", $p) && (strlen($p) > 1)) { + + // "/foo/" --> "/foo", but "/" --> "/" + $p = substr($p, 0, strlen($p) - 1); + + } + + return $p; + } + + /** + * Whether file can be deleted. + * @param PhingFile $f + * @return boolean + */ + function canDelete(PhingFile $f) + { + @clearstatcache(); + $dir = dirname($f->getAbsolutePath()); + return (bool) @is_writable($dir); + } + +} diff --git a/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php b/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php new file mode 100644 index 00000000..58331cde --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php @@ -0,0 +1,477 @@ +<?php +/* + * $Id: 0cb3f51d8745f08b64f6f394fc0abb84705f512e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/FileSystem.php'; + +/** + * @package phing.system.io + */ +class Win32FileSystem extends FileSystem { + + protected $slash; + protected $altSlash; + protected $semicolon; + + private static $driveDirCache = array(); + + function __construct() { + $this->slash = self::getSeparator(); + $this->semicolon = self::getPathSeparator(); + $this->altSlash = ($this->slash === '\\') ? '/' : '\\'; + } + + function isSlash($c) { + return ($c == '\\') || ($c == '/'); + } + + function isLetter($c) { + return ((ord($c) >= ord('a')) && (ord($c) <= ord('z'))) + || ((ord($c) >= ord('A')) && (ord($c) <= ord('Z'))); + } + + function slashify($p) { + if ((strlen($p) > 0) && ($p{0} != $this->slash)) { + return $this->slash.$p; + } + else { + return $p; + } + } + + /* -- Normalization and construction -- */ + + function getSeparator() { + // the ascii value of is the \ + return chr(92); + } + + function getPathSeparator() { + return ';'; + } + + /** + * A normal Win32 pathname contains no duplicate slashes, except possibly + * for a UNC prefix, and does not end with a slash. It may be the empty + * string. Normalized Win32 pathnames have the convenient property that + * the length of the prefix almost uniquely identifies the type of the path + * and whether it is absolute or relative: + * + * 0 relative to both drive and directory + * 1 drive-relative (begins with '\\') + * 2 absolute UNC (if first char is '\\'), else directory-relative (has form "z:foo") + * 3 absolute local pathname (begins with "z:\\") + */ + function normalizePrefix($strPath, $len, &$sb) { + $src = 0; + while (($src < $len) && $this->isSlash($strPath{$src})) { + $src++; + } + $c = ""; + if (($len - $src >= 2) + && $this->isLetter($c = $strPath{$src}) + && $strPath{$src + 1} === ':') { + /* Remove leading slashes if followed by drive specifier. + * This hack is necessary to support file URLs containing drive + * specifiers (e.g., "file://c:/path"). As a side effect, + * "/c:/path" can be used as an alternative to "c:/path". */ + $sb .= $c; + $sb .= ':'; + $src += 2; + } + else { + $src = 0; + if (($len >= 2) + && $this->isSlash($strPath{0}) + && $this->isSlash($strPath{1})) { + /* UNC pathname: Retain first slash; leave src pointed at + * second slash so that further slashes will be collapsed + * into the second slash. The result will be a pathname + * beginning with "\\\\" followed (most likely) by a host + * name. */ + $src = 1; + $sb.=$this->slash; + } + } + return $src; + } + + /** Normalize the given pathname, whose length is len, starting at the given + offset; everything before this offset is already normal. */ + protected function normalizer($strPath, $len, $offset) { + if ($len == 0) { + return $strPath; + } + if ($offset < 3) { + $offset = 0; //Avoid fencepost cases with UNC pathnames + } + $src = 0; + $slash = $this->slash; + $sb = ""; + + if ($offset == 0) { + // Complete normalization, including prefix + $src = $this->normalizePrefix($strPath, $len, $sb); + } else { + // Partial normalization + $src = $offset; + $sb .= substr($strPath, 0, $offset); + } + + // Remove redundant slashes from the remainder of the path, forcing all + // slashes into the preferred slash + while ($src < $len) { + $c = $strPath{$src++}; + if ($this->isSlash($c)) { + while (($src < $len) && $this->isSlash($strPath{$src})) { + $src++; + } + if ($src === $len) { + /* Check for trailing separator */ + $sn = (int) strlen($sb); + if (($sn == 2) && ($sb{1} === ':')) { + // "z:\\" + $sb .= $slash; + break; + } + if ($sn === 0) { + // "\\" + $sb .= $slash; + break; + } + if (($sn === 1) && ($this->isSlash($sb{0}))) { + /* "\\\\" is not collapsed to "\\" because "\\\\" marks + the beginning of a UNC pathname. Even though it is + not, by itself, a valid UNC pathname, we leave it as + is in order to be consistent with the win32 APIs, + which treat this case as an invalid UNC pathname + rather than as an alias for the root directory of + the current drive. */ + $sb .= $slash; + break; + } + // Path does not denote a root directory, so do not append + // trailing slash + break; + } else { + $sb .= $slash; + } + } else { + $sb.=$c; + } + } + $rv = (string) $sb; + return $rv; + } + + /** + * Check that the given pathname is normal. If not, invoke the real + * normalizer on the part of the pathname that requires normalization. + * This way we iterate through the whole pathname string only once. + * @param string $strPath + * @return string + */ + function normalize($strPath) { + $n = strlen($strPath); + $slash = $this->slash; + $altSlash = $this->altSlash; + $prev = 0; + for ($i = 0; $i < $n; $i++) { + $c = $strPath{$i}; + if ($c === $altSlash) { + return $this->normalizer($strPath, $n, ($prev === $slash) ? $i - 1 : $i); + } + if (($c === $slash) && ($prev === $slash) && ($i > 1)) { + return $this->normalizer($strPath, $n, $i - 1); + } + if (($c === ':') && ($i > 1)) { + return $this->normalizer($strPath, $n, 0); + } + $prev = $c; + } + if ($prev === $slash) { + return $this->normalizer($strPath, $n, $n - 1); + } + return $strPath; + } + + function prefixLength($strPath) { + $path = (string) $strPath; + $slash = (string) $this->slash; + $n = (int) strlen($path); + if ($n === 0) { + return 0; + } + $c0 = $path{0}; + $c1 = ($n > 1) ? $path{1} : + 0; + if ($c0 === $slash) { + if ($c1 === $slash) { + return 2; // absolute UNC pathname "\\\\foo" + } + return 1; // drive-relative "\\foo" + } + + if ($this->isLetter($c0) && ($c1 === ':')) { + if (($n > 2) && ($path{2}) === $slash) { + return 3; // Absolute local pathname "z:\\foo" */ + } + return 2; // Directory-relative "z:foo" + } + return 0; // Completely relative + } + + function resolve($parent, $child) { + $parent = (string) $parent; + $child = (string) $child; + $slash = (string) $this->slash; + + $pn = (int) strlen($parent); + if ($pn === 0) { + return $child; + } + $cn = (int) strlen($child); + if ($cn === 0) { + return $parent; + } + + $c = $child; + if (($cn > 1) && ($c{0} === $slash)) { + if ($c{1} === $slash) { + // drop prefix when child is a UNC pathname + $c = substr($c, 2); + } + else { + //Drop prefix when child is drive-relative */ + $c = substr($c, 1); + } + } + + $p = $parent; + if ($p{$pn - 1} === $slash) { + $p = substr($p, 0, $pn - 1); + } + return $p.$this->slashify($c); + } + + function getDefaultParent() { + return (string) ("".$this->slash); + } + + function fromURIPath($strPath) { + $p = (string) $strPath; + if ((strlen($p) > 2) && ($p{2} === ':')) { + + // "/c:/foo" --> "c:/foo" + $p = substr($p,1); + + // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/" + if ((strlen($p) > 3) && StringHelper::endsWith('/', $p)) { + $p = substr($p, 0, strlen($p) - 1); + } + } elseif ((strlen($p) > 1) && StringHelper::endsWith('/', $p)) { + // "/foo/" --> "/foo" + $p = substr($p, 0, strlen($p) - 1); + } + return (string) $p; + } + + + /* -- Path operations -- */ + + function isAbsolute(PhingFile $f) { + $pl = (int) $f->getPrefixLength(); + $p = (string) $f->getPath(); + return ((($pl === 2) && ($p{0} === $this->slash)) || ($pl === 3) || ($pl === 1 && $p{0} === $this->slash)); + } + + /** private */ + function _driveIndex($d) { + $d = (string) $d{0}; + if ((ord($d) >= ord('a')) && (ord($d) <= ord('z'))) { + return ord($d) - ord('a'); + } + if ((ord($d) >= ord('A')) && (ord($d) <= ord('Z'))) { + return ord($d) - ord('A'); + } + return -1; + } + + /** private */ + function _getDriveDirectory($drive) { + $drive = (string) $drive{0}; + $i = (int) $this->_driveIndex($drive); + if ($i < 0) { + return null; + } + + $s = (isset(self::$driveDirCache[$i]) ? self::$driveDirCache[$i] : null); + + if ($s !== null) { + return $s; + } + + $s = $this->_getDriveDirectory($i + 1); + self::$driveDirCache[$i] = $s; + return $s; + } + + function _getUserPath() { + //For both compatibility and security, we must look this up every time + return (string) $this->normalize(Phing::getProperty("user.dir")); + } + + function _getDrive($path) { + $path = (string) $path; + $pl = $this->prefixLength($path); + return ($pl === 3) ? substr($path, 0, 2) : null; + } + + function resolveFile(PhingFile $f) { + $path = $f->getPath(); + $pl = (int) $f->getPrefixLength(); + + if (($pl === 2) && ($path{0} === $this->slash)) { + return $path; // UNC + } + + if ($pl === 3) { + return $path; // Absolute local + } + + if ($pl === 0) { + return (string) ($this->_getUserPath().$this->slashify($path)); //Completely relative + } + + if ($pl === 1) { // Drive-relative + $up = (string) $this->_getUserPath(); + $ud = (string) $this->_getDrive($up); + if ($ud !== null) { + return (string) $ud.$path; + } + return (string) $up.$path; //User dir is a UNC path + } + + if ($pl === 2) { // Directory-relative + $up = (string) $this->_getUserPath(); + $ud = (string) $this->_getDrive($up); + if (($ud !== null) && StringHelper::startsWith($ud, $path)) { + return (string) ($up . $this->slashify(substr($path,2))); + } + $drive = (string) $path{0}; + $dir = (string) $this->_getDriveDirectory($drive); + + $np = (string) ""; + if ($dir !== null) { + /* When resolving a directory-relative path that refers to a + drive other than the current drive, insist that the caller + have read permission on the result */ + $p = (string) $drive . (':'.$dir.$this->slashify(substr($path,2))); + + if (!$this->checkAccess($p, false)) { + // FIXME + // throw security error + die("Can't resolve path $p"); + } + return $p; + } + return (string) $drive.':'.$this->slashify(substr($path,2)); //fake it + } + + throw new Exception("Unresolvable path: " . $path); + } + + /* -- most of the following is mapped to the functions mapped th php natives in FileSystem */ + + /* -- Attribute accessors -- */ + + function setReadOnly($f) { + // dunno how to do this on win + throw new Exception("WIN32FileSystem doesn't support read-only yet."); + } + + /* -- Filesystem interface -- */ + + protected function _access($path) { + if (!$this->checkAccess($path, false)) { + throw new Exception("Can't resolve path $p"); + } + return true; + } + + function _nativeListRoots() { + // FIXME + } + + function listRoots() { + $ds = _nativeListRoots(); + $n = 0; + for ($i = 0; $i < 26; $i++) { + if ((($ds >> $i) & 1) !== 0) { + if (!$this->access((string)( chr(ord('A') + $i) . ':' . $this->slash))) { + $ds &= ~(1 << $i); + } else { + $n++; + } + } + } + $fs = array(); + $j = (int) 0; + $slash = (string) $this->slash; + for ($i = 0; $i < 26; $i++) { + if ((($ds >> $i) & 1) !== 0) { + $fs[$j++] = new PhingFile(chr(ord('A') + $i) . ':' . $this->slash); + } + } + return $fs; + } + + /* -- Basic infrastructure -- */ + + /** compares file paths lexicographically */ + function compare(PhingFile $f1, PhingFile $f2) { + $f1Path = $f1->getPath(); + $f2Path = $f2->getPath(); + return (boolean) strcasecmp((string) $f1Path, (string) $f2Path); + } + + + /** + * returns the contents of a directory in an array + */ + function lister($f) { + $dir = @opendir($f->getAbsolutePath()); + if (!$dir) { + throw new Exception("Can't open directory " . $f->__toString()); + } + $vv = array(); + while (($file = @readdir($dir)) !== false) { + if ($file == "." || $file == "..") { + continue; + } + $vv[] = (string) $file; + } + @closedir($dir); + return $vv; + } + +} + + diff --git a/buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php b/buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php new file mode 100644 index 00000000..1eae49c4 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/WinNTFileSystem.php @@ -0,0 +1,34 @@ +<?php +/* + * $Id: de8f1d144dc3d34fa978937632a98625c3e5c15d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/Win32FileSystem.php'; + +/** + * FileSystem for Windows NT/2000. + * @package phing.system.io + */ +class WinNTFileSystem extends Win32FileSystem { + + /* -- class only for convenience and future use everything is inherinted --*/ + + +} + diff --git a/buildscripts/phing/classes/phing/system/io/Writer.php b/buildscripts/phing/classes/phing/system/io/Writer.php new file mode 100644 index 00000000..86fa67e9 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Writer.php @@ -0,0 +1,53 @@ +<?php +/* + * $Id: 1dbdd04d4483e88c8e409811babeaa83c47f8418 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Abstract class for writing character streams. + * + * @package phing.system.io + */ +abstract class Writer { + + /** + * Writes data to output stream. + * @param string $buf + * @param int $off + * @param int $len + */ + abstract public function write($buf, $off = null, $len = null); + + /** + * Close the stream. + * @throws IOException - if there is an error closing stream. + */ + abstract public function close(); + + /** + * Flush the stream, if supported by the stream. + */ + public function flush() {} + + /** + * Returns a string representation of resource filename, url, etc. that is being written to. + * @return string + */ + abstract public function getResource(); +} diff --git a/buildscripts/phing/classes/phing/system/lang/Character.php b/buildscripts/phing/classes/phing/system/lang/Character.php new file mode 100644 index 00000000..60285df6 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/Character.php @@ -0,0 +1,49 @@ +<?php +/* + * $Id: afe71e9fbaaa9c49e543c338b5fdca1bc7c9d198 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * @package phing.system.lang + */ +class Character { + + // this class might be extended with plenty of ordinal char constants + // and the like to support the multibyte aware datatype (char) in php + // in form of an object. + // anyway just a thought + + public static function isLetter($char) { + + if (strlen($char) !== 1) + $char = 0; + + $char = (int) ord($char); + + if ($char >= ord('A') && $char <= ord('Z')) + return true; + + if ($char >= ord('a') && $char <= ord('z')) + return true; + + return false; + } + +} + diff --git a/buildscripts/phing/classes/phing/system/lang/EventObject.php b/buildscripts/phing/classes/phing/system/lang/EventObject.php new file mode 100644 index 00000000..489a82a8 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/EventObject.php @@ -0,0 +1,52 @@ +<?php +/* + * $Id: b844b1250c5bf730b5eeaccc01bdb24ebbb336e4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * @package phing.system.lang + */ +class EventObject { + + /** The object on which the Event initially occurred. */ + protected $source; + + /** Constructs a prototypical Event. */ + function __construct($source) { + if ($source === null) { + throw new Exception("Null source"); + } + $this->source = $source; + } + + /** The object on which the Event initially occurred. */ + function getSource() { + return $this->source; + } + + /** Returns a String representation of this EventObject.*/ + function toString() { + if (method_exists($this->source, "toString")) { + return get_class($this)."[source=".$this->source->toString()."]"; + } else { + return get_class($this)."[source=".get_class($this->source)."]"; + } + } +} + diff --git a/buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php b/buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php new file mode 100644 index 00000000..5da28838 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/FileNotFoundException.php @@ -0,0 +1,26 @@ +<?php +/* + * $Id: b5edc38a7438b81c032898ccf3c2be0e83d55203 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * @package phing.system.lang + */ +class FileNotFoundException extends Exception {} + diff --git a/buildscripts/phing/classes/phing/system/lang/NullPointerException.php b/buildscripts/phing/classes/phing/system/lang/NullPointerException.php new file mode 100644 index 00000000..ccf080f5 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/NullPointerException.php @@ -0,0 +1,26 @@ +<?php +/* + * $Id: b1e0bb7b6ed5dd7391d7c251e736bba1d14ce4b5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * @package phing.system.lang + */ +class NullPointerException extends Exception {} + diff --git a/buildscripts/phing/classes/phing/system/lang/SecurityException.php b/buildscripts/phing/classes/phing/system/lang/SecurityException.php new file mode 100644 index 00000000..74013bc0 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/lang/SecurityException.php @@ -0,0 +1,26 @@ +<?php +/* + * $Id: 2df99e97af67f5f2cbe2ec930e8daddc8a10b406 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * @package phing.system.lang + */ +class SecurityException extends Exception {} + diff --git a/buildscripts/phing/classes/phing/system/util/Properties.php b/buildscripts/phing/classes/phing/system/util/Properties.php new file mode 100755 index 00000000..4ffd04fc --- /dev/null +++ b/buildscripts/phing/classes/phing/system/util/Properties.php @@ -0,0 +1,314 @@ +<?php + +/* + * $Id: 577374dcb65bb9a2614bc80f605ce49600d64778 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/FileWriter.php'; + +/** + * Convenience class for reading and writing property files. + * + * FIXME + * - Add support for arrays (separated by ',') + * + * @package phing.system.util + * @version $Id: 577374dcb65bb9a2614bc80f605ce49600d64778 $ + */ +class Properties { + + private $properties = array(); + + /** + * @var PhingFile + */ + private $file = null; + + /** + * Constructor + * + * @param array $properties + */ + function __construct($properties = NULL) + { + if (is_array($properties)) { + foreach ($properties as $key => $value) { + $this->setProperty($key, $value); + } + } + } + + /** + * Load properties from a file. + * + * @param PhingFile $file + * @return void + * @throws IOException - if unable to read file. + */ + function load(PhingFile $file) { + if ($file->canRead()) { + $this->parse($file->getPath(), false); + + $this->file = $file; + } else { + throw new IOException("Can not read file ".$file->getPath()); + } + + } + + /** + * Replaces parse_ini_file() or better_parse_ini_file(). + * Saves a step since we don't have to parse and then check return value + * before throwing an error or setting class properties. + * + * @param string $filePath + * @param boolean $processSections Whether to honor [SectionName] sections in INI file. + * @return array Properties loaded from file (no prop replacements done yet). + */ + protected function parse($filePath) { + + // load() already made sure that file is readable + // but we'll double check that when reading the file into + // an array + + if (($lines = @file($filePath)) === false) { + throw new IOException("Unable to parse contents of $filePath"); + } + + $this->properties = array(); + $sec_name = ""; + + foreach($lines as $line) { + // strip comments and leading/trailing spaces + $line = trim(preg_replace("/\s+[;#]\s.+$/", "", $line)); + + if (empty($line) || $line[0] == ';' || $line[0] == '#') { + continue; + } + + $pos = strpos($line, '='); + $property = trim(substr($line, 0, $pos)); + $value = trim(substr($line, $pos + 1)); + $this->properties[$property] = $this->inVal($value); + + } // for each line + } + + /** + * Process values when being read in from properties file. + * does things like convert "true" => true + * @param string $val Trimmed value. + * @return mixed The new property value (may be boolean, etc.) + */ + protected function inVal($val) { + if ($val === "true") { + $val = true; + } elseif ($val === "false") { + $val = false; + } + return $val; + } + + /** + * Process values when being written out to properties file. + * does things like convert true => "true" + * @param mixed $val The property value (may be boolean, etc.) + * @return string + */ + protected function outVal($val) { + if ($val === true) { + $val = "true"; + } elseif ($val === false) { + $val = "false"; + } + return $val; + } + + /** + * Create string representation that can be written to file and would be loadable using load() method. + * + * Essentially this function creates a string representation of properties that is ready to + * write back out to a properties file. This is used by store() method. + * + * @return string + */ + public function toString() { + $buf = ""; + foreach($this->properties as $key => $item) { + $buf .= $key . "=" . $this->outVal($item) . PHP_EOL; + } + return $buf; + } + + /** + * Stores current properties to specified file. + * + * @param PhingFile $file File to create/overwrite with properties. + * @param string $header Header text that will be placed (within comments) at the top of properties file. + * @return void + * @throws IOException - on error writing properties file. + */ + function store(PhingFile $file = null, $header = null) { + if ($file == null) { + $file = $this->file; + } + + if ($file == null) { + throw new IOException("Unable to write to empty filename"); + } + + // stores the properties in this object in the file denoted + // if file is not given and the properties were loaded from a + // file prior, this method stores them in the file used by load() + try { + $fw = new FileWriter($file); + if ($header !== null) { + $fw->write( "# " . $header . PHP_EOL ); + } + $fw->write($this->toString()); + $fw->close(); + } catch (IOException $e) { + throw new IOException("Error writing property file: " . $e->getMessage()); + } + } + + /** + * Returns copy of internal properties hash. + * Mostly for performance reasons, property hashes are often + * preferable to passing around objects. + * + * @return array + */ + function getProperties() { + return $this->properties; + } + + /** + * Get value for specified property. + * This is the same as get() method. + * + * @param string $prop The property name (key). + * @return mixed + * @see get() + */ + function getProperty($prop) { + if (!isset($this->properties[$prop])) { + return null; + } + return $this->properties[$prop]; + } + + /** + * Get value for specified property. + * This function exists to provide a hashtable-like interface for + * properties. + * + * @param string $prop The property name (key). + * @return mixed + * @see getProperty() + */ + function get($prop) { + if (!isset($this->properties[$prop])) { + return null; + } + return $this->properties[$prop]; + } + + /** + * Set the value for a property. + * + * @param string $key + * @param mixed $value + * @return mixed Old property value or NULL if none was set. + */ + function setProperty($key, $value) { + $oldValue = null; + if (isset($this->properties[$key])) { + $oldValue = $this->properties[$key]; + } + $this->properties[$key] = $value; + return $oldValue; + } + + /** + * Set the value for a property. + * This function exists to provide hashtable-lie + * interface for properties. + * + * @param string $key + * @param mixed $value + */ + function put($key, $value) { + return $this->setProperty($key, $value); + } + + /** + * Appends a value to a property if it already exists with a delimiter + * + * If the property does not, it just adds it. + * + * @param string $key + * @param mixed $value + * @param string $delimiter + */ + function append($key, $value, $delimiter = ',') { + $newValue = $value; + if (isset($this->properties[$key]) && !empty($this->properties[$key]) ) { + $newValue = $this->properties[$key] . $delimiter . $value; + } + $this->properties[$key] = $newValue; + } + + /** + * Same as keys() function, returns an array of property names. + * @return array + */ + function propertyNames() { + return $this->keys(); + } + + /** + * Whether loaded properties array contains specified property name. + * @return boolean + */ + function containsKey($key) { + return isset($this->properties[$key]); + } + + /** + * Returns properties keys. + * Use this for foreach() {} iterations, as this is + * faster than looping through property values. + * @return array + */ + function keys() { + return array_keys($this->properties); + } + + /** + * Whether properties list is empty. + * @return boolean + */ + function isEmpty() { + return empty($this->properties); + } + +} + diff --git a/buildscripts/phing/classes/phing/system/util/Register.php b/buildscripts/phing/classes/phing/system/util/Register.php new file mode 100755 index 00000000..56ab0e45 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/util/Register.php @@ -0,0 +1,149 @@ +<?php + +/** + * Static class to handle a slot-listening system. + * + * Unlike the slots/signals Qt model, this class manages something that is + * more like a simple hashtable, where each slot has only one value. For that + * reason "Registers" makes more sense, the reference being to CPU registers. + * + * This could be used for anything, but it's been built for a pretty specific phing + * need, and that is to allow access to dynamic values that are set by logic + * that is not represented in a build file. For exampe, we need a system for getting + * the current resource (file) that is being processed by a filterchain in a fileset. + * + * Each slot corresponds to only one read-only, dynamic-value RegisterSlot object. In + * a build.xml register slots are expressed using a syntax similar to variables: + * + * <replaceregexp> + * <regexp pattern="\n" replace="%{task.current_file}"/> + * </replaceregexp> + * + * The task/type must provide a supporting setter for the attribute: + * + * <code> + * function setListeningReplace(RegisterSlot $slot) { + * $this->replace = $slot; + * } + * + * // in main() + * if ($this->replace instanceof RegisterSlot) { + * $this->regexp->setReplace($this->replace->getValue()); + * } else { + * $this->regexp->setReplace($this->replace); + * } + * </code> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.system.util + */ +class Register { + + /** Slots that have been registered */ + private static $slots = array(); + + /** + * Returns RegisterSlot for specified key. + * + * If not slot exists a new one is created for key. + * + * @param string $key + * @return RegisterSlot + */ + public static function getSlot($key) { + if (!isset(self::$slots[$key])) { + self::$slots[$key] = new RegisterSlot($key); + } + return self::$slots[$key]; + } +} + + +/** + * Represents a slot in the register. + * + * @package phing.system.util + */ +class RegisterSlot { + + /** The name of this slot. */ + private $key; + + /** The value for this slot. */ + private $value; + + /** + * Constructs a new RegisterSlot, setting the key to passed param. + * @param string $key + */ + public function __construct($key) { + $this->key = (string) $key; + } + + /** + * Sets the key / name for this slot. + * @param string $k + */ + public function setKey($k) { + $this->key = (string) $k; + } + + /** + * Gets the key / name for this slot. + * @return string + */ + public function getKey() { + return $this->key; + } + + /** + * Sets the value for this slot. + * @param mixed + */ + public function setValue($v) { + $this->value = $v; + } + + /** + * Returns the value at this slot. + * @return mixed + */ + public function getValue() { + return $this->value; + } + + /** + * Recursively implodes an array to a comma-separated string + * @param array $arr + * @return string + */ + private function implodeArray(array $arr) { + $values = array(); + + foreach ($arr as $value) { + if (is_array($value)) { + $values[] = $this->implodeArray($value); + } else { + $values[] = $value; + } + } + + return "{" . implode(",", $values) . "}"; + } + + /** + * Returns the value at this slot as a string value. + * @return string + */ + public function __toString() + { + if (is_array($this->value)) { + return $this->implodeArray($this->value); + } else { + return (string) $this->value; + } + } + +} + diff --git a/buildscripts/phing/classes/phing/system/util/Timer.php b/buildscripts/phing/classes/phing/system/util/Timer.php new file mode 100755 index 00000000..2d2bcc01 --- /dev/null +++ b/buildscripts/phing/classes/phing/system/util/Timer.php @@ -0,0 +1,96 @@ +<?php +/* + * $Id: 085b1a92f765375e97d2c09c7569ca5747a44634 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +/** + * This class can be used to obtain the execution time of all of the scripts + * that are executed in the process of building a page. + * + * Example: + * To be done before any scripts execute: + * + * $Timer = new Timer; + * $Timer->Start_Timer(); + * + * To be done after all scripts have executed: + * + * $timer->Stop_Timer(); + * $timer->Get_Elapsed_Time(int number_of_places); + * + * @author Charles Killian + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.system.util + * @version $Id$ + */ +class Timer { + + /** start time */ + protected $stime; + + /** end time */ + protected $etime; + + /** + * This function sets the class variable $stime to the current time in + * microseconds. + * @return void + */ + public function start() { + $this->stime = $this->getMicrotime(); + } + + /** + * This function sets the class variable $etime to the current time in + * microseconds. + * @return void + */ + function stop() { + $this->etime = $this->getMicrotime(); + } + + /** + * This function returns the elapsed time in seconds. + * + * Call start_time() at the beginning of script execution and end_time() at + * the end of script execution. Then, call elapsed_time() to obtain the + * difference between start_time() and end_time(). + * + * @param $places decimal place precision of elapsed time (default is 5) + * @return string Properly formatted time. + */ + function getElapsedTime($places=5) { + $etime = $this->etime - $this->stime; + $format = "%0.".$places."f"; + return (sprintf ($format, $etime)); + } + + /** + * This function returns the current time in microseconds. + * + * @author Everett Michaud, Zend.com + * @return current time in microseconds + * @access private + */ + function getMicrotime() { + list($usec, $sec) = explode(" ", microtime()); + return ((float)$usec + (float)$sec); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/defaults.properties b/buildscripts/phing/classes/phing/tasks/defaults.properties new file mode 100755 index 00000000..32a03295 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/defaults.properties @@ -0,0 +1,145 @@ +; ------------------------------------- +; These taskdefs are loaded at startup. +; ------------------------------------- + +; Internal system tasks +; +adhoc=phing.tasks.system.AdhocTask +adhoc-task=phing.tasks.system.AdhocTaskdefTask +adhoc-type=phing.tasks.system.AdhocTypedefTask +append=phing.tasks.system.AppendTask +available=phing.tasks.system.AvailableTask +chmod=phing.tasks.system.ChmodTask +chown=phing.tasks.system.ChownTask +concat=phing.tasks.system.AppendTask +condition=phing.tasks.system.ConditionTask +copy=phing.tasks.system.CopyTask +cvs=phing.tasks.system.CvsTask +cvspass=phing.tasks.system.CvsPassTask +delete=phing.tasks.system.DeleteTask +echo=phing.tasks.system.EchoTask +exec=phing.tasks.system.ExecTask +fail=phing.tasks.system.FailTask +foreach=phing.tasks.system.ForeachTask +includepath=phing.tasks.system.IncludePathTask +input=phing.tasks.system.InputTask +mkdir=phing.tasks.system.MkdirTask +move=phing.tasks.system.MoveTask +phing=phing.tasks.system.PhingTask +phingcall=phing.tasks.system.PhingCallTask +php=phing.tasks.system.PhpEvalTask +property=phing.tasks.system.PropertyTask +propertyprompt=phing.tasks.system.PropertyPromptTask +reflexive=phing.tasks.system.ReflexiveTask +resolvepath=phing.tasks.system.ResolvePathTask +taskdef=phing.tasks.system.TaskdefTask +touch=phing.tasks.system.TouchTask +tstamp=phing.tasks.system.TstampTask +typedef=phing.tasks.system.TypedefTask +uptodate=phing.tasks.system.UpToDateTask +xslt=phing.tasks.system.XsltTask +if=phing.tasks.system.IfTask +warn=phing.tasks.system.WarnTask +import=phing.tasks.system.ImportTask +loadfile=phing.tasks.system.LoadFileTask + +; "Core" contributed tasks +; -- i.e. no taskdef needed. + +creole=phing.tasks.ext.creole.CreoleSQLExecTask +pdo=phing.tasks.ext.pdo.PDOSQLExecTask +pdosqlexec=phing.tasks.ext.pdo.PDOSQLExecTask +package-as-path=phing.tasks.ext.PackageAsPathTask +smarty=phing.tasks.ext.SmartyTask +capsule=phing.tasks.ext.CapsuleTask +tar=phing.tasks.ext.TarTask +untar=phing.tasks.ext.UntarTask +pearpkg=phing.tasks.ext.PearPackageTask +pearpkg2=phing.tasks.ext.PearPackage2Task +mail=phing.tasks.ext.MailTask +zip=phing.tasks.ext.ZipTask +unzip=phing.tasks.ext.UnzipTask +waitfor=phing.tasks.system.WaitForTask +trycatch=phing.tasks.system.TryCatchTask + +; "ext" tasks +phpdoc=phing.tasks.ext.phpdoc.PhpDocumentorTask +phpdocext=phing.tasks.ext.phpdoc.PhpDocumentorExternalTask +svnlastrevision=phing.tasks.ext.svn.SvnLastRevisionTask +svncheckout=phing.tasks.ext.svn.SvnCheckoutTask +svnexport=phing.tasks.ext.svn.SvnExportTask +svnupdate=phing.tasks.ext.svn.SvnUpdateTask +svnswitch=phing.tasks.ext.svn.SvnSwitchTask +svncopy=phing.tasks.ext.svn.SvnCopyTask +svncommit=phing.tasks.ext.svn.SvnCommitTask +svnlist=phing.tasks.ext.svn.SvnListTask +svnlog=phing.tasks.ext.svn.SvnLogTask +svninfo=phing.tasks.ext.svn.SvnInfoTask +gitinit=phing.tasks.ext.git.GitInitTask +gitclone=phing.tasks.ext.git.GitCloneTask +gitgc=phing.tasks.ext.git.GitGcTask +gitbranch=phing.tasks.ext.git.GitBranchTask +gitfetch=phing.tasks.ext.git.GitFetchTask +gitmerge=phing.tasks.ext.git.GitMergeTask +gitcheckout=phing.tasks.ext.git.GitCheckoutTask +gitpull=phing.tasks.ext.git.GitPullTask +gitpush=phing.tasks.ext.git.GitPushTask +gitlog=phing.tasks.ext.git.GitLogTask +gittag=phing.tasks.ext.git.GitTagTask +phpunit3=phing.tasks.ext.phpunit.PHPUnitTask +phpunit3report=phing.tasks.ext.phpunit.PHPUnitReportTask +phpunit=phing.tasks.ext.phpunit.PHPUnitTask +phpunitreport=phing.tasks.ext.phpunit.PHPUnitReportTask +coverage-setup=phing.tasks.ext.coverage.CoverageSetupTask +coverage-merger=phing.tasks.ext.coverage.CoverageMergerTask +coverage-report=phing.tasks.ext.coverage.CoverageReportTask +coverage-threshold=phing.tasks.ext.coverage.CoverageThresholdTask +ioncubeencoder=phing.tasks.ext.ioncube.IoncubeEncoderTask +ioncubelicense=phing.tasks.ext.ioncube.IoncubeLicenseTask +simpletest=phing.tasks.ext.simpletest.SimpleTestTask +phplint=phing.tasks.ext.PhpLintTask +xmllint=phing.tasks.ext.XmlLintTask +analyze=phing.tasks.ext.ZendCodeAnalyzerTask +zendcodeanalyzer=phing.tasks.ext.ZendCodeAnalyzerTask +jsllint=phing.tasks.ext.JslLintTask +manifest=phing.tasks.ext.ManifestTask +phpcodesniffer=phing.tasks.ext.PhpCodeSnifferTask +phpcpd=phing.tasks.ext.phpcpd.PHPCPDTask +phpmd=phing.tasks.ext.phpmd.PHPMDTask +phpdepend=phing.tasks.ext.pdepend.PhpDependTask +ftpdeploy=phing.tasks.ext.FtpDeployTask +phkpackage=phing.tasks.ext.phk.PhkPackageTask +pharpackage=phing.tasks.ext.phar.PharPackageTask +scp=phing.tasks.ext.ScpTask +; deprecate ScpSendTask +scpsend=phing.tasks.ext.ScpTask +ssh=phing.tasks.ext.SshTask +replaceregexp=phing.tasks.ext.ReplaceRegexpTask +jsmin=phing.tasks.ext.jsmin.JsMinTask +version=phing.tasks.ext.VersionTask +filehash=phing.tasks.ext.FileHashTask +filesize=phing.tasks.ext.FileSizeTask +xmlproperty=phing.tasks.ext.XmlPropertyTask +exportproperties=phing.tasks.ext.ExportPropertiesTask +http-request=phing.tasks.ext.HttpRequestTask +httpget=phing.tasks.ext.HttpGetTask +patch=phing.tasks.ext.PatchTask +dbdeploy=phing.tasks.ext.dbdeploy.DbDeployTask +symlink=phing.tasks.ext.SymlinkTask +s3get=phing.tasks.ext.Service.Amazon.S3.S3GetTask +s3put=phing.tasks.ext.Service.Amazon.S3.S3PutTask +zendguardencode=phing.tasks.ext.zendguard.ZendGuardEncodeTask +zendguardlicense=phing.tasks.ext.zendguard.ZendGuardLicenseTask +docblox=phing.tasks.ext.docblox.DocBloxTask +phpdoc2=phing.tasks.ext.phpdoc.PhpDocumentor2Task +rST=phing.tasks.ext.rSTTask +apigen=phing.tasks.ext.apigen.ApiGenTask +parallel=phing.tasks.ext.ParallelTask +symfonyconsole=phing.tasks.ext.SymfonyConsole.SymfonyConsoleTask +; liquibase +liquibase-changelog=phing.tasks.ext.liquibase.LiquibaseChangeLogTask +liquibase-dbdoc=phing.tasks.ext.liquibase.LiquibaseDbDocTask +liquibase-diff=phing.tasks.ext.liquibase.LiquibaseDiffTask +liquibase-rollback=phing.tasks.ext.liquibase.LiquibaseRollbackTask +liquibase-tag=phing.tasks.ext.liquibase.LiquibaseTagTask +liquibase-update=phing.tasks.ext.liquibase.LiquibaseUpdateTask diff --git a/buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php b/buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php new file mode 100644 index 00000000..13ccd73d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/CapsuleTask.php @@ -0,0 +1,480 @@ +<?php + +/* + * $Id: 205bc55fd1f7f36783d105ff2d0e27357282bbed $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/Task.php'; +include_once 'phing/BuildException.php'; +include_once 'phing/lib/Capsule.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * A phing task for generating output by using Capsule. + * + * This is based on the interface to TexenTask from Apache's Velocity engine. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: 205bc55fd1f7f36783d105ff2d0e27357282bbed $ + * @package phing.tasks.ext + */ +class CapsuleTask extends Task { + + /** + * Capsule "template" engine. + * @var Capsule + */ + protected $context; + + /** + * Any vars assigned via the build file. + * @var array AssignedVar[] + */ + protected $assignedVars = array(); + + /** + * This is the control template that governs the output. + * It may or may not invoke the services of worker + * templates. + * @var string + */ + protected $controlTemplate; + + /** + * This is where Velocity will look for templates + * using the file template loader. + * @var string + */ + protected $templatePath; + + /** + * This is where texen will place all the output + * that is a product of the generation process. + * @var string + */ + protected $outputDirectory; + + /** + * This is the file where the generated text + * will be placed. + * @var string + */ + protected $outputFile; + + /** + * <p> + * These are properties that are fed into the + * initial context from a properties file. This + * is simply a convenient way to set some values + * that you wish to make available in the context. + * </p> + * <p> + * These values are not critical, like the template path + * or output path, but allow a convenient way to + * set a value that may be specific to a particular + * generation task. + * </p> + * <p> + * For example, if you are generating scripts to allow + * user to automatically create a database, then + * you might want the <code>$databaseName</code> + * to be placed + * in the initial context so that it is available + * in a script that might look something like the + * following: + * <code><pre> + * #!bin/sh + * + * echo y | mysqladmin create $databaseName + * </pre></code> + * The value of <code>$databaseName</code> isn't critical to + * output, and you obviously don't want to change + * the ant task to simply take a database name. + * So initial context values can be set with + * properties file. + * + * @var array + */ + protected $contextProperties; + + // ----------------------------------------------------------------------- + // The following getters & setters are used by phing to set properties + // specified in the XML for the capsule task. + // ----------------------------------------------------------------------- + + /** + * [REQUIRED] Set the control template for the + * generating process. + * @param string $controlTemplate + * @return void + */ + public function setControlTemplate ($controlTemplate) { + $this->controlTemplate = $controlTemplate; + } + + /** + * Get the control template for the + * generating process. + * @return string + */ + public function getControlTemplate() { + return $this->controlTemplate; + } + + /** + * [REQUIRED] Set the path where Velocity will look + * for templates using the file template + * loader. + * @return void + * @throws Exception + */ + public function setTemplatePath($templatePath) { + $resolvedPath = ""; + $tok = strtok($templatePath, ","); + while ( $tok ) { + // resolve relative path from basedir and leave + // absolute path untouched. + $fullPath = $this->project->resolveFile($tok); + $cpath = $fullPath->getCanonicalPath(); + if ($cpath === false) { + $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath()); + } else { + $resolvedPath .= $cpath; + } + $tok = strtok(","); + if ( $tok ) { + $resolvedPath .= ","; + } + } + $this->templatePath = $resolvedPath; + } + + /** + * Get the path where Velocity will look + * for templates using the file template + * loader. + * @return string + */ + public function getTemplatePath() { + return $this->templatePath; + } + + /** + * [REQUIRED] Set the output directory. It will be + * created if it doesn't exist. + * @param PhingFile $outputDirectory + * @return void + * @throws Exception + */ + public function setOutputDirectory(PhingFile $outputDirectory) { + try { + if (!$outputDirectory->exists()) { + $this->log("Output directory does not exist, creating: " . $outputDirectory->getPath(),Project::MSG_VERBOSE); + if (!$outputDirectory->mkdirs()) { + throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath()); + } + } + $this->outputDirectory = $outputDirectory->getCanonicalPath(); + } catch (IOException $ioe) { + throw new BuildException($ioe); + } + } + + /** + * Get the output directory. + * @return string + */ + public function getOutputDirectory() { + return $this->outputDirectory; + } + + /** + * [REQUIRED] Set the output file for the + * generation process. + * @param string $outputFile (TODO: change this to File) + * @return void + */ + public function setOutputFile($outputFile) { + $this->outputFile = $outputFile; + } + + /** + * Get the output file for the + * generation process. + * @return string + */ + public function getOutputFile() { + return $this->outputFile; + } + + /** + * Set the context properties that will be + * fed into the initial context be the + * generating process starts. + * @param string $file + * @return void + */ + public function setContextProperties($file) { + $sources = explode(",", $file); + $this->contextProperties = new Properties(); + + // Always try to get the context properties resource + // from a file first. Templates may be taken from a JAR + // file but the context properties resource may be a + // resource in the filesystem. If this fails than attempt + // to get the context properties resource from the + // classpath. + for ($i=0, $sourcesLength=count($sources); $i < $sourcesLength; $i++) { + $source = new Properties(); + + try { + + // resolve relative path from basedir and leave + // absolute path untouched. + $fullPath = $this->project->resolveFile($sources[$i]); + $this->log("Using contextProperties file: " . $fullPath->toString()); + $source->load($fullPath); + + } catch (Exception $e) { + + throw new BuildException("Context properties file " . $sources[$i] . + " could not be found in the file system!"); + + } + + $keys = $source->keys(); + + foreach ($keys as $key) { + $name = $key; + $value = $this->project->replaceProperties($source->getProperty($name)); + $this->contextProperties->setProperty($name, $value); + } + } + } + + /** + * Get the context properties that will be + * fed into the initial context be the + * generating process starts. + * @return Properties + */ + public function getContextProperties() { + return $this->contextProperties; + } + + /** + * Creates an "AssignedVar" class. + */ + public function createAssign() { + $a = new AssignedVar(); + $this->assignedVars[] = $a; + return $a; + } + + // --------------------------------------------------------------- + // End of XML setters & getters + // --------------------------------------------------------------- + + /** + * Creates a Smarty object. + * + * @return Smarty initialized (cleared) Smarty context. + * @throws Exception the execute method will catch + * and rethrow as a <code>BuildException</code> + */ + public function initControlContext() { + $this->context->clear(); + foreach($this->assignedVars as $var) { + $this->context->put($var->getName(), $var->getValue()); + } + return $this->context; + } + + /** + * Execute the input script with Velocity + * + * @throws BuildException + * BuildExceptions are thrown when required attributes are missing. + * Exceptions thrown by Velocity are rethrown as BuildExceptions. + */ + public function main() { + + // Make sure the template path is set. + if (empty($this->templatePath)) { + throw new BuildException("The template path needs to be defined!"); + } + + // Make sure the control template is set. + if ($this->controlTemplate === null) { + throw new BuildException("The control template needs to be defined!"); + } + + // Make sure the output directory is set. + if ($this->outputDirectory === null) { + throw new BuildException("The output directory needs to be defined!"); + } + + // Make sure there is an output file. + if ($this->outputFile === null) { + throw new BuildException("The output file needs to be defined!"); + } + + // Setup Smarty runtime. + + // Smarty uses one object to store properties and to store + // the context for the template (unlike Velocity). We setup this object, calling it + // $this->context, and then initControlContext simply zeros out + // any assigned variables. + $this->context = new Capsule(); + + if ($this->templatePath !== null) { + $this->log("Using templatePath: " . $this->templatePath); + $this->context->setTemplatePath($this->templatePath); + } + + // Make sure the output directory exists, if it doesn't + // then create it. + $outputDir = new PhingFile($this->outputDirectory); + if (!$outputDir->exists()) { + $this->log("Output directory does not exist, creating: " . $outputDir->getAbsolutePath()); + $outputDir->mkdirs(); + } + + $this->context->setOutputDirectory($outputDir->getAbsolutePath()); + + $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile; + $this->log("Generating to file " . $path); + + //$writer = new FileWriter($path); + + // The generator and the output path should + // be placed in the init context here and + // not in the generator class itself. + $c = $this->initControlContext(); + + // Set any variables that need to always + // be loaded + $this->populateInitialContext($c); + + // Feed all the options into the initial + // control context so they are available + // in the control/worker templates. + if ($this->contextProperties !== null) { + + foreach($this->contextProperties->keys() as $property) { + + $value = $this->contextProperties->getProperty($property); + + // Special exception (from Texen) + // for properties ending in file.contents: + // in that case we dump the contents of the file + // as the "value" for the Property. + if (preg_match('/file\.contents$/', $property)) { + // pull in contents of file specified + + $property = substr($property, 0, strpos($property, "file.contents") - 1); + + // reset value, and then + // read in teh contents of the file into that var + $value = ""; + $f = new PhingFile($this->project->resolveFile($value)->getCanonicalPath()); + if ($f->exists()) { + $fr = new FileReader($f); + $fr->readInto($value); + } + + } // if ends with file.contents + + if (StringHelper::isBoolean($value)) { + $value = StringHelper::booleanValue($value); + } + + $c->put($property, $value); + + } // foreach property + + } // if contextProperties !== null + + try { + $this->log("Parsing control template: " . $this->controlTemplate); + $c->parse($this->controlTemplate, $path); + } catch (Exception $ioe) { + throw new BuildException("Cannot write parsed template: ". $ioe->getMessage()); + } + + $this->cleanup(); + } + + /** + * Place useful objects into the initial context. + * + * + * @param Capsule $context The context to populate, as retrieved from + * {@link #initControlContext()}. + * @return void + * @throws Exception Error while populating context. The {@link + * #main()} method will catch and rethrow as a + * <code>BuildException</code>. + */ + protected function populateInitialContext(Capsule $context) { + $this->context->put("now", strftime("%c", time())); + $this->context->put("task", $this); + } + + /** + * A hook method called at the end of {@link #execute()} which can + * be overridden to perform any necessary cleanup activities (such + * as the release of database connections, etc.). By default, + * does nothing. + * @return void + * @throws Exception Problem cleaning up. + */ + protected function cleanup() { + } +} + + +/** + * An "inner" class for holding assigned var values. + * May be need to expand beyond name/value in the future. + * + * @package phing.tasks.ext + */ +class AssignedVar { + + private $name; + private $value; + + public function setName($v) { + $this->name = $v; + } + + public function setValue($v) { + $this->value = $v; + } + + public function getName() { + return $this->name; + } + + public function getValue() { + return $this->value; + } + +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php b/buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php new file mode 100644 index 00000000..8bc64bbb --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ExportPropertiesTask.php @@ -0,0 +1,141 @@ +<?php + +/* + * $Id: 7d96a453b74edc40fdea85ba8befe6459334016d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once "phing/Task.php"; + +/** + * Saves currently defined properties into a specified file + * + * @author Andrei Serdeliuc + * @extends Task + * @version $Id: 7d96a453b74edc40fdea85ba8befe6459334016d $ + * @package phing.tasks.ext + */ +class ExportPropertiesTask extends Task +{ + /** + * Array of project properties + * + * (default value: null) + * + * @var array + * @access private + */ + private $_properties = null; + + /** + * Target file for saved properties + * + * (default value: null) + * + * @var string + * @access private + */ + private $_targetFile = null; + + /** + * Exclude properties starting with these prefixes + * + * @var array + * @access private + */ + private $_disallowedPropertyPrefixes = array( + 'host.', + 'phing.', + 'os.', + 'php.', + 'line.', + 'env.', + 'user.' + ); + + /** + * setter for _targetFile + * + * @access public + * @param string $file + * @return bool + */ + public function setTargetFile($file) + { + if(!is_dir(dirname($file))) { + throw new BuildException("Parent directory of target file doesn't exist"); + } + + if(!is_writable(dirname($file)) && (file_exists($file) && !is_writable($file))) { + throw new BuildException("Target file isn't writable"); + } + + $this->_targetFile = $file; + return true; + } + + /** + * setter for _disallowedPropertyPrefixes + * + * @access public + * @param string $file + * @return bool + */ + public function setDisallowedPropertyPrefixes($prefixes) + { + $this->_disallowedPropertyPrefixes = explode(",", $prefixes); + return true; + } + + public function main() + { + // Sets the currently declared properties + $this->_properties = $this->getProject()->getProperties(); + + if(is_array($this->_properties) && !empty($this->_properties) && null !== $this->_targetFile) { + $propertiesString = ''; + foreach($this->_properties as $propertyName => $propertyValue) { + if(!$this->isDisallowedPropery($propertyName)) { + $propertiesString .= $propertyName . "=" . $propertyValue . PHP_EOL; + } + } + + if(!file_put_contents($this->_targetFile, $propertiesString)) { + throw new BuildException('Failed writing to ' . $this->_targetFile); + } + } + } + + /** + * Checks if a property name is disallowed + * + * @access protected + * @param string $propertyName + * @return bool + */ + protected function isDisallowedPropery($propertyName) + { + foreach($this->_disallowedPropertyPrefixes as $property) { + if(substr($propertyName, 0, strlen($property)) == $property) { + return true; + } + } + + return false; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php b/buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php new file mode 100644 index 00000000..e47acc24 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ExtractBaseTask.php @@ -0,0 +1,199 @@ +<?php +/* + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; + +/** + * Base class for extracting tasks such as Unzip and Untar. + * + * @author Joakim Bodin <joakim.bodin+phing@gmail.com> + * @version $Id: 8aa7996b72792da30f1ec94174f09b9c612bcc1a $ + * @package phing.tasks.ext + * @since 2.2.0 + */ +abstract class ExtractBaseTask extends MatchingTask { + /** + * @var PhingFile $file + */ + protected $file; + /** + * @var PhingFile $todir + */ + protected $todir; + protected $removepath; + protected $filesets = array(); // all fileset objects assigned to this task + + /** + * Set to true to always extract (and possibly overwrite) + * all files from the archive + * @var boolean + */ + protected $forceExtract = false; + + /** + * Add a new fileset. + * @return FileSet + */ + public function createFileSet() { + $this->fileset = new FileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + + /** + * Set the name of the zip file to extract. + * @param PhingFile $file zip file to extract + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * This is the base directory to look in for things to zip. + * @param PhingFile $baseDir + */ + public function setToDir(PhingFile $todir) { + $this->todir = $todir; + } + + public function setRemovePath($removepath) + { + $this->removepath = $removepath; + } + + /** + * Sets the forceExtract attribute + * @param boolean $forceExtract + */ + public function setForceExtract($forceExtract) + { + $this->forceExtract = (bool) $forceExtract; + } + + /** + * do the work + * @throws BuildException + */ + public function main() { + + $this->validateAttributes(); + + $filesToExtract = array(); + if ($this->file !== null) { + if(!$this->isDestinationUpToDate($this->file)) { + $filesToExtract[] = $this->file; + } else { + $this->log('Nothing to do: ' . $this->todir->getAbsolutePath() . ' is up to date for ' . $this->file->getCanonicalPath(), Project::MSG_INFO); + } + } + + foreach($this->filesets as $compressedArchiveFileset) { + $compressedArchiveDirScanner = $compressedArchiveFileset->getDirectoryScanner($this->project); + $compressedArchiveFiles = $compressedArchiveDirScanner->getIncludedFiles(); + $compressedArchiveDir = $compressedArchiveFileset->getDir($this->project); + + foreach ($compressedArchiveFiles as $compressedArchiveFilePath) { + $compressedArchiveFile = new PhingFile($compressedArchiveDir, $compressedArchiveFilePath); + if($compressedArchiveFile->isDirectory()) + { + throw new BuildException($compressedArchiveFile->getAbsolutePath() . ' compressed archive cannot be a directory.'); + } + + if ($this->forceExtract || !$this->isDestinationUpToDate($compressedArchiveFile)) { + $filesToExtract[] = $compressedArchiveFile; + } else { + $this->log('Nothing to do: ' . $this->todir->getAbsolutePath() . ' is up to date for ' . $compressedArchiveFile->getCanonicalPath(), Project::MSG_INFO); + } + } + } + + foreach ($filesToExtract as $compressedArchiveFile) { + $this->extractArchive($compressedArchiveFile); + } + } + + abstract protected function extractArchive(PhingFile $compressedArchiveFile); + + /** + * @param array $files array of filenames + * @param PhingFile $dir + * @return boolean + */ + protected function isDestinationUpToDate(PhingFile $compressedArchiveFile) { + if (!$compressedArchiveFile->exists()) { + throw new BuildException("Could not find file " . $compressedArchiveFile->__toString() . " to extract."); + } + + $compressedArchiveContent = $this->listArchiveContent($compressedArchiveFile); + if(is_array($compressedArchiveContent)) { + + $fileSystem = FileSystem::getFileSystem(); + foreach ($compressedArchiveContent as $compressArchivePathInfo) { + $compressArchiveFilename = $compressArchivePathInfo['filename']; + if(!empty($this->removepath) && strlen($compressArchiveFilename) >= strlen($this->removepath)) + { + $compressArchiveFilename = preg_replace('/^' . $this->removepath . '/','', $compressArchiveFilename); + } + $compressArchivePath = new PhingFile($this->todir, $compressArchiveFilename); + + if(!$compressArchivePath->exists() || + $fileSystem->compareMTimes($compressedArchiveFile->getCanonicalPath(), $compressArchivePath->getCanonicalPath()) == 1) { + return false; + } + } + + } + + return true; + } + + abstract protected function listArchiveContent(PhingFile $compressedArchiveFile); + + /** + * Validates attributes coming in from XML + * + * @access private + * @return void + * @throws BuildException + */ + protected function validateAttributes() { + + if ($this->file === null && count($this->filesets) === 0) { + throw new BuildException("Specify at least one source compressed archive - a file or a fileset."); + } + + if ($this->todir === null) { + throw new BuildException("todir must be set."); + } + + if ($this->todir !== null && $this->todir->exists() && !$this->todir->isDirectory()) { + throw new BuildException("todir must be a directory."); + } + + if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) { + throw new BuildException("Compressed archive file cannot be a directory."); + } + + if ($this->file !== null && !$this->file->exists()) { + throw new BuildException("Could not find compressed archive file " . $this->file->__toString() . " to extract."); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php b/buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php new file mode 100644 index 00000000..5660e793 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/FileHashTask.php @@ -0,0 +1,147 @@ +<?php +/* + * $Id: c59aff266a03f0e2cf22dc33143f2decf2d5e05c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ +require_once 'phing/Task.php'; + +/** + * fileHash + * + * Calculate either MD5 or SHA hash value of a specified file and retun the + * value in a property + * + * @author Johan Persson <johan162@gmail.com> + * @version $Id: c59aff266a03f0e2cf22dc33143f2decf2d5e05c $ + * @package phing.tasks.ext + */ +class FileHashTask extends Task +{ + /** + * Property for File + * @var PhingFile file + */ + private $file; + + /** + * Property to be set + * @var string $property + */ + private $propertyName = "filehashvalue"; + + /** + * Specify which hash algorithm to use. + * 0 = MD5 + * 1 = SHA1 + * + * @var integer $hashtype + */ + private $hashtype=0; + + + /** + * Specify if MD5 or SHA1 hash should be used + * @param integer $type 0=MD5, 1=SHA1 + */ + public function setHashtype($type) + { + $this->hashtype = $type; + } + + /** + * Which file to calculate the hash value of + * @param PhingFile $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Set the name of the property to store the hash value in + * @param $property + * @return void + */ + public function setPropertyName($property) + { + $this->propertyName = $property; + } + + /** + * Main-Method for the Task + * + * @return void + * @throws BuildException + */ + public function main() + { + $this->checkFile(); + $this->checkPropertyName(); + + // read file + if( (int)$this->hashtype === 0 ) { + $this->log("Calculating MD5 hash from: ".$this->file); + $hashValue = md5_file($this->file,false); + } + elseif( (int)$this->hashtype === 1 ) { + $this->log("Calculating SHA1 hash from: ".$this->file); + $hashValue = sha1_file($this->file,false); + } + else { + throw new BuildException( + sprintf('[FileHash] Unknown hashtype specified %d. Must be either 0 (=MD5) or 1 (=SHA1).',$this->hashtype)); + + } + + // publish hash value + $this->project->setProperty($this->propertyName, $hashValue); + + } + + /** + * checks file attribute + * @return void + * @throws BuildException + */ + private function checkFile() + { + // check File + if ($this->file === null || + strlen($this->file) == 0) { + throw new BuildException('[FileHash] You must specify an input file.', $this->file); + } + + if( ! is_readable($this->file) ) { + throw new BuildException(sprintf('[FileHash] Input file does not exist or is not readable: %s',$this->file)); + } + + } + + /** + * checks property attribute + * @return void + * @throws BuildException + */ + private function checkPropertyName() + { + if (is_null($this->propertyName) || + strlen($this->propertyName) === 0) { + throw new BuildException('Property name for publishing hashvalue is not set'); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php b/buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php new file mode 100644 index 00000000..120197dd --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/FileSizeTask.php @@ -0,0 +1,120 @@ +<?php +/* + * $Id: 2a59c1a9b46f3fd71df0fd3b50908eff268fd630 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ +require_once 'phing/Task.php'; + +/** + * fileHash + * + * Calculate either MD5 or SHA hash value of a specified file and retun the + * value in a property + * + * @author Johan Persson <johan162@gmail.com> + * @version $Id: 2a59c1a9b46f3fd71df0fd3b50908eff268fd630 $ + * @package phing.tasks.ext + */ +class FileSizeTask extends Task +{ + /** + * Property for File + * @var PhingFile file + */ + private $file; + + /** + * Property where the file size will be stored + * @var string $property + */ + private $propertyName = "filesize"; + + /** + * Which file to calculate the file size of + * @param PhingFile $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Set the name of the property to store the file size + * @param $property + * @return void + */ + public function setPropertyName($property) + { + $this->propertyName = $property; + } + + /** + * Main-Method for the Task + * + * @return void + * @throws BuildException + */ + public function main() + { + $this->checkFile(); + $this->checkPropertyName(); + + $size = filesize($this->file); + + if( $size === false ) { + throw new BuildException(sprintf('[FileSize] Cannot determine size of file: %s',$this->file)); + + } + + // publish hash value + $this->project->setProperty($this->propertyName, $size); + + } + + /** + * checks file attribute + * @return void + * @throws BuildException + */ + private function checkFile() + { + // check File + if ($this->file === null || + strlen($this->file) == 0) { + throw new BuildException('[FileSize] You must specify an input file.', $this->file); + } + + if( ! is_readable($this->file) ) { + throw new BuildException(sprintf('[FileSize] Input file does not exist or is not readable: %s',$this->file)); + } + + } + + /** + * checks property attribute + * @return void + * @throws BuildException + */ + private function checkPropertyName() + { + if (is_null($this->propertyName) || + strlen($this->propertyName) === 0) { + throw new BuildException('[FileSize] Property name for publishing file size is not set'); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php b/buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php new file mode 100644 index 00000000..136c0ac7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/FtpDeployTask.php @@ -0,0 +1,233 @@ +<?php +/** + * $Id: 87063ecf88b18eae74c2bca3918a1b4ac9f52807 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * FtpDeployTask + * + * Deploys a set of files to a remote FTP server. + * + * + * Example usage: + * <ftpdeploy host="host" port="21" username="user" password="password" dir="public_html" mode="ascii" clearfirst="true"> + * <fileset dir="."> + * <include name="**"/> + * <exclude name="phing"/> + * <exclude name="build.xml"/> + * <exclude name="images/**.png"/> + * <exclude name="images/**.gif"/> + * <exclude name="images/**.jpg"/> + * </fileset> + * </ftpdeploy> + * + * @author Jorrit Schippers <jorrit at ncode dot nl> + * @version $Id: 87063ecf88b18eae74c2bca3918a1b4ac9f52807 $ + * @since 2.3.1 + * @package phing.tasks.ext + */ +class FtpDeployTask extends Task +{ + private $host = null; + private $port = 21; + private $username = null; + private $password = null; + private $dir = null; + private $filesets; + private $completeDirMap; + private $mode = FTP_BINARY; + private $clearFirst = false; + private $passive = false; + + protected $logLevel = Project::MSG_VERBOSE; + + public function __construct() { + $this->filesets = array(); + $this->completeDirMap = array(); + } + + public function setHost($host) { + $this->host = $host; + } + + public function setPort($port) { + $this->port = (int) $port; + } + + public function setUsername($username) { + $this->username = $username; + } + + public function setPassword($password) { + $this->password = $password; + } + + public function setDir($dir) { + $this->dir = $dir; + } + + public function setMode($mode) { + switch(strtolower($mode)) { + case 'ascii': + $this->mode = FTP_ASCII; + break; + case 'binary': + case 'bin': + $this->mode = FTP_BINARY; + break; + } + } + + public function setPassive($passive) + { + $this->passive = (bool) $passive; + } + + public function setClearFirst($clearFirst) { + $this->clearFirst = (bool) $clearFirst; + } + + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Set level of log messages generated (default = info) + * @param string $level + */ + public function setLevel($level) + { + switch ($level) + { + case "error": $this->logLevel = Project::MSG_ERR; break; + case "warning": $this->logLevel = Project::MSG_WARN; break; + case "info": $this->logLevel = Project::MSG_INFO; break; + case "verbose": $this->logLevel = Project::MSG_VERBOSE; break; + case "debug": $this->logLevel = Project::MSG_DEBUG; break; + } + } + + /** + * The init method: check if Net_FTP is available + */ + public function init() { + require_once 'PEAR.php'; + + $paths = explode(PATH_SEPARATOR, get_include_path()); + foreach($paths as $path) { + if(file_exists($path.DIRECTORY_SEPARATOR.'Net'.DIRECTORY_SEPARATOR.'FTP.php')) { + return true; + } + } + throw new BuildException('The FTP Deploy task requires the Net_FTP PEAR package.'); + } + + /** + * The main entry point method. + */ + public function main() { + $project = $this->getProject(); + + require_once 'Net/FTP.php'; + $ftp = new Net_FTP($this->host, $this->port); + $ret = $ftp->connect(); + if(@PEAR::isError($ret)) { + throw new BuildException('Could not connect to FTP server '.$this->host.' on port '.$this->port.': '.$ret->getMessage()); + } else { + $this->log('Connected to FTP server ' . $this->host . ' on port ' . $this->port, $this->logLevel); + } + + $ret = $ftp->login($this->username, $this->password); + if(@PEAR::isError($ret)) { + throw new BuildException('Could not login to FTP server '.$this->host.' on port '.$this->port.' with username '.$this->username.': '.$ret->getMessage()); + } else { + $this->log('Logged in to FTP server with username ' . $this->username, $this->logLevel); + } + + if ($this->passive) { + $this->log('Setting passive mode', $this->logLevel); + $ret = $ftp->setPassive(); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not set PASSIVE mode: '.$ret->getMessage()); + } + } + + // append '/' to the end if necessary + $dir = substr($this->dir, -1) == '/' ? $this->dir : $this->dir.'/'; + + if($this->clearFirst) { + // TODO change to a loop through all files and directories within current directory + $this->log('Clearing directory '.$dir, $this->logLevel); + $ftp->rm($dir, true); + } + + // Create directory just in case + $ret = $ftp->mkdir($dir, true); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not create directory '.$dir.': '.$ret->getMessage()); + } + + $ret = $ftp->cd($dir); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not change to directory '.$dir.': '.$ret->getMessage()); + } else { + $this->log('Changed directory ' . $dir, $this->logLevel); + } + + $fs = FileSystem::getFileSystem(); + $convert = $fs->getSeparator() == '\\'; + + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + foreach($srcDirs as $dirname) { + if($convert) + $dirname = str_replace('\\', '/', $dirname); + $this->log('Will create directory '.$dirname, $this->logLevel); + $ret = $ftp->mkdir($dirname, true); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not create directory '.$dirname.': '.$ret->getMessage()); + } + } + foreach($srcFiles as $filename) { + $file = new PhingFile($fromDir->getAbsolutePath(), $filename); + if($convert) + $filename = str_replace('\\', '/', $filename); + $this->log('Will copy '.$file->getCanonicalPath().' to '.$filename, $this->logLevel); + $ret = $ftp->put($file->getCanonicalPath(), $filename, true, $this->mode); + if(@PEAR::isError($ret)) { + $ftp->disconnect(); + throw new BuildException('Could not deploy file '.$filename.': '.$ret->getMessage()); + } + } + } + + $ftp->disconnect(); + $this->log('Disconnected from FTP server', $this->logLevel); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php b/buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php new file mode 100755 index 00000000..114b80a1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/HttpGetTask.php @@ -0,0 +1,170 @@ +<?php +/* + * $Id: f3fa317b72e2f70f1e483fa49dbf089094e2a476 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * A HTTP request task. + * Making an HTTP request and try to match the response against an provided + * regular expression. + * + * @package phing.tasks.ext + * @author Ole Markus With <o.with@sportradar.com> + * @version $Id: f3fa317b72e2f70f1e483fa49dbf089094e2a476 $ + */ +class HttpGetTask extends Task +{ + /** + * Holds the request URL + * + * @var string + */ + protected $url = null; + + /** + * Holds the filename to store the output in + * + * @var string + */ + protected $filename = null; + + /** + * Holds the save location + * + * @var string + */ + protected $dir = null; + + /** + * Holds the proxy + * + * @var string + */ + protected $_proxy = null; + + /** + * Load the necessary environment for running this task. + * + * @throws BuildException + */ + public function init() + { + @include_once 'HTTP/Request2.php'; + + if (! class_exists('HTTP_Request2')) { + throw new BuildException( + 'HttpRequestTask depends on HTTP_Request2 being installed ' + . 'and on include_path.', + $this->getLocation() + ); + } + } + + + /** + * Make the GET request + * + * @throws BuildException + */ + public function main() + { + if (!isset($this->url)) { + throw new BuildException("Missing attribute 'url'"); + } + + if (!isset($this->dir)) { + throw new BuildException("Missing attribute 'dir'"); + } + + $config = array(); + if (isset($this->_proxy) && $url = parse_url($this->_proxy)) { + $config['proxy_user'] = $url['user']; + $config['proxy_password'] = $url['pass']; + $config['proxy_host'] = $url['host']; + $config['proxy_port'] = $url['port']; + } + + $this->log("Fetching " . $this->url); + + $request = new HTTP_Request2($this->url, '', $config); + $response = $request->send(); + if ($response->getStatus() != 200) { + throw new BuildException("Request unsuccessful. Response from server: " . $response->getStatus() . " " . $response->getReasonPhrase()); + } + + $content = $response->getBody(); + $disposition = $response->getHeader('content-disposition'); + + if ($this->filename) { + $filename = $this->filename; + } elseif ($disposition && 0 == strpos($disposition, 'attachment') + && preg_match('/filename="([^"]+)"/', $disposition, $m)) { + $filename = basename($m[1]); + } else { + $filename = basename(parse_url($this->url, PHP_URL_PATH)); + } + + if (!is_writable($this->dir)) { + throw new BuildException("Cannot write to directory: " . $this->dir); + } + + $filename = $this->dir . "/" . $filename; + file_put_contents($filename, $content); + + $this->log("Contents from " . $this->url . " saved to $filename"); + } + + /** + * Sets the request URL + * + * @param string $url + */ + public function setUrl($url) { + $this->url = $url; + } + + /** + * Sets the filename to store the output in + * + * @param string $filename + */ + public function setFilename($filename) { + $this->filename = $filename; + } + + /** + * Sets the save location + * + * @param string $dir + */ + public function setDir($dir) { + $this->dir = $dir; + } + + /** + * Sets the proxy + * + * @param string $proxy + */ + public function setProxy($proxy) { + $this->_proxy = $proxy; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php b/buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php new file mode 100644 index 00000000..e6f15075 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/HttpRequestTask.php @@ -0,0 +1,286 @@ +<?php +/* + * $Id: 495c02bc3a90d24694d8a4bf2d43ac077e0f9ec6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * A HTTP request task. + * Making an HTTP request and try to match the response against an provided + * regular expression. + * + * @package phing.tasks.ext + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 495c02bc3a90d24694d8a4bf2d43ac077e0f9ec6 $ + * @since 2.4.1 + */ +class HttpRequestTask extends Task +{ + /** + * Holds the request URL + * + * @var string + */ + protected $_url = null; + + /** + * Holds the regular expression that should match the response + * + * @var string + */ + protected $_responseRegex = ''; + + /** + * Whether to enable detailed logging + * + * @var boolean + */ + protected $_verbose = false; + + /** + * Holds additional header data + * + * @var array<Parameter> + */ + protected $_headers = array(); + + /** + * Holds additional config data for HTTP_Request2 + * + * @var array<Parameter> + */ + protected $_configData = array(); + + /** + * Holds the authentication user name + * + * @var string + */ + protected $_authUser = null; + + /** + * Holds the authentication password + * + * @var string + */ + protected $_authPassword = ''; + + /** + * Holds the authentication scheme + * + * @var string + */ + protected $_authScheme; + + /** + * Holds the events that will be logged + * + * @var array<string> + */ + protected $_observerEvents = array( + 'connect', + 'sentHeaders', + 'sentBodyPart', + 'receivedHeaders', + 'receivedBody', + 'disconnect', + ); + + /** + * Sets the request URL + * + * @param string $url + */ + public function setUrl($url) + { + $this->_url = $url; + } + + /** + * Sets the response regex + * + * @param string $regex + */ + public function setResponseRegex($regex) + { + $this->_responseRegex = $regex; + } + + /** + * Sets the authentication user name + * + * @param string $user + */ + public function setAuthUser($user) + { + $this->_authUser = $user; + } + + /** + * Sets the authentication password + * + * @param string $password + */ + public function setAuthPassword($password) + { + $this->_authPassword = $password; + } + + /** + * Sets the authentication scheme + * + * @param string $scheme + */ + public function setAuthScheme($scheme) + { + $this->_authScheme = $scheme; + } + + /** + * Sets whether to enable detailed logging + * + * @param boolean $verbose + */ + public function setVerbose($verbose) + { + $this->_verbose = StringHelper::booleanValue($verbose); + } + + /** + * Sets a list of observer events that will be logged + * if verbose output is enabled. + * + * @param string $observerEvents List of observer events + * + * @return void + */ + public function setObserverEvents($observerEvents) + { + $this->_observerEvents = array(); + + $token = ' ,;'; + $ext = strtok($observerEvents, $token); + + while ($ext !== false) { + $this->_observerEvents[] = $ext; + $ext = strtok($token); + } + } + + /** + * Creates an additional header for this task + * + * @return Parameter The created header + */ + public function createHeader() + { + $num = array_push($this->_headers, new Parameter()); + return $this->_headers[$num-1]; + } + + /** + * Creates a config parameter for this task + * + * @return Parameter The created parameter + */ + public function createConfig() + { + $num = array_push($this->_configData, new Parameter()); + return $this->_configData[$num-1]; + } + + /** + * Load the necessary environment for running this task. + * + * @throws BuildException + */ + public function init() + { + @include_once 'HTTP/Request2.php'; + + if (! class_exists('HTTP_Request2')) { + throw new BuildException( + 'HttpRequestTask depends on HTTP_Request2 being installed ' + . 'and on include_path.', + $this->getLocation() + ); + } + + $this->_authScheme = HTTP_Request2::AUTH_BASIC; + + // Other dependencies that should only be loaded + // when class is actually used + require_once 'HTTP/Request2/Observer/Log.php'; + } + + /** + * Make the http request + */ + public function main() + { + if (!isset($this->_url)) { + throw new BuildException("Missing attribute 'url' set"); + } + + $request = new HTTP_Request2($this->_url); + + // set the authentication data + if (!empty($this->_authUser)) { + $request->setAuth( + $this->_authUser, + $this->_authPassword, + $this->_authScheme + ); + } + + foreach ($this->_configData as $config) { + $request->setConfig($config->getName(), $config->getValue()); + } + + foreach ($this->_headers as $header) { + $request->setHeader($header->getName(), $header->getValue()); + } + + if ($this->_verbose) { + $observer = new HTTP_Request2_Observer_Log(); + + // set the events we want to log + $observer->events = $this->_observerEvents; + + $request->attach($observer); + } + + $response = $request->send(); + + if ($this->_responseRegex !== '') { + $matches = array(); + preg_match($this->_responseRegex, $response->getBody(), $matches); + + if (count($matches) === 0) { + throw new BuildException( + 'The received response body did not match the ' + . 'given regular expression' + ); + } else { + $this->log('The response body matched the provided regex.'); + } + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php b/buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php new file mode 100644 index 00000000..77a4aad5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/JslLintTask.php @@ -0,0 +1,284 @@ +<?php +/* + * $Id: 551de2e94aa21f44e19dd0806051f1eabf8b20f9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/util/DataStore.php'; + +/** + * A Javascript lint task. Checks syntax of Javascript files. + * Javascript lint (http://www.javascriptlint.com) must be in the system path. + * This class is based on Knut Urdalen's PhpLintTask. + * + * @author Stefan Priebsch <stefan.priebsch@e-novative.de> + * @version $Id: 551de2e94aa21f44e19dd0806051f1eabf8b20f9 $ + * @package phing.tasks.ext + */ +class JslLintTask extends Task +{ + protected $file; // the source file (from xml attribute) + protected $filesets = array(); // all fileset objects assigned to this task + + protected $showWarnings = true; + protected $haltOnFailure = false; + protected $hasErrors = false; + private $badFiles = array(); + + private $cache = null; + private $conf = null; + + private $executable = "jsl"; + + /** + * @var PhingFile + */ + protected $tofile = null; + + /** + * Sets the flag if warnings should be shown + * @param boolean $show + */ + public function setShowWarnings($show) { + $this->showWarnings = StringHelper::booleanValue($show); + } + + /** + * The haltonfailure property + * @param boolean $aValue + */ + public function setHaltOnFailure($aValue) { + $this->haltOnFailure = $aValue; + } + + /** + * File to be performed syntax check on + * @param PhingFile $file + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Whether to store last-modified times in cache + * + * @param PhingFile $file + */ + public function setCacheFile(PhingFile $file) + { + $this->cache = new DataStore($file); + } + + /** + * jsl config file + * + * @param PhingFile $file + */ + public function setConfFile(PhingFile $file) + { + $this->conf = $file; + } + + public function setExecutable($path){ + $this->executable = $path; + + if (!@file_exists($path)) { + throw new BuildException("JavaScript Lint executable '{$path}' not found"); + } + } + + public function getExecutable(){ + return $this->executable; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * File to save error messages to + * + * @param PhingFile $file + */ + public function setToFile(PhingFile $tofile) + { + $this->tofile = $tofile; + } + + /** + * Execute lint check against PhingFile or a FileSet + */ + public function main() { + if(!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + if (empty($this->executable)) { + throw new BuildException("Missing the 'executable' attribute"); + } + + if($this->file instanceof PhingFile) { + $this->lint($this->file->getPath()); + } else { // process filesets + $project = $this->getProject(); + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getPath(); + foreach($files as $file) { + $this->lint($dir.DIRECTORY_SEPARATOR.$file); + } + } + } + + // write list of 'bad files' to file (if specified) + if ($this->tofile) { + $writer = new FileWriter($this->tofile); + + foreach ($this->badFiles as $file => $messages) { + foreach ($messages as $msg) { + $writer->write($file . "=" . $msg . PHP_EOL); + } + } + + $writer->close(); + } + + if ($this->haltOnFailure && $this->hasErrors) throw new BuildException('Syntax error(s) in JS files:' .implode(', ', array_keys($this->badFiles))); + } + + /** + * Performs the actual syntax check + * + * @param string $file + * @return void + */ + protected function lint($file) + { + $command = $this->executable . ' -output-format ' . escapeshellarg('file:__FILE__;line:__LINE__;message:__ERROR__') . ' '; + + if (isset($this->conf)) { + $command .= '-conf ' . escapeshellarg($this->conf->getPath()) . ' '; + } + + $command .= '-process '; + + if(file_exists($file)) + { + if(is_readable($file)) + { + if ($this->cache) + { + $lastmtime = $this->cache->get($file); + + if ($lastmtime >= filemtime($file)) + { + $this->log("Not linting '" . $file . "' due to cache", Project::MSG_DEBUG); + return false; + } + } + + $messages = array(); + exec($command.'"'.$file.'"', $messages, $return); + + if ($return > 100) { + throw new BuildException("Could not execute Javascript Lint executable '{$this->executable}'"); + } + + $summary = $messages[sizeof($messages) - 1]; + + preg_match('/(\d+)\serror/', $summary, $matches); + $errorCount = (count($matches) > 1 ? $matches[1] : 0); + + preg_match('/(\d+)\swarning/', $summary, $matches); + $warningCount = (count($matches) > 1 ? $matches[1] : 0); + + $errors = array(); + $warnings = array(); + if ($errorCount > 0 || $warningCount > 0) { + $last = false; + foreach ($messages as $message) { + $matches = array(); + if (preg_match('/^(\.*)\^$/', $message)) { + $column = strlen($message); + if ($last == 'error') { + $errors[count($errors) - 1]['column'] = $column; + } else if ($last == 'warning') { + $warnings[count($warnings) - 1]['column'] = $column; + } + $last = false; + } + if (!preg_match('/^file:(.+);line:(\d+);message:(.+)$/', $message, $matches)) continue; + $msg = $matches[3]; + $data = array('filename' => $matches[1], 'line' => $matches[2], 'message' => $msg); + if (preg_match('/^.*error:.+$/i', $msg)) { + $errors[] = $data; + $last = 'error'; + } else if (preg_match('/^.*warning:.+$/i', $msg)) { + $warnings[] = $data; + $last = 'warning'; + } + } + } + + if($this->showWarnings && $warningCount > 0) + { + $this->log($file . ': ' . $warningCount . ' warnings detected', Project::MSG_WARN); + foreach ($warnings as $warning) { + $this->log('- line ' . $warning['line'] . (isset($warning['column']) ? ' column ' . $warning['column'] : '') . ': ' . $warning['message'], Project::MSG_WARN); + } + } + + if($errorCount > 0) + { + $this->log($file . ': ' . $errorCount . ' errors detected', Project::MSG_ERR); + if (!isset($this->badFiles[$file])) { + $this->badFiles[$file] = array(); + } + + foreach ($errors as $error) { + $message = 'line ' . $error['line'] . (isset($error['column']) ? ' column ' . $error['column'] : '') . ': ' . $error['message']; + $this->log('- ' . $message, Project::MSG_ERR); + array_push($this->badFiles[$file], $message); + } + $this->hasErrors = true; + } else if (!$this->showWarnings || $warningCount == 0) { + $this->log($file . ': No syntax errors detected', Project::MSG_VERBOSE); + + if ($this->cache) + { + $this->cache->put($file, filemtime($file)); + } + } + } else { + throw new BuildException('Permission denied: '.$file); + } + } else { + throw new BuildException('File not found: '.$file); + } + } +} + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/MailTask.php b/buildscripts/phing/classes/phing/tasks/ext/MailTask.php new file mode 100755 index 00000000..0fff88cc --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/MailTask.php @@ -0,0 +1,159 @@ +<?php +/* + * $Id: 901fb0efa435ae78d249a50ed0e0f6e5d31e0d32 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/Task.php'; + +/** + * Send an e-mail message + * + * <mail tolist="user@example.org" subject="build complete">The build process is a success...</mail> + * + * @author Michiel Rook <mrook@php.net> + * @author Francois Harvey at SecuriWeb (http://www.securiweb.net) + * @version $Id: 901fb0efa435ae78d249a50ed0e0f6e5d31e0d32 $ + * @package phing.tasks.ext + */ +class MailTask extends Task +{ + protected $tolist = null; + protected $subject = null; + protected $msg = null; + protected $from = null; + + protected $filesets = array(); + + public function main() + { + if (empty($this->from)) { + throw new BuildException('Missing "from" attribute'); + } + + $this->log('Sending mail to ' . $this->tolist); + + if (!empty($this->filesets)) { + @require_once 'Mail.php'; + @require_once 'Mail/mime.php'; + + if (!class_exists('Mail_mime')) { + throw new BuildException('Need the PEAR Mail_mime package to send attachments'); + } + + $mime = new Mail_mime(); + $hdrs = array( + 'From' => $this->from, + 'Subject' => $this->subject + ); + $mime->setTXTBody($this->msg); + + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $fromDir = $fs->getDir($this->project); + $srcFiles = $ds->getIncludedFiles(); + + foreach ($srcFiles as $file) { + $mime->addAttachment($fromDir . DIRECTORY_SEPARATOR . $file, 'application/octet-stream'); + } + } + + $body = $mime->get(); + $hdrs = $mime->headers($hdrs); + + $mail = Mail::factory('mail'); + $mail->send($this->tolist, $hdrs, $body); + } else { + mail($this->tolist, $this->subject, $this->msg, "From: {$this->from}\n"); + } + } + + /** + * Setter for message + */ + public function setMsg($msg) + { + $this->setMessage($msg); + } + + /** + * Alias setter + */ + public function setMessage($msg) + { + $this->msg = (string) $msg; + } + + /** + * Setter for subject + */ + public function setSubject($subject) + { + $this->subject = (string) $subject; + } + + /** + * Setter for tolist + */ + public function setToList($tolist) + { + $this->tolist = $tolist; + } + + /** + * Alias for (deprecated) recipient + */ + public function setRecipient($recipient) + { + $this->tolist = (string) $recipient; + } + + /** + * Alias for to + */ + public function setTo($to) + { + $this->tolist = (string) $to; + } + + /** + * Supports the <mail>Message</mail> syntax. + */ + public function addText($msg) + { + $this->msg = (string) $msg; + } + + /** + * Sets email address of sender + */ + public function setFrom($from) + { + $this->from = $from; + } + + /** + * Adds a fileset + */ + public function createFileSet() + { + $fileset = new FileSet(); + $this->filesets[] = $fileset; + return $fileset; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php b/buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php new file mode 100644 index 00000000..b9cfae64 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ManifestTask.php @@ -0,0 +1,343 @@ +<?php +/** + * $Id: 7f8f119fe5dd44ca9f374e24d776a1a764260e33 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once "phing/Task.php"; +require_once 'phing/system/io/PhingFile.php'; + +/** + * ManifestTask + * + * Generates a simple Manifest file with optional checksums. + * + * + * Manifest schema: + * ... + * path/to/file CHECKSUM [CHECKSUM2] [CHECKSUM3] + * path/to/secondfile CHECKSUM [CHECKSUM2] [CHECKSUM3] + * ... + * + * Example usage: + * <manifest checksum="crc32" file="${dir_build}/Manifest"> + * <fileset refid="files_build" /> + * </manifest> + * + * <manifest checksum="md5,adler32,sha256" file="${dir_build}/Manifest"> + * <fileset refid="files_build" /> + * </manifest> + * + * + * + * @author David Persson <davidpersson at qeweurope dot org> + * @package phing.tasks.ext + * @version $Id: 7f8f119fe5dd44ca9f374e24d776a1a764260e33 $ + * @since 2.3.1 + */ +class ManifestTask extends Task +{ + var $taskname = 'manifest'; + + /** + * Action + * + * "w" for reading in files from fileSet + * and writing manifest + * + * or + * + * "r" for reading in files from fileSet + * and checking against manifest + * + * @var string "r" or "w" + */ + private $action = 'w'; + + /** + * The target file passed in the buildfile. + */ + private $destFile = null; + + /** + * Holds filesets + * + * @var array An Array of objects + */ + private $filesets = array(); + + /** + * Enable/Disable checksuming or/and select algorithm + * true defaults to md5 + * false disables checksuming + * string "md5,sha256,..." enables generation of multiple checksums + * string "sha256" generates sha256 checksum only + * + * @var mixed + */ + private $checksum = false; + + /** + * A string used in hashing method + * + * @var string + */ + private $salt = ''; + + /** + * Holds some data collected during runtime + * + * @var array + */ + private $meta = array('totalFileCount' => 0,'totalFileSize' => 0); + + + /** + * The setter for the attribute "file" + * This is where the manifest will be written to/read from + * + * @param string Path to readable file + * @return void + */ + public function setFile(PhingFile $file) + { + $this->file = $file; + } + + /** + * The setter for the attribute "checksum" + * + * @param mixed $mixed + * @return void + */ + public function setChecksum($mixed) + { + if(is_string($mixed)) { + $data = array(strtolower($mixed)); + + if(strpos($data[0],',')) { + $data = explode(',',$mixed); + } + + $this->checksum = $data; + + } elseif($mixed === true) { + $this->checksum = array('md5'); + + } + } + + /** + * The setter for the optional attribute "salt" + * + * @param string $string + * @return void + */ + public function setSalt($string) + { + $this->salt = $string; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @access public + * @return object The created fileset object + */ + public function createFileSet() + { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * The init method: Do init steps. + */ + public function init() + { + // nothing to do here + } + + /** + * Delegate the work + */ + public function main() + { + $this->validateAttributes(); + + if($this->action == 'w') { + $this->write(); + + } elseif($this->action == 'r') { + $this->read(); + + } + } + + /** + * Creates Manifest file + * Writes to $this->file + * + * @throws BuildException + */ + private function write() + { + $project = $this->getProject(); + + if(!touch($this->file->getPath())) { + throw new BuildException("Unable to write to ".$this->file->getPath()."."); + } + + $this->log("Writing to " . $this->file->__toString(), Project::MSG_INFO); + + if(is_array($this->checksum)) { + $this->log("Using " . implode(', ',$this->checksum)." for checksuming.", Project::MSG_INFO); + } + + foreach($this->filesets as $fs) { + + $dir = $fs->getDir($this->project)->getPath(); + + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + foreach($ds->getIncludedFiles() as $file_path) { + $line = $file_path; + if($this->checksum) { + foreach($this->checksum as $algo) { + if(!$hash = $this->hashFile($dir.'/'.$file_path,$algo)) { + throw new BuildException("Hashing $dir/$file_path with $algo failed!"); + } + + $line .= "\t".$hash; + } + } + $line .= "\n"; + $manifest[] = $line; + $this->log("Adding file ".$file_path,Project::MSG_VERBOSE); + $this->meta['totalFileCount'] ++; + $this->meta['totalFileSize'] += filesize($dir.'/'.$file_path); + } + + } + + file_put_contents($this->file,$manifest); + + $this->log("Done. Total files: ".$this->meta['totalFileCount'].". Total file size: ".$this->meta['totalFileSize']." bytes.", Project::MSG_INFO); + } + + /** + * @todo implement + */ + private function read() + { + throw new BuildException("Checking against manifest not yet supported."); + } + + /** + * Wrapper method for hash generation + * Automatically selects extension + * Falls back to built-in functions + * + * @link http://www.php.net/mhash + * @link http://www.php.net/hash + * + * @param string $msg The string that should be hashed + * @param string $algo Algorithm + * @return mixed String on success, false if $algo is not available + */ + private function hash($msg,$algo) + { + if(extension_loaded('hash')) { + $algo = strtolower($algo); + + if(in_array($algo,hash_algos())) { + return hash($algo,$this->salt.$msg); + } + + } + + if(extension_loaded('mhash')) { + $algo = strtoupper($algo); + + if(defined('MHASH_'.$algo)) { + return mhash('MHASH_'.$algo,$this->salt.$msg); + + } + } + + switch(strtolower($algo)) { + case 'md5': + return md5($this->salt.$msg); + case 'crc32': + return abs(crc32($this->salt.$msg)); + } + + return false; + } + + /** + * Hash a files contents + * plus it's size an modification time + * + * @param string $file + * @param string $algo + * @return mixed String on success, false if $algo is not available + */ + private function hashFile($file,$algo) + { + if(!file_exists($file)) { + return false; + } + + $msg = file_get_contents($file).filesize($file).filemtime($file); + + return $this->hash($msg,$algo); + } + + /** + * Validates attributes coming in from XML + * + * @access private + * @return void + * @throws BuildException + */ + protected function validateAttributes() + { + if($this->action != 'r' && $this->action != 'w') { + throw new BuildException("'action' attribute has non valid value. Use 'r' or 'w'"); + } + + if(empty($this->salt)) { + $this->log("No salt provided. Specify one with the 'salt' attribute.", Project::MSG_WARN); + } + + if (is_null($this->file) && count($this->filesets) === 0) { + throw new BuildException("Specify at least sources and destination - a file or a fileset."); + } + + if (!is_null($this->file) && $this->file->exists() && $this->file->isDirectory()) { + throw new BuildException("Destination file cannot be a directory."); + } + + } +} + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php b/buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php new file mode 100644 index 00000000..2db5ad69 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PackageAsPathTask.php @@ -0,0 +1,65 @@ +<?php + +/* + * $Id: 976dafaf4cafd9ff8f47907a09943ae3963aea79 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Convert dot-notation packages to relative paths. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: 976dafaf4cafd9ff8f47907a09943ae3963aea79 $ + * @package phing.tasks.ext + */ +class PackageAsPathTask extends Task { + + /** The package to convert. */ + protected $pckg; + + /** The value to store the conversion in. */ + protected $name; + + /** + * Executes the package to patch converstion and stores it + * in the user property <code>value</code>. + */ + public function main() + { + $this->project->setUserProperty($this->name, strtr($this->pckg, '.', '/')); + } + + /** + * @param string $pckg the package to convert + */ + public function setPackage($pckg) + { + $this->pckg = $pckg; + } + + /** + * @param string $name the Ant variable to store the path in + */ + public function setName($name) + { + $this->name = $name; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php b/buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php new file mode 100755 index 00000000..b8da5cb4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ParallelTask.php @@ -0,0 +1,83 @@ +<?php + +/** + * $Id: 860b2b6cdbd797754660fe2c1554e22ab2db4967 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + * + * @package phing.tasks.ext + */ + +/** + * Uses the DocBlox_Parallel library to run nested Phing tasks concurrently. + * + * WARNING: this task is highly experimental! + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 860b2b6cdbd797754660fe2c1554e22ab2db4967 $ + * @package phing.tasks.ext + * @see https://github.com/phpdocumentor/Parallel + * @since 2.4.10 + */ +class ParallelTask extends SequentialTask +{ + /** + * Maximum number of threads / processes + * @var int + */ + private $threadCount = 2; + + /** + * Sets the maximum number of threads / processes to use + * @param int $threadCount + */ + public function setThreadCount($threadCount) + { + $this->threadCount = $threadCount; + } + + public function init() + { + } + + public function main() + { + @include_once 'phing/contrib/DocBlox/Parallel/Manager.php'; + @include_once 'phing/contrib/DocBlox/Parallel/Worker.php'; + @include_once 'phing/contrib/DocBlox/Parallel/WorkerPipe.php'; + if (!class_exists('DocBlox_Parallel_Worker')) { + throw new BuildException( + 'ParallelTask depends on DocBlox being installed and on include_path.', + $this->getLocation() + ); + } + + $mgr = new DocBlox_Parallel_Manager(); + $mgr->setProcessLimit($this->threadCount); + + foreach ($this->nestedTasks as $task) { + $worker = new DocBlox_Parallel_Worker( + array($task, 'perform'), + array($task) + ); + + $mgr->addWorker($worker); + } + + $mgr->execute(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/PatchTask.php b/buildscripts/phing/classes/phing/tasks/ext/PatchTask.php new file mode 100755 index 00000000..162f5fe5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PatchTask.php @@ -0,0 +1,301 @@ +<?php +/** + * Patches a file by applying a 'diff' file to it + * + * Requires "patch" to be on the execution path. + * + * Based on Apache Ant PatchTask: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require_once 'phing/Task.php'; + +/** + * Patches a file by applying a 'diff' file to it + * + * Requires "patch" to be on the execution path. + * + * @package phing.tasks.ext + */ +class PatchTask extends Task +{ + /** + * Base command to be executed (must end with a space character!) + * @var string + */ + const CMD = 'patch --batch --silent '; + + /** + * File to be patched + * @var string + */ + private $originalFile; + + /** + * Patch file + * + * @var string + */ + private $patchFile; + + /** + * Value for a "-p" option + * @var int + */ + private $strip; + + /** + * Command line arguments for patch binary + * @var array + */ + private $cmdArgs = array(); + + /** + * Halt on error return value from patch invocation. + * @var bool + */ + private $haltOnFailure = false; + + /** + * The file containing the diff output + * + * Required. + * + * @param string $file File containing the diff output + * @return void + * @throws BuildException if $file not exists + */ + public function setPatchFile($file) + { + if (!is_file($file)) + { + throw new BuildException(sprintf('Patchfile %s doesn\'t exist', $file)); + } + $this->patchFile = $file; + } + + /** + * The file to patch + * + * Optional if it can be inferred from the diff file. + * + * @param string $file File to patch + * @return void + */ + public function setOriginalFile($file) + { + $this->originalFile = $file; + } + + /** + * The name of a file to send the output to, instead of patching + * the file(s) in place + * + * Optional. + * + * @param string $file File to send the output to + * @return void + */ + public function setDestFile($file) + { + if ($file !== null) + { + $this->cmdArgs []= "--output=$file"; + } + } + + /** + * Flag to create backups + * + * Optional, default - false + * + * @param bool $backups If true create backups + * @return void + */ + public function setBackups($backups) + { + if ($backups) + { + $this->cmdArgs []= '--backup'; + } + } + + /** + * Flag to ignore whitespace differences; + * + * Default - false + * + * @param bool $ignore If true ignore whitespace differences + * @return void + */ + public function setIgnoreWhiteSpace($ignore) + { + if ($ignore) + { + $this->cmdArgs []= '--ignore-whitespace'; + } + } + + /** + * Strip the smallest prefix containing <i>num</i> leading slashes + * from filenames. + * + * patch's <i>--strip</i> option. + * + * @param int $num number of lines to strip + * @return void + * @throws BuildException if num is < 0, or other errors + */ + public function setStrip($num) + { + if ($num < 0) + { + throw new BuildException('strip has to be >= 0'); + } + + $this->strip = $num; + } + + /** + * Work silently unless an error occurs + * + * Optional, default - false + * @param bool $flag If true suppress set the -s option on the patch command + * @return void + */ + public function setQuiet($flag) + { + if ($flag) + { + $this->cmdArgs []= '--silent'; + } + } + + /** + * Assume patch was created with old and new files swapped + * + * Optional, default - false + * + * @param bool $flag If true set the -R option on the patch command + * @return void + */ + public function setReverse($flag) + { + if ($flag) + { + $this->cmdArgs []= '--reverse'; + } + } + + /** + * The directory to run the patch command in + * + * Defaults to the project's base directory. + * + * @param string $directory Directory to run the patch command in + * @return void + */ + public function setDir($directory) + { + $this->cmdArgs []= "--directory=$directory"; + } + + /** + * Ignore patches that seem to be reversed or already applied + * + * @param bool $flag If true set the -N (--forward) option + * @return void + */ + public function setForward($flag) + { + if ($flag) + { + $this->cmdArgs []= "--forward"; + } + } + + /** + * Set the maximum fuzz factor + * + * Defaults to 0 + * + * @param string $value Value of a fuzz factor + * @return void + */ + public function setFuzz($value) + { + $this->cmdArgs []= "--fuzz=$value"; + } + + /** + * If true, stop the build process if the patch command + * exits with an error status. + * + * The default is "false" + * + * @param bool $value "true" if it should halt, otherwise "false" + * @return void + */ + public function setHaltOnFailure($value) + { + $this->haltOnFailure = $value; + } + + /** + * Main task method + * + * @return void + * @throws BuildException when it all goes a bit pear shaped + */ + public function main() + { + if ($this->patchFile == null) + { + throw new BuildException('patchfile argument is required'); + } + + // Define patch file + $this->cmdArgs []= '-i ' . $this->patchFile; + // Define strip factor + if ($this->strip != null) + { + $this->cmdArgs []= '--strip=' . $this->strip; + } + // Define original file if specified + if ($this->originalFile != null) + { + $this->cmdArgs []= $this->originalFile; + } + + $cmd = self::CMD . implode(' ', $this->cmdArgs); + + $this->log('Applying patch: ' . $this->patchFile); + + exec($cmd, $output, $exitCode); + + foreach ($output as $line) + { + $this->log($line, Project::MSG_VERBOSE); + } + + if ($exitCode != 0 && $this->haltOnFailure) + { + throw new BuildException( "Task exited with code $exitCode" ); + } + + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php b/buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php new file mode 100644 index 00000000..f42231f7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PearPackage2Task.php @@ -0,0 +1,279 @@ +<?php +/* + * $Id: 1b20dbb6595bd4c41d1e5f1430900e3bf95de411 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/PearPackageTask.php'; + +/** + * A task to create a PEAR package.xml version 2.0 file. + * + * This class uses the PEAR_PackageFileManager2 class to perform the work. + * + * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o + * requiring changes to this class. We've accomplished this by having generic <option> and <mapping> + * nested elements. All options are set using PEAR_PackageFileManager2::setOptions(). + * + * The <option> tag is used to set a simple option value. + * <code> + * <option name="option_name" value="option_value"/> + * or <option name="option_name">option_value</option> + * </code> + * + * The <mapping> tag represents a complex data type. You can use nested <element> (and nested <element> with + * <element> tags) to represent the full complexity of the structure. Bear in mind that what you are creating + * will be mapped to an associative array that will be passed in via PEAR_PackageFileManager2::setOptions(). + * <code> + * <mapping name="option_name"> + * <element key="key_name" value="key_val"/> + * <element key="key_name" value="key_val"/> + * </mapping> + * </code> + * + * Here's an over-simple example of how this could be used: + * <code> + * <pearpkg2 name="phing" dir="${build.src.dir}"> + * <fileset dir="src"> + * <include name="**"/> + * </fileset> + * <option name="outputdirectory" value="./build"/> + * <option name="packagefile" value="package2.xml"/> + * <option name="packagedirectory" value="./${build.dist.dir}"/> + * <option name="baseinstalldir" value="${pkg.prefix}"/> + * <option name="channel" value="my.pear-channel.com"/> + * <option name="summary" value="${pkg.summary}"/> + * <option name="description" value="${pkg.description}"/> + * <option name="apiversion" value="${pkg.version}"/> + * <option name="apistability" value="beta"/> + * <option name="releaseversion" value="${pkg.version}"/> + * <option name="releasestability" value="beta"/> + * <option name="license" value="none"/> + * <option name="phpdep" value="5.0.0"/> + * <option name="pearinstallerdep" value="1.4.6"/> + * <option name="packagetype" value="php"/> + * <option name="notes" value="${pkg.relnotes}"/> + * <mapping name="maintainers"> + * <element> + * <element key="handle" value="hlellelid"/> + * <element key="name" value="Hans"/> + * <element key="email" value="hans@xmpl.org"/> + * <element key="role" value="lead"/> + * <element key="active" value="yes"/> + * </element> + * </mapping> + * </pearpkg2> + * </code> + * + * Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to + * see a more complete example of how to call this script. + * + * @author Stuart Binge <stuart.binge@complinet.com> + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext + * @version $Id: 1b20dbb6595bd4c41d1e5f1430900e3bf95de411 $ + */ +class PearPackage2Task extends PearPackageTask { + + public function init() { + include_once 'PEAR/PackageFileManager2.php'; + if (!class_exists('PEAR_PackageFileManager2')) { + throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml version 2.0 file."); + } + } + + protected function setVersion2Options() + { + $this->pkg->setPackage($this->package); + $this->pkg->setDate(strftime('%Y-%m-%d')); + $this->pkg->setTime(strftime('%H:%M:%S')); + + $newopts = array(); + foreach ($this->options as $opt) { + switch ($opt->getName()) { + case 'summary': + $this->pkg->setSummary($opt->getValue()); + break; + + case 'description': + $this->pkg->setDescription($opt->getValue()); + break; + + case 'uri': + $this->pkg->setUri($opt->getValue()); + break; + + case 'license': + $this->pkg->setLicense($opt->getValue()); + break; + + case 'channel': + $this->pkg->setChannel($opt->getValue()); + break; + + case 'apiversion': + $this->pkg->setAPIVersion($opt->getValue()); + break; + + case 'releaseversion': + $this->pkg->setReleaseVersion($opt->getValue()); + break; + + case 'releasestability': + $this->pkg->setReleaseStability($opt->getValue()); + break; + + case 'apistability': + $this->pkg->setAPIStability($opt->getValue()); + break; + + case 'notes': + $this->pkg->setNotes($opt->getValue()); + break; + + case 'packagetype': + $this->pkg->setPackageType($opt->getValue()); + break; + + case 'phpdep': + $this->pkg->setPhpDep($opt->getValue()); + break; + + case 'pearinstallerdep': + $this->pkg->setPearinstallerDep($opt->getValue()); + break; + + default: + $newopts[] = $opt; + break; + } + } + $this->options = $newopts; + + $newmaps = array(); + foreach ($this->mappings as $map) { + switch ($map->getName()) { + case 'deps': + $deps = $map->getValue(); + foreach ($deps as $dep) { + $type = isset($dep['optional']) ? 'optional' : 'required'; + $min = isset($dep['min']) ? $dep['min'] : $dep['version']; + $max = isset($dep['max']) ? $dep['max'] : null; + $rec = isset($dep['recommended']) ? $dep['recommended'] : null; + $channel = isset($dep['channel']) ? $dep['channel'] : false; + $uri = isset($dep['uri']) ? $dep['uri'] : false; + + if (!empty($channel)) { + $this->pkg->addPackageDepWithChannel( + $type, $dep['name'], $channel, $min, $max, $rec + ); + } elseif (!empty($uri)) { + $this->pkg->addPackageDepWithUri( + $type, $dep['name'], $uri + ); + } + }; + break; + + case 'extdeps': + $deps = $map->getValue(); + foreach ($deps as $dep) { + $type = isset($dep['optional']) ? 'optional' : 'required'; + $min = isset($dep['min']) ? $dep['min'] : $dep['version']; + $max = isset($dep['max']) ? $dep['max'] : $dep['version']; + $rec = isset($dep['recommended']) ? $dep['recommended'] : $dep['version']; + + $this->pkg->addExtensionDep( + $type, $dep['name'], $min, $max, $rec + ); + }; + break; + + case 'maintainers': + $maintainers = $map->getValue(); + + foreach ($maintainers as $maintainer) { + if (!isset($maintainer['active'])) { + $maintainer['active'] = 'yes'; + } else { + $maintainer['active'] = $maintainer['active'] === false ? 'no' : 'yes'; + } + $this->pkg->addMaintainer( + $maintainer['role'], + $maintainer['handle'], + $maintainer['name'], + $maintainer['email'], + $maintainer['active'] + ); + } + break; + + case 'replacements': + $replacements = $map->getValue(); + + foreach($replacements as $replacement) { + $this->pkg->addReplacement( + $replacement['path'], + $replacement['type'], + $replacement['from'], + $replacement['to'] + ); + } + break; + + case 'role': + foreach ($map->getValue() as $role) { + $this->pkg->addRole($role['extension'], $role['role']); + } + break; + + default: + $newmaps[] = $map; + } + } + $this->mappings = $newmaps; + } + + /** + * Main entry point. + * @return void + */ + public function main() + { + if ($this->dir === null) { + throw new BuildException("You must specify the \"dir\" attribute for PEAR package 2 task."); + } + + if ($this->package === null) { + throw new BuildException("You must specify the \"name\" attribute for PEAR package 2 task."); + } + + $this->pkg = new PEAR_PackageFileManager2(); + + $this->setVersion2Options(); + $this->setOptions(); + + $this->pkg->addRelease(); + $this->pkg->generateContents(); + $e = $this->pkg->writePackageFile(); + if (PEAR::isError($e)) { + throw new BuildException("Unable to write package file.", new Exception($e->getMessage())); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/PearPackageTask.php b/buildscripts/phing/classes/phing/tasks/ext/PearPackageTask.php new file mode 100644 index 00000000..47945998 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PearPackageTask.php @@ -0,0 +1,504 @@ +<?php +/* + * $Id: 2078fd5bab3dd6dcea0345a12fce86a24586c765 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; +include_once 'phing/types/FileSet.php'; + +/** + * A task to create PEAR package.xml file. + * + * This class uses the PEAR_PackageFileMaintainer class to perform the work. + * + * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o + * requiring changes to this class. We've accomplished this by having generic <option> and <mapping> + * nested elements. All options are set using PEAR_PackageFileMaintainer::setOptions(). + * + * The <option> tag is used to set a simple option value. + * <code> + * <option name="option_name" value="option_value"/> + * or <option name="option_name">option_value</option> + * </code> + * + * The <mapping> tag represents a complex data type. You can use nested <element> (and nested <element> with + * <element> tags) to represent the full complexity of the structure. Bear in mind that what you are creating + * will be mapped to an associative array that will be passed in via PEAR_PackageFileMaintainer::setOptions(). + * <code> + * <mapping name="option_name"> + * <element key="key_name" value="key_val"/> + * <element key="key_name" value="key_val"/> + * </mapping> + * </code> + * + * Here's an over-simple example of how this could be used: + * <code> + * <pearpkg name="phing" dir="${build.src.dir}" destFile="${build.base.dir}/package.xml"> + * <fileset> + * <include name="**"/> + * </fileset> + * <option name="notes">Sample release notes here.</option> + * <option name="description">Package description</option> + * <option name="summary">Short description</option> + * <option name="version" value="2.0.0b1"/> + * <option name="state" value="beta"/> + * <mapping name="maintainers"> + * <element> + * <element key="handle" value="hlellelid"/> + * <element key="name" value="Hans"/> + * <element key="email" value="hans@xmpl.org"/> + * <element key="role" value="lead"/> + * </element> + * </mapping> + * </pearpkg> + * </code> + * + * Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to + * see a more complete example of how to call this script. + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext + * @version $Id: 2078fd5bab3dd6dcea0345a12fce86a24586c765 $ + */ +class PearPackageTask extends MatchingTask { + + /** */ + protected $package; + + /** Base directory for reading files. */ + protected $dir; + + /** Package file */ + private $packageFile; + + /** @var array FileSet[] */ + private $filesets = array(); + + /** @var PEAR_PackageFileManager */ + protected $pkg; + + private $preparedOptions = array(); + + /** @var array PearPkgOption[] */ + protected $options = array(); + + /** Nested <mapping> (complex options) types. */ + protected $mappings = array(); + + /** + * Nested <role> elements + * @var PearPkgRole[] + */ + protected $roles = array(); + + public function init() { + include_once 'PEAR/PackageFileManager.php'; + if (!class_exists('PEAR_PackageFileManager')) { + throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml file."); + } + } + + /** + * Sets PEAR package.xml options, based on class properties. + * @return void + */ + protected function setOptions() { + + // 1) first prepare/populate options + $this->populateOptions(); + + // 2) make any final adjustments (this could move into populateOptions() also) + + // default PEAR basedir would be the name of the package (e.g."phing") + if (!isset($this->preparedOptions['baseinstalldir'])) { + $this->preparedOptions['baseinstalldir'] = $this->package; + } + + // unless filelistgenerator has been overridden, we use Phing FileSet generator + if (!isset($this->preparedOptions['filelistgenerator'])) { + if (empty($this->filesets)) { + throw new BuildException("You must use a <fileset> tag to specify the files to include in the package.xml"); + } + $this->preparedOptions['filelistgenerator'] = 'Fileset'; + $this->preparedOptions['usergeneratordir'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pearpackage'; + // Some PHING-specific options needed by our Fileset reader + $this->preparedOptions['phing_project'] = $this->project; + $this->preparedOptions['phing_filesets'] = $this->filesets; + } elseif ($this->preparedOptions['filelistgeneragor'] != 'Fileset' && !empty($this->filesets)) { + throw new BuildException("You cannot use <fileset> element if you have specified the \"filelistgenerator\" option."); + } + + // 3) Set the options + + // No need for excessive validation here, since the PEAR class will do its own + // validation & return errors + $e = $this->pkg->setOptions($this->preparedOptions); + + if (PEAR::isError($e)) { + throw new BuildException("Unable to set options.", new Exception($e->getMessage())); + } + + // convert roles + foreach ($this->roles as $role) { + $this->pkg->addRole($role->getExtension(), $role->getRole()); + } + } + + /** + * Fixes the boolean in optional dependencies + */ + private function fixDeps($deps) + { + foreach (array_keys($deps) as $dep) + { + if (isset($deps[$dep]['optional']) && $deps[$dep]['optional']) + { + $deps[$dep]['optional'] = "yes"; + } + } + + return $deps; + } + + /** + * Adds the options that are set via attributes and the nested tags to the options array. + */ + private function populateOptions() { + + // These values could be overridden if explicitly defined using nested tags + $this->preparedOptions['package'] = $this->package; + $this->preparedOptions['packagedirectory'] = $this->dir->getAbsolutePath(); + + if ($this->packageFile !== null) { + // create one w/ full path + $f = new PhingFile($this->packageFile->getAbsolutePath()); + $this->preparedOptions['packagefile'] = $f->getName(); + // must end in trailing slash + $this->preparedOptions['outputdirectory'] = $f->getParent() . DIRECTORY_SEPARATOR; + $this->log("Creating package file: " . $f->__toString(), Project::MSG_INFO); + } else { + $this->log("Creating [default] package.xml file in base directory.", Project::MSG_INFO); + } + + // converts option objects and mapping objects into + // key => value options that can be passed to PEAR_PackageFileManager + + foreach($this->options as $opt) { + $this->preparedOptions[ $opt->getName() ] = $opt->getValue(); //no arrays yet. preg_split('/\s*,\s*/', $opt->getValue()); + } + + foreach($this->mappings as $map) { + $value = $map->getValue(); // getValue returns complex value + + if ($map->getName() == 'deps') + { + $value = $this->fixDeps($value); + } + + $this->preparedOptions[ $map->getName() ] = $value; + } + } + + /** + * Main entry point. + * @return void + */ + public function main() { + + if ($this->dir === null) { + throw new BuildException("You must specify the \"dir\" attribute for PEAR package task."); + } + + if ($this->package === null) { + throw new BuildException("You must specify the \"name\" attribute for PEAR package task."); + } + + $this->pkg = new PEAR_PackageFileManager(); + + $this->setOptions(); + + $e = $this->pkg->writePackageFile(); + if (PEAR::isError($e)) { + throw new BuildException("Unable to write package file.", new Exception($e->getMessage())); + } + + } + + /** + * Used by the PEAR_PackageFileManager_PhingFileSet lister. + * @return array FileSet[] + */ + public function getFileSets() { + return $this->filesets; + } + + // ------------------------------- + // Set properties from XML + // ------------------------------- + + /** + * Nested creator, creates a FileSet for this task + * + * @param FileSet $fileset Set of files to add to the package + * + * @return void + */ + public function addFileSet(FileSet $fs) { + $this->filesets[] = $fs; + } + + /** + * Set "package" property from XML. + * @see setName() + * @param string $v + * @return void + */ + public function setPackage($v) { + $this->package = $v; + } + + /** + * Sets "dir" property from XML. + * @param PhingFile $f + * @return void + */ + public function setDir(PhingFile $f) { + $this->dir = $f; + } + + /** + * Sets "name" property from XML. + * @param string $v + * @return void + */ + public function setName($v) { + $this->package = $v; + } + + /** + * Sets the file to use for generated package.xml + */ + public function setDestFile(PhingFile $f) { + $this->packageFile = $f; + } + + /** + * Handles nested generic <option> elements. + */ + public function createOption() { + $o = new PearPkgOption(); + $this->options[] = $o; + return $o; + } + + /** + * Handles nested generic <option> elements. + */ + public function createMapping() { + $o = new PearPkgMapping(); + $this->mappings[] = $o; + return $o; + } + + /** + * Handles nested <role> elements + * @return PearPkgRole + */ + public function createRole() + { + $role = new PearPkgRole(); + $this->roles[] = $role; + return $role; + } +} + + + +/** + * Generic option class is used for non-complex options. + * + * @package phing.tasks.ext + */ +class PearPkgOption { + + private $name; + private $value; + + public function setName($v) { $this->name = $v; } + public function getName() { return $this->name; } + + public function setValue($v) { $this->value = $v; } + public function getValue() { return $this->value; } + public function addText($txt) { $this->value = trim($txt); } + +} + +/** + * Handles complex options <mapping> elements which are hashes (assoc arrays). + * + * @package phing.tasks.ext + */ +class PearPkgMapping { + + private $name; + private $elements = array(); + + public function setName($v) { + $this->name = $v; + } + + public function getName() { + return $this->name; + } + + public function createElement() { + $e = new PearPkgMappingElement(); + $this->elements[] = $e; + return $e; + } + + public function getElements() { + return $this->elements; + } + + /** + * Returns the PHP hash or array of hashes (etc.) that this mapping represents. + * @return array + */ + public function getValue() { + $value = array(); + foreach($this->getElements() as $el) { + if ($el->getKey() !== null) { + $value[ $el->getKey() ] = $el->getValue(); + } else { + $value[] = $el->getValue(); + } + } + return $value; + } +} + +/** + * Sub-element of <mapping>. + * + * @package phing.tasks.ext + */ +class PearPkgMappingElement { + + private $key; + private $value; + private $elements = array(); + + public function setKey($v) { + $this->key = $v; + } + + public function getKey() { + return $this->key; + } + + public function setValue($v) { + $this->value = $v; + } + + /** + * Returns either the simple value or + * the calculated value (array) of nested elements. + * @return mixed + */ + public function getValue() { + if (!empty($this->elements)) { + $value = array(); + foreach($this->elements as $el) { + if ($el->getKey() !== null) { + $value[ $el->getKey() ] = $el->getValue(); + } else { + $value[] = $el->getValue(); + } + } + return $value; + } else { + return $this->value; + } + } + + /** + * Handles nested <element> tags. + */ + public function createElement() { + $e = new PearPkgMappingElement(); + $this->elements[] = $e; + return $e; + } + +} + +/** + * Encapsulates file roles + * + * @package phing.tasks.ext + */ +class PearPkgRole +{ + /** + * @var string + */ + private $extension; + + /** + * @var string + */ + private $role; + + /** + * Sets the file extension + * @param string $extension + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Retrieves the file extension + * @return string + */ + public function getExtension() + { + return $this->extension; + } + + /** + * Sets the role + * @param string $role + */ + public function setRole($role) + { + $this->role = $role; + } + + /** + * Retrieves the role + * @return string + */ + public function getRole() + { + return $this->role; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php b/buildscripts/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php new file mode 100644 index 00000000..de40ae36 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php @@ -0,0 +1,648 @@ +<?php +/* + * $Id: 8c8f9369e06a3467e34fc8d89f6355df048ece90 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * A PHP code sniffer task. Checking the style of one or more PHP source files. + * + * @author Dirk Thomas <dirk.thomas@4wdmedia.de> + * @version $Id: 8c8f9369e06a3467e34fc8d89f6355df048ece90 $ + * @package phing.tasks.ext + */ +class PhpCodeSnifferTask extends Task { + + protected $file; // the source file (from xml attribute) + protected $filesets = array(); // all fileset objects assigned to this task + + // parameters for php code sniffer + protected $standard = 'Generic'; + protected $sniffs = array(); + protected $showWarnings = true; + protected $showSources = false; + protected $reportWidth = 80; + protected $verbosity = 0; + protected $tabWidth = 0; + protected $allowedFileExtensions = array('php'); + protected $ignorePatterns = false; + protected $noSubdirectories = false; + protected $configData = array(); + protected $encoding = 'iso-8859-1'; + + // parameters to customize output + protected $showSniffs = false; + protected $format = 'default'; + protected $formatters = array(); + + /** + * Holds the type of the doc generator + * + * @var string + */ + protected $docGenerator = ''; + + /** + * Holds the outfile for the documentation + * + * @var PhingFile + */ + protected $docFile = null; + + private $haltonerror = false; + private $haltonwarning = false; + private $skipversioncheck = false; + + /** + * Load the necessary environment for running PHP_CodeSniffer. + * + * @return void + */ + public function init() + { + } + + /** + * File to be performed syntax check on + * @param PhingFile $file + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Sets the coding standard to test for + * + * @param string $standard The coding standard + * + * @return void + */ + public function setStandard($standard) + { + $this->standard = $standard; + } + + /** + * Sets the sniffs which the standard should be restricted to + * @param string $sniffs + */ + public function setSniffs($sniffs) + { + $token = ' ,;'; + $sniff = strtok($sniffs, $token); + while ($sniff !== false) { + $this->sniffs[] = $sniff; + $sniff = strtok($token); + } + } + + /** + * Sets the type of the doc generator + * + * @param string $generator HTML or Text + * + * @return void + */ + public function setDocGenerator($generator) + { + $this->docGenerator = $generator; + } + + /** + * Sets the outfile for the documentation + * + * @param PhingFile $file The outfile for the doc + * + * @return void + */ + public function setDocFile(PhingFile $file) + { + $this->docFile = $file; + } + + /** + * Sets the flag if warnings should be shown + * @param boolean $show + */ + public function setShowWarnings($show) + { + $this->showWarnings = StringHelper::booleanValue($show); + } + + /** + * Sets the flag if sources should be shown + * + * @param boolean $show Whether to show sources or not + * + * @return void + */ + public function setShowSources($show) + { + $this->showSources = StringHelper::booleanValue($show); + } + + /** + * Sets the width of the report + * + * @param int $width How wide the screen reports should be. + * + * @return void + */ + public function setReportWidth($width) + { + $this->reportWidth = (int) $width; + } + + /** + * Sets the verbosity level + * @param int $level + */ + public function setVerbosity($level) + { + $this->verbosity = (int)$level; + } + + /** + * Sets the tab width to replace tabs with spaces + * @param int $width + */ + public function setTabWidth($width) + { + $this->tabWidth = (int)$width; + } + + /** + * Sets file encoding + * @param string $encoding + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + } + + /** + * Sets the allowed file extensions when using directories instead of specific files + * @param array $extensions + */ + public function setAllowedFileExtensions($extensions) + { + $this->allowedFileExtensions = array(); + $token = ' ,;'; + $ext = strtok($extensions, $token); + while ($ext !== false) { + $this->allowedFileExtensions[] = $ext; + $ext = strtok($token); + } + } + + /** + * Sets the ignore patterns to skip files when using directories instead of specific files + * @param array $extensions + */ + public function setIgnorePatterns($patterns) + { + $this->ignorePatterns = array(); + $token = ' ,;'; + $pattern = strtok($patterns, $token); + while ($pattern !== false) { + $this->ignorePatterns[] = $pattern; + $pattern = strtok($token); + } + } + + /** + * Sets the flag if subdirectories should be skipped + * @param boolean $subdirectories + */ + public function setNoSubdirectories($subdirectories) + { + $this->noSubdirectories = StringHelper::booleanValue($subdirectories); + } + + /** + * Creates a config parameter for this task + * + * @return Parameter The created parameter + */ + public function createConfig() { + $num = array_push($this->configData, new Parameter()); + return $this->configData[$num-1]; + } + + /** + * Sets the flag if the used sniffs should be listed + * @param boolean $show + */ + public function setShowSniffs($show) + { + $this->showSniffs = StringHelper::booleanValue($show); + } + + /** + * Sets the output format + * @param string $format + */ + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Create object for nested formatter element. + * @return CodeSniffer_FormatterElement + */ + public function createFormatter () { + $num = array_push($this->formatters, + new PhpCodeSnifferTask_FormatterElement()); + return $this->formatters[$num-1]; + } + + /** + * Sets the haltonerror flag + * @param boolean $value + */ + public function setHaltonerror($value) + { + $this->haltonerror = $value; + } + + /** + * Sets the haltonwarning flag + * @param boolean $value + */ + public function setHaltonwarning($value) + { + $this->haltonwarning = $value; + } + + /** + * Sets the skipversioncheck flag + * @param boolean $value + */ + public function setSkipVersionCheck($value) + { + $this->skipversioncheck = $value; + } + + /** + * Executes PHP code sniffer against PhingFile or a FileSet + */ + public function main() { + if (!class_exists('PHP_CodeSniffer')) { + @include_once 'PHP/CodeSniffer.php'; + + if (!class_exists('PHP_CodeSniffer')) { + throw new BuildException("This task requires the PHP_CodeSniffer package installed and available on the include path", $this->getLocation()); + } + } + + /** + * Determine PHP_CodeSniffer version number + */ + if (!$this->skipversioncheck) { + preg_match('/\d\.\d\.\d/', shell_exec('phpcs --version'), $version); + + if (version_compare($version[0], '1.2.2') < 0) { + throw new BuildException( + 'PhpCodeSnifferTask requires PHP_CodeSniffer version >= 1.2.2', + $this->getLocation() + ); + } + } + + if(!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + if (PHP_CodeSniffer::isInstalledStandard($this->standard) === false) { + // They didn't select a valid coding standard, so help them + // out by letting them know which standards are installed. + $installedStandards = PHP_CodeSniffer::getInstalledStandards(); + $numStandards = count($installedStandards); + $errMsg = ''; + + if ($numStandards === 0) { + $errMsg = 'No coding standards are installed.'; + } else { + $lastStandard = array_pop($installedStandards); + + if ($numStandards === 1) { + $errMsg = 'The only coding standard installed is ' . $lastStandard; + } else { + $standardList = implode(', ', $installedStandards); + $standardList .= ' and ' . $lastStandard; + $errMsg = 'The installed coding standards are ' . $standardList; + } + } + + throw new BuildException( + 'ERROR: the "' . $this->standard . '" coding standard is not installed. ' . $errMsg, + $this->getLocation() + ); + } + + if (count($this->formatters) == 0) { + // turn legacy format attribute into formatter + $fmt = new PhpCodeSnifferTask_FormatterElement(); + $fmt->setType($this->format); + $fmt->setUseFile(false); + $this->formatters[] = $fmt; + } + + if (!isset($this->file)) + { + $fileList = array(); + $project = $this->getProject(); + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getAbsolutePath(); + foreach ($files as $file) { + $fileList[] = $dir.DIRECTORY_SEPARATOR.$file; + } + } + } + + $cwd = getcwd(); + // Save command line arguments because it confuses PHPCS (version 1.3.0) + $oldArgs = $_SERVER['argv']; + $_SERVER['argv'] = array(); + $_SERVER['argc'] = 0; + $codeSniffer = new PHP_CodeSniffer($this->verbosity, $this->tabWidth, $this->encoding); + $codeSniffer->setAllowedFileExtensions($this->allowedFileExtensions); + if (is_array($this->ignorePatterns)) $codeSniffer->setIgnorePatterns($this->ignorePatterns); + foreach ($this->configData as $configData) { + $codeSniffer->setConfigData($configData->getName(), $configData->getValue(), true); + } + + if ($this->file instanceof PhingFile) { + $codeSniffer->process($this->file->getPath(), $this->standard, $this->sniffs, $this->noSubdirectories); + + } else { + $codeSniffer->process($fileList, $this->standard, $this->sniffs, $this->noSubdirectories); + } + // Restore command line arguments + $_SERVER['argv'] = $oldArgs; + $_SERVER['argc'] = count($oldArgs); + chdir($cwd); + + $report = $this->printErrorReport($codeSniffer); + + // generate the documentation + if ($this->docGenerator !== '' && $this->docFile !== null) { + ob_start(); + + $codeSniffer->generateDocs($this->standard, $this->sniffs, $this->docGenerator); + + $output = ob_get_contents(); + ob_end_clean(); + + // write to file + $outputFile = $this->docFile->getPath(); + $check = file_put_contents($outputFile, $output); + + if (is_bool($check) && !$check) { + throw new BuildException('Error writing doc to ' . $outputFile); + } + } elseif ($this->docGenerator !== '' && $this->docFile === null) { + $codeSniffer->generateDocs($this->standard, $this->sniffs, $this->docGenerator); + } + + if ($this->haltonerror && $report['totals']['errors'] > 0) + { + throw new BuildException('phpcodesniffer detected ' . $report['totals']['errors']. ' error' . ($report['totals']['errors'] > 1 ? 's' : '')); + } + + if ($this->haltonwarning && $report['totals']['warnings'] > 0) + { + throw new BuildException('phpcodesniffer detected ' . $report['totals']['warnings'] . ' warning' . ($report['totals']['warnings'] > 1 ? 's' : '')); + } + } + + /** + * Prints the error report. + * + * @param PHP_CodeSniffer $phpcs The PHP_CodeSniffer object containing + * the errors. + * + * @return int The number of error and warning messages shown. + */ + protected function printErrorReport($phpcs) + { + if ($this->showSniffs) { + $sniffs = $phpcs->getSniffs(); + $sniffStr = ''; + foreach ($sniffs as $sniff) { + $sniffStr .= '- ' . $sniff.PHP_EOL; + } + $this->log('The list of used sniffs (#' . count($sniffs) . '): ' . PHP_EOL . $sniffStr, Project::MSG_INFO); + } + + $filesViolations = $phpcs->getFilesErrors(); + $reporting = new PHP_CodeSniffer_Reporting(); + $report = $reporting->prepare($filesViolations, $this->showWarnings); + + // process output + foreach ($this->formatters as $fe) { + switch ($fe->getType()) { + case 'default': + // default format goes to logs, no buffering + $this->outputCustomFormat($report); + $fe->setUseFile(false); + break; + + default: + $reportFile = null; + + if ($fe->getUseFile()) { + $reportFile = $fe->getOutfile(); + ob_start(); + } + + // Determine number of parameters required to + // ensure backwards compatibility + $rm = new ReflectionMethod('PHP_CodeSniffer_Reporting', 'printReport'); + + if ($rm->getNumberOfParameters() == 5) { + $reporting->printReport( + $fe->getType(), + $filesViolations, + $this->showSources, + $reportFile, + $this->reportWidth + ); + } else { + $reporting->printReport( + $fe->getType(), + $filesViolations, + $this->showWarnings, + $this->showSources, + $reportFile, + $this->reportWidth + ); + } + + // reporting class uses ob_end_flush(), but we don't want + // an output if we use a file + if ($fe->getUseFile()) { + ob_end_clean(); + } + break; + } + } + + return $report; + } + + /** + * Outputs the results with a custom format + * + * @param array $report Packaged list of all errors in each file + */ + protected function outputCustomFormat($report) { + $files = $report['files']; + foreach ($files as $file => $attributes) { + $errors = $attributes['errors']; + $warnings = $attributes['warnings']; + $messages = $attributes['messages']; + if ($errors > 0) { + $this->log($file . ': ' . $errors . ' error' . ($errors > 1 ? 's' : '') . ' detected', Project::MSG_ERR); + $this->outputCustomFormatMessages($messages, 'ERROR'); + } else { + $this->log($file . ': No syntax errors detected', Project::MSG_VERBOSE); + } + if ($warnings > 0) { + $this->log($file . ': ' . $warnings . ' warning' . ($warnings > 1 ? 's' : '') . ' detected', Project::MSG_WARN); + $this->outputCustomFormatMessages($messages, 'WARNING'); + } + } + + $totalErrors = $report['totals']['errors']; + $totalWarnings = $report['totals']['warnings']; + $this->log(count($files) . ' files where checked', Project::MSG_INFO); + if ($totalErrors > 0) { + $this->log($totalErrors . ' error' . ($totalErrors > 1 ? 's' : '') . ' detected', Project::MSG_ERR); + } else { + $this->log('No syntax errors detected', Project::MSG_INFO); + } + if ($totalWarnings > 0) { + $this->log($totalWarnings . ' warning' . ($totalWarnings > 1 ? 's' : '') . ' detected', Project::MSG_INFO); + } + } + + /** + * Outputs the messages of a specific type for one file + * @param array $messages + * @param string $type + */ + protected function outputCustomFormatMessages($messages, $type) { + foreach ($messages as $line => $messagesPerLine) { + foreach ($messagesPerLine as $column => $messagesPerColumn) { + foreach ($messagesPerColumn as $message) { + $msgType = $message['type']; + if ($type == $msgType) { + $logLevel = Project::MSG_INFO; + if ($msgType == 'ERROR') { + $logLevel = Project::MSG_ERR; + } else if ($msgType == 'WARNING') { + $logLevel = Project::MSG_WARN; + } + $text = $message['message']; + $string = $msgType . ' in line ' . $line . ' column ' . $column . ': ' . $text; + $this->log($string, $logLevel); + } + } + } + } + } + +} //end phpCodeSnifferTask + +/** + * @package phing.tasks.ext + */ +class PhpCodeSnifferTask_FormatterElement extends DataType { + + /** + * Type of output to generate + * @var string + */ + protected $type = ""; + + /** + * Output to file? + * @var bool + */ + protected $useFile = true; + + /** + * Output file. + * @var string + */ + protected $outfile = ""; + + /** + * Validate config. + */ + public function parsingComplete () { + if(empty($this->type)) { + throw new BuildException("Format missing required 'type' attribute."); + } + if ($useFile && empty($this->outfile)) { + throw new BuildException("Format requires 'outfile' attribute when 'useFile' is true."); + } + + } + + public function setType ($type) { + $this->type = $type; + } + + public function getType () { + return $this->type; + } + + public function setUseFile ($useFile) { + $this->useFile = $useFile; + } + + public function getUseFile () { + return $this->useFile; + } + + public function setOutfile ($outfile) { + $this->outfile = $outfile; + } + + public function getOutfile () { + return $this->outfile; + } + +} //end FormatterElement diff --git a/buildscripts/phing/classes/phing/tasks/ext/PhpLintTask.php b/buildscripts/phing/classes/phing/tasks/ext/PhpLintTask.php new file mode 100644 index 00000000..63ec1812 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/PhpLintTask.php @@ -0,0 +1,278 @@ +<?php +/* + * $Id: 524bae55e1007bc9778c232dd7b437964a66c5a4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/util/DataStore.php'; +require_once 'phing/system/io/FileWriter.php'; + +/** + * A PHP lint task. Checking syntax of one or more PHP source file. + * + * @author Knut Urdalen <knut.urdalen@telio.no> + * @author Stefan Priebsch <stefan.priebsch@e-novative.de> + * @version $Id: 524bae55e1007bc9778c232dd7b437964a66c5a4 $ + * @package phing.tasks.ext + */ +class PhpLintTask extends Task { + + protected $file; // the source file (from xml attribute) + protected $filesets = array(); // all fileset objects assigned to this task + + protected $errorProperty; + protected $haltOnFailure = false; + protected $hasErrors = false; + protected $badFiles = array(); + protected $interpreter = ''; // php interpreter to use for linting + + protected $logLevel = Project::MSG_VERBOSE; + + protected $cache = null; + + protected $tofile = null; + + protected $deprecatedAsError = false; + + /** + * Initialize the interpreter with the Phing property + */ + public function __construct() { + $this->setInterpreter(Phing::getProperty('php.interpreter')); + } + + /** + * Override default php interpreter + * @todo Do some sort of checking if the path is correct but would + * require traversing the systems executeable path too + * @param string $sPhp + */ + public function setInterpreter($sPhp) { + $this->Interpreter = $sPhp; + } + + /** + * The haltonfailure property + * @param boolean $aValue + */ + public function setHaltOnFailure($aValue) { + $this->haltOnFailure = $aValue; + } + + /** + * File to be performed syntax check on + * @param PhingFile $file + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Set an property name in which to put any errors. + * @param string $propname + */ + public function setErrorproperty($propname) + { + $this->errorProperty = $propname; + } + + /** + * Whether to store last-modified times in cache + * + * @param PhingFile $file + */ + public function setCacheFile(PhingFile $file) + { + $this->cache = new DataStore($file); + } + + /** + * File to save error messages to + * + * @param PhingFile $file + */ + public function setToFile(PhingFile $tofile) + { + $this->tofile = $tofile; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Set level of log messages generated (default = info) + * @param string $level + */ + public function setLevel($level) + { + switch ($level) + { + case "error": $this->logLevel = Project::MSG_ERR; break; + case "warning": $this->logLevel = Project::MSG_WARN; break; + case "info": $this->logLevel = Project::MSG_INFO; break; + case "verbose": $this->logLevel = Project::MSG_VERBOSE; break; + case "debug": $this->logLevel = Project::MSG_DEBUG; break; + } + } + + /** + * Sets whether to treat deprecated warnings (introduced in PHP 5.3) as errors + * @param boolean $deprecatedAsError + */ + public function setDeprecatedAsError($deprecatedAsError) + { + $this->deprecatedAsError = $deprecatedAsError; + } + + /** + * Execute lint check against PhingFile or a FileSet + */ + public function main() { + if(!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + if($this->file instanceof PhingFile) { + $this->lint($this->file->getPath()); + } else { // process filesets + $project = $this->getProject(); + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getPath(); + foreach($files as $file) { + $this->lint($dir.DIRECTORY_SEPARATOR.$file); + } + } + } + + // write list of 'bad files' to file (if specified) + if ($this->tofile) { + $writer = new FileWriter($this->tofile); + + foreach ($this->badFiles as $file => $messages) { + foreach ($messages as $msg) { + $writer->write($file . "=" . $msg . PHP_EOL); + } + } + + $writer->close(); + } + + $message = ''; + foreach ($this->badFiles as $file => $messages) { + foreach ($messages as $msg) { + $message .= $file . "=" . $msg . PHP_EOL; + } + } + + // save list of 'bad files' with errors to property errorproperty (if specified) + if ($this->errorProperty) { + $this->project->setProperty($this->errorProperty, $message); + } + + if (!empty($this->cache)) { + $this->cache->commit(); + } + + if ($this->haltOnFailure && $this->hasErrors) { + throw new BuildException('Syntax error(s) in PHP files: ' . $message); + } + } + + /** + * Performs the actual syntax check + * + * @param string $file + * @return void + */ + protected function lint($file) { + $command = $this->Interpreter == '' + ? 'php' + : $this->Interpreter; + $command .= ' -n -l '; + + if ($this->deprecatedAsError) { + $command .= '-d error_reporting=32767 '; + } + + if(file_exists($file)) { + if(is_readable($file)) { + if ($this->cache) + { + $lastmtime = $this->cache->get($file); + + if ($lastmtime >= filemtime($file)) + { + $this->log("Not linting '" . $file . "' due to cache", Project::MSG_DEBUG); + return false; + } + } + + $messages = array(); + $errorCount = 0; + + exec($command.'"'.$file.'" 2>&1', $messages); + + for ($i = 0; $i < count($messages) - 1; $i++) { + $message = $messages[$i]; + if (trim($message) == '') { + continue; + } + + if ((!preg_match('/^(.*)Deprecated:/', $message) || $this->deprecatedAsError) && !preg_match('/^No syntax errors detected/', $message)) { + $this->log($message, Project::MSG_ERR); + + if (!isset($this->badFiles[$file])) { + $this->badFiles[$file] = array(); + } + + array_push($this->badFiles[$file], $message); + + $this->hasErrors = true; + $errorCount++; + } + } + + if (!$errorCount) { + $this->log($file.': No syntax errors detected', $this->logLevel); + + if ($this->cache) + { + $this->cache->put($file, filemtime($file)); + } + } + } else { + throw new BuildException('Permission denied: '.$file); + } + } else { + throw new BuildException('File not found: '.$file); + } + } +} + + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php b/buildscripts/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php new file mode 100644 index 00000000..dc4ccbf7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php @@ -0,0 +1,204 @@ +<?php +/* + * $Id: f81043cad2c0ffe0a2571a0a8dc16a98651eac51 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * ReplaceRegExp is a directory based task for replacing the occurrence of a given regular expression with a substitution + * pattern in a selected file or set of files. + * + * <code> + * <replaceregexp file="${src}/build.properties" + * match="OldProperty=(.*)" + * replace="NewProperty=\1" + * byline="true"/> + * </code> + * + * @author Jonathan Bond-Caron <jbondc@openmv.com> + * @version $Id: f81043cad2c0ffe0a2571a0a8dc16a98651eac51 $ + * @package phing.tasks.system + * @link http://ant.apache.org/manual/OptionalTasks/replaceregexp.html + */ +class ReplaceRegexpTask extends Task { + + /** Single file to process. */ + private $file; + + /** Any filesets that should be processed. */ + private $filesets = array(); + + /** + * Regular expression + * + * @var RegularExpression + */ + private $_regexp; + + /** + * File to apply regexp on + * + * @param string $path + */ + public function setFile(PhingFile $path) + { + $this->file = $path; + } + + /** + * Sets the regexp match pattern + * + * @param string $regexp + */ + public function setMatch( $regexp ) + { + $this->_regexp->setPattern( $regexp ); + } + + /** + * @see setMatch() + */ + public function setPattern( $regexp ) + { + $this->setMatch( $regexp ); + } + + /** + * Sets the replacement string + * + * @param string $string + */ + public function setReplace( $string ) + { + $this->_regexp->setReplace( $string ); + } + + /** + * Sets the regexp flags + * + * @param string $flags + */ + public function setFlags( $flags ) + { + // TODO... $this->_regexp->setFlags( $flags ); + } + + /** + * Match only per line + * + * @param bool $yesNo + */ + public function setByline( $yesNo ) + { + // TODO... $this->_regexp-> + } + + /** Nested creator, adds a set of files (nested fileset attribute). */ + public function createFileSet() + { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + public function init() + { + $this->_regexp = new RegularExpression; + } + + public function main() + { + if ($this->file === null && empty($this->filesets)) { + throw new BuildException("You must specify a file or fileset(s) for the <ReplaceRegexp> task."); + } + + // compile a list of all files to modify, both file attrib and fileset elements + // can be used. + $files = array(); + + if ($this->file !== null) { + $files[] = $this->file; + } + + if (!empty($this->filesets)) { + $filenames = array(); + foreach($this->filesets as $fs) { + try { + $ds = $fs->getDirectoryScanner($this->project); + $filenames = $ds->getIncludedFiles(); // get included filenames + $dir = $fs->getDir($this->project); + foreach ($filenames as $fname) { + $files[] = new PhingFile($dir, $fname); + } + } catch (BuildException $be) { + $this->log($be->getMessage(), Project::MSG_WARN); + } + } + } + + $this->log("Applying Regexp processing to " . count($files) . " files."); + + // These "slots" allow filters to retrieve information about the currently-being-process files + $slot = $this->getRegisterSlot("currentFile"); + $basenameSlot = $this->getRegisterSlot("currentFile.basename"); + + $filter = new FilterChain($this->project); + + $r = new ReplaceRegexp; + $r->setRegexps(array($this->_regexp)); + + $filter->addReplaceRegexp($r); + $filters = array($filter); + + foreach($files as $file) { + // set the register slots + + $slot->setValue($file->getPath()); + $basenameSlot->setValue($file->getName()); + + // 1) read contents of file, pulling through any filters + $in = null; + try { + $contents = ""; + $in = FileUtils::getChainedReader(new FileReader($file), $filters, $this->project); + while(-1 !== ($buffer = $in->read())) { + $contents .= $buffer; + } + $in->close(); + } catch (Exception $e) { + if ($in) $in->close(); + $this->log("Error reading file: " . $e->getMessage(), Project::MSG_WARN); + } + + try { + // now create a FileWriter w/ the same file, and write to the file + $out = new FileWriter($file); + $out->write($contents); + $out->close(); + $this->log("Applying regexp processing to " . $file->getPath(), Project::MSG_VERBOSE); + } catch (Exception $e) { + if ($out) $out->close(); + $this->log("Error writing file back: " . $e->getMessage(), Project::MSG_WARN); + } + + } + + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ScpTask.php b/buildscripts/phing/classes/phing/tasks/ext/ScpTask.php new file mode 100644 index 00000000..f6ceadd1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ScpTask.php @@ -0,0 +1,380 @@ +<?php +/* + * $Id: 300efdab5b721c6312491450bc2ba93ffc8124b4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Copy files to and from a remote host using scp. + * + * @author Michiel Rook <mrook@php.net> + * @author Johan Van den Brande <johan@vandenbrande.com> + * @version $Id: 300efdab5b721c6312491450bc2ba93ffc8124b4 $ + * @package phing.tasks.ext + */ + +class ScpTask extends Task +{ + protected $file = ""; + protected $filesets = array(); // all fileset objects assigned to this task + protected $todir = ""; + protected $mode = null; + + protected $host = ""; + protected $port = 22; + protected $username = ""; + protected $password = ""; + protected $autocreate = true; + protected $fetch = false; + protected $localEndpoint = ""; + protected $remoteEndpoint = ""; + + protected $pubkeyfile = ''; + protected $privkeyfile = ''; + protected $privkeyfilepassphrase = ''; + + protected $connection = null; + protected $sftp = null; + + protected $count = 0; + + protected $logLevel = Project::MSG_VERBOSE; + + /** + * Sets the remote host + */ + public function setHost($h) + { + $this->host = $h; + } + + /** + * Returns the remote host + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the remote host port + */ + public function setPort($p) + { + $this->port = $p; + } + + /** + * Returns the remote host port + */ + public function getPort() + { + return $this->port; + } + + /** + * Sets the mode value + */ + public function setMode($value) + { + $this->mode = $value; + } + + /** + * Returns the mode value + */ + public function getMode() + { + return $this->mode; + } + + /** + * Sets the username of the user to scp + */ + public function setUsername($username) + { + $this->username = $username; + } + + /** + * Returns the username + */ + public function getUsername() + { + return $this->username; + } + + /** + * Sets the password of the user to scp + */ + public function setPassword($password) + { + $this->password = $password; + } + + /** + * Returns the password + */ + public function getPassword() + { + return $this->password; + } + + /** + * Sets the public key file of the user to scp + */ + public function setPubkeyfile($pubkeyfile) + { + $this->pubkeyfile = $pubkeyfile; + } + + /** + * Returns the pubkeyfile + */ + public function getPubkeyfile() + { + return $this->pubkeyfile; + } + + /** + * Sets the private key file of the user to scp + */ + public function setPrivkeyfile($privkeyfile) + { + $this->privkeyfile = $privkeyfile; + } + + /** + * Returns the private keyfile + */ + public function getPrivkeyfile() + { + return $this->privkeyfile; + } + + /** + * Sets the private key file passphrase of the user to scp + */ + public function setPrivkeyfilepassphrase($privkeyfilepassphrase) + { + $this->privkeyfilepassphrase = $privkeyfilepassphrase; + } + + /** + * Returns the private keyfile passphrase + */ + public function getPrivkeyfilepassphrase($privkeyfilepassphrase) + { + return $this->privkeyfilepassphrase; + } + + /** + * Sets whether to autocreate remote directories + */ + public function setAutocreate($autocreate) + { + $this->autocreate = (bool) $autocreate; + } + + /** + * Returns whether to autocreate remote directories + */ + public function getAutocreate() + { + return $this->autocreate; + } + + /** + * Set destination directory + */ + public function setTodir($todir) + { + $this->todir = $todir; + } + + /** + * Returns the destination directory + */ + public function getTodir() + { + return $this->todir; + } + + /** + * Sets local filename + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Returns local filename + */ + public function getFile() + { + return $this->file; + } + + /** + * Sets whether to send (default) or fetch files + */ + public function setFetch($fetch) + { + $this->fetch = (bool) $fetch; + } + + /** + * Returns whether to send (default) or fetch files + */ + public function getFetch() + { + return $this->fetch; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Set level of log messages generated (default = verbose) + * @param string $level + */ + public function setLevel($level) + { + switch ($level) + { + case "error": $this->logLevel = Project::MSG_ERR; break; + case "warning": $this->logLevel = Project::MSG_WARN; break; + case "info": $this->logLevel = Project::MSG_INFO; break; + case "verbose": $this->logLevel = Project::MSG_VERBOSE; break; + case "debug": $this->logLevel = Project::MSG_DEBUG; break; + } + } + + public function init() + { + } + + public function main() + { + if (!function_exists('ssh2_connect')) { + throw new BuildException("To use ScpTask, you need to install the PHP SSH2 extension."); + } + + if ($this->file == "" && empty($this->filesets)) { + throw new BuildException("Missing either a nested fileset or attribute 'file'"); + } + + if ($this->host == "" || $this->username == "") { + throw new BuildException("Attribute 'host' and 'username' must be set"); + } + + $this->connection = ssh2_connect($this->host, $this->port); + if (is_null($this->connection)) { + throw new BuildException("Could not establish connection to " . $this->host . ":" . $this->port . "!"); + } + + $could_auth = null; + if ( $this->pubkeyfile ) { + $could_auth = ssh2_auth_pubkey_file($this->connection, $this->username, $this->pubkeyfile, $this->privkeyfile, $this->privkeyfilepassphrase); + } else { + $could_auth = ssh2_auth_password($this->connection, $this->username, $this->password); + } + if (!$could_auth) { + throw new BuildException("Could not authenticate connection!"); + } + + // prepare sftp resource + if ($this->autocreate) { + $this->sftp = ssh2_sftp($this->connection); + } + + if ($this->file != "") { + $this->copyFile($this->file, basename($this->file)); + } else { + if ($this->fetch) { + throw new BuildException("Unable to use filesets to retrieve files from remote server"); + } + + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getPath(); + foreach($files as $file) { + $path = $dir.DIRECTORY_SEPARATOR.$file; + + // Translate any Windows paths + $this->copyFile($path, strtr($file, '\\', '/')); + } + } + } + + $this->log("Copied " . $this->counter . " file(s) " . ($this->fetch ? "from" : "to") . " '" . $this->host . "'"); + + // explicitly close ssh connection + @ssh2_exec($this->connection, 'exit'); + } + + protected function copyFile($local, $remote) + { + $path = rtrim($this->todir, "/") . "/"; + + if ($this->fetch) { + $localEndpoint = $path . $remote; + $remoteEndpoint = $local; + + $this->log('Will fetch ' . $remoteEndpoint . ' to ' . $localEndpoint, $this->logLevel); + + $ret = @ssh2_scp_recv($this->connection, $remoteEndpoint, $localEndpoint); + + if ($ret === false) { + throw new BuildException("Could not fetch remote file '" . $remoteEndpoint . "'"); + } + } else { + $localEndpoint = $local; + $remoteEndpoint = $path . $remote; + + if ($this->autocreate) { + ssh2_sftp_mkdir($this->sftp, dirname($remoteEndpoint), (is_null($this->mode) ? 0777 : $this->mode), true); + } + + $this->log('Will copy ' . $localEndpoint . ' to ' . $remoteEndpoint, $this->logLevel); + + if (!is_null($this->mode)) { + $ret = @ssh2_scp_send($this->connection, $localEndpoint, $remoteEndpoint, $this->mode); + } else { + $ret = @ssh2_scp_send($this->connection, $localEndpoint, $remoteEndpoint); + } + + if ($ret === false) { + throw new BuildException("Could not create remote file '" . $remoteEndpoint . "'"); + } + } + + $this->counter++; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon.php b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon.php new file mode 100644 index 00000000..6e8fa8e0 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon.php @@ -0,0 +1,120 @@ +<?php + +/* + * $Id: 81e9d8cbc94bac15a6a32ed0bb23c04d2b0ff439 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once "phing/Task.php"; + +/** + * Abstract Service_Amazon class. + * + * Implements common methods & properties used by all Amazon services + * + * @extends Task + * @version $ID$ + * @package phing.tasks.ext + * @author Andrei Serdeliuc <andrei@serdeliuc.ro> + * @abstract + */ +abstract class Service_Amazon extends Task +{ + /** + * Collection of set options + * + * We set these magically so we can also load then from the environment + * + * (default value: array()) + * + * @var array + * @access protected + */ + protected $_options = array(); + + public function __set($var, $val) + { + $this->_options[$var] = $val; + } + + /** + * Property getter + * + * If the property hasn't been previously set (through the task call normally), + * it will try to load it from the project + * + * This way, we can define global properties for the "Amazon" service, like key and secret + * + * @access public + * @param mixed $var + * @return void + */ + public function __get($var) + { + if(!isset($this->$var)) { + if(!($val = $this->getProject()->getProperty('amazon.' . strtolower($var)))) { + return false; + } else { + return $val; + } + } + + return $this->_options[$var]; + } + + public function __isset($var) + { + return array_key_exists($var, $this->_options); + } + + public function setKey($key) + { + if(empty($key) || !is_string($key)) { + throw new BuildException('Key must be a non empty string'); + } + + $this->key = $key; + } + + public function getKey() + { + if(!($key = $this->key)) { + throw new BuildException('Key is not set'); + } + + return $key; + } + + public function setSecret($secret) + { + if(empty($secret) || !is_string($secret)) { + throw new BuildException('Secret must be a non empty string'); + } + + $this->secret = $secret; + } + + public function getSecret() + { + if(!($secret = $this->secret)) { + throw new BuildException('Secret is not set'); + } + + return $this->secret; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3.php b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3.php new file mode 100644 index 00000000..7bed642e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3.php @@ -0,0 +1,188 @@ +<?php + +/* + * $Id: a205dcffd1f42b70a8101808242d66620e3dabbd $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once dirname(dirname(__FILE__)) . "/Amazon.php"; + +/** + * Abstract Service_Amazon_S3 class. + * + * Provides common methods and properties to all of the S3 tasks + * + * @extends Service_Amazon + * @version $ID$ + * @package phing.tasks.ext + * @author Andrei Serdeliuc <andrei@serdeliuc.ro> + * @abstract + */ +abstract class Service_Amazon_S3 extends Service_Amazon +{ + /** + * Services_Amazon_S3 client + * + * (default value: null) + * + * @var Services_Amazon_S3 + * @see Services_Amazon_S3 + * @access protected + */ + protected $_client = null; + + /** + * We only instantiate the client once per task call + * + * @access public + * @return Services_Amazon_S3 + */ + public function getClient() + { + require_once "Services/Amazon/S3.php"; + + if($this->_client === null) { + $this->_client = Services_Amazon_S3::getAccount($this->getKey(), $this->getSecret()); + } + + return $this->_client; + } + + public function setBucket($bucket) + { + if(empty($bucket) || !is_string($bucket)) { + throw new BuildException('Bucket must be a non-empty string'); + } + + $this->bucket = (string) $bucket; + } + + public function getBucket() + { + if(!($bucket = $this->bucket)) { + throw new BuildException('Bucket is not set'); + } + + return $this->bucket; + } + + /** + * Returns an instance of Services_Amazon_S3_Resource_Object + * + * @access public + * @param mixed $object + * @return Services_Amazon_S3_Resource_Object + */ + public function getObjectInstance($object) + { + return $this->getBucketInstance()->getObject($object); + } + + /** + * Check if the object already exists in the current bucket + * + * @access public + * @param mixed $object + * @return bool + */ + public function isObjectAvailable($object) + { + return (bool) $this->getObjectInstance($object)->load(Services_Amazon_S3_Resource_Object::LOAD_METADATA_ONLY); + } + + /** + * Returns an instance of Services_Amazon_S3_Resource_Bucket + * + * @access public + * @return Services_Amazon_S3_Resource_Bucket + */ + public function getBucketInstance() + { + return $this->getClient()->getBucket($this->getBucket()); + } + + /** + * Check if the current bucket is available + * + * @access public + * @return bool + */ + public function isBucketAvailable() + { + return (bool) $this->getBucketInstance($this->getBucket())->load(); + } + + /** + * Get the contents of an object (by it's name) + * + * @access public + * @param string $object + * @return mixed + */ + public function getObjectContents($object) + { + if(!$this->isBucketAvailable($this->getBucket())) { + throw new BuildException('Bucket doesn\'t exist or wrong permissions'); + } + + $bucket = $this->getClient()->getBucket($this->getBucket()); + if(!$this->isObjectAvailable($object)) { + throw new BuildException('Object not available: ' . $object); + } + + $object = $this->getObjectInstance($object); + $object->load(); + return $object->data; + } + + /** + * Create a bucket + * + * @access public + * @return void + */ + public function createBucket() + { + $bucket = $this->getBucketInstance(); + $bucket->name = $this->getBucket(); + $bucket->save(); + + return $this->isBucketAvailable(); + } + + /** + * Main entry point, doesn't do anything + * + * @access public + * @final + * @return void + */ + final public function main() + { + $this->execute(); + } + + /** + * Entry point to children tasks + * + * @access public + * @abstract + * @return void + */ + abstract public function execute(); +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php new file mode 100644 index 00000000..37b4e817 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php @@ -0,0 +1,108 @@ +<?php + +/* + * $Id: 214ed107be71d8dbc0f68ffc90bfd8b11a76b36d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once dirname(dirname(__FILE__)) . '/S3.php'; + +/** + * Downloads an object off S3 + * + * @version $Id: 214ed107be71d8dbc0f68ffc90bfd8b11a76b36d $ + * @package phing.tasks.ext + * @author Andrei Serdeliuc <andrei@serdeliuc.ro> + * @extends Service_Amazon_S3 + */ +class S3GetTask extends Service_Amazon_S3 +{ + /** + * This is where we'll store the object + * + * (default value: null) + * + * @var mixed + * @access protected + */ + protected $_target = null; + + /** + * The S3 object we're working with + * + * (default value: null) + * + * @var mixed + * @access protected + */ + protected $_object = null; + + public function setObject($object) + { + if(empty($object) || !is_string($object)) { + throw new BuildException('Object must be a non-empty string'); + } + + $this->_object = $object; + } + + public function getObject() + { + if($this->_object === null) { + throw new BuildException('Object is not set'); + } + + return $this->_object; + } + + public function setTarget($target) + { + if(!is_file($target) && !is_dir($target) && !is_link($target)) { + if(!is_writable(dirname($target))) { + throw new BuildException('Target is not writable: ' . $target); + } + } else { + if(!is_writable($target)) { + throw new BuildException('Target is not writable: ' . $target); + } + } + + $this->_target = $target; + } + + public function getTarget() + { + if($this->_target === null) { + throw new BuildException('Target is not set'); + } + + return $this->_target; + } + + public function execute() + { + $target = $this->getTarget(); + + // Use the object name as the target if the current target is a directory + if(is_dir($target)) { + $target = rtrim($target, '/') . '/' . $this->getObject(); + } + + file_put_contents($target, $this->getObjectContents($this->getObject())); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php new file mode 100644 index 00000000..dbb18b56 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php @@ -0,0 +1,243 @@ +<?php +/* + * $Id: 84b1d6039427591cbf43dbe1a82691063ae4238a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once dirname(dirname(__FILE__)) . '/S3.php'; + +/** + * Stores an object on S3 + * + * @version $Id: 84b1d6039427591cbf43dbe1a82691063ae4238a $ + * @package phing.tasks.ext + * @author Andrei Serdeliuc <andrei@serdeliuc.ro> + * @extends Service_Amazon_S3 + */ +class S3PutTask extends Service_Amazon_S3 +{ + /** + * File we're trying to upload + * + * (default value: null) + * + * @var string + * @access protected + */ + protected $_source = null; + + /** + * Content we're trying to upload + * + * The user can specify either a file to upload or just a bit of content + * + * (default value: null) + * + * @var mixed + * @access protected + */ + protected $_content = null; + + /** + * Collection of filesets + * Used for uploading multiple files + * + * (default value: array()) + * + * @var array + * @access protected + */ + protected $_filesets = array(); + + /** + * Whether to try to create buckets or not + * + * (default value: false) + * + * @var bool + * @access protected + */ + protected $_createBuckets = false; + + public function setSource($source) + { + if(!is_readable($source)) { + throw new BuildException('Source is not readable: ' . $source); + } + + $this->_source = $source; + } + + public function getSource() + { + if($this->_source === null) { + throw new BuildException('Source is not set'); + } + + return $this->_source; + } + + public function setContent($content) + { + if(empty($content) || !is_string($content)) { + throw new BuildException('Content must be a non-empty string'); + } + + $this->_content = $content; + } + + public function getContent() + { + if($this->_content === null) { + throw new BuildException('Content is not set'); + } + + return $this->_content; + } + + public function setObject($object) + { + if(empty($object) || !is_string($object)) { + throw new BuildException('Object must be a non-empty string'); + } + + $this->_object = $object; + } + + public function getObject() + { + if($this->_object === null) { + throw new BuildException('Object is not set'); + } + + return $this->_object; + } + + public function setCreateBuckets($createBuckets) + { + $this->_createBuckets = (bool) $createBuckets; + } + + public function getCreateBuckets() + { + return (bool) $this->_createBuckets; + } + + /** + * creator for _filesets + * + * @access public + * @return FileSet + */ + public function createFileset() + { + $num = array_push($this->_filesets, new FileSet()); + return $this->_filesets[$num-1]; + } + + /** + * getter for _filesets + * + * @access public + * @return array + */ + public function getFilesets() + { + return $this->_filesets; + } + + /** + * Determines what we're going to store in the object + * + * If _content has been set, this will get stored, + * otherwise, we read from _source + * + * @access public + * @return string + */ + public function getObjectData() + { + try { + $content = $this->getContent(); + } catch(BuildException $e) { + $source = $this->getSource(); + + if(!is_file($source)) { + throw new BuildException('Currently only files can be used as source'); + } + + $content = file_get_contents($source); + } + + return $content; + } + + /** + * Store the object on S3 + * + * @access public + * @return void + */ + public function execute() + { + if(!$this->isBucketAvailable()) { + if(!$this->getCreateBuckets()) { + throw new BuildException('Bucket doesn\'t exist and createBuckets not specified'); + } else{ + if(!$this->createBucket()) { + throw new BuildException('Bucket cannot be created'); + } + } + } + + // Filesets take precedence + if(!empty($this->_filesets)) { + $objects = array(); + + foreach($this->_filesets as $fs) { + if(!($fs instanceof FileSet)) { + continue; + } + + $ds = $fs->getDirectoryScanner($this->getProject()); + $objects = array_merge($objects, $ds->getIncludedFiles()); + } + + $fromDir = $fs->getDir($this->getProject())->getAbsolutePath(); + + foreach($objects as $object) { + $this->saveObject($object, file_get_contents($fromDir . DIRECTORY_SEPARATOR . $object)); + } + + return true; + } + + $this->saveObject($this->getObject(), $this->getObjectData()); + } + + protected function saveObject($object, $data) + { + $object = $this->getObjectInstance($object); + $object->data = $data; + $object->save(); + + if(!$this->isObjectAvailable($object->key)) { + throw new BuildException('Upload failed'); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/SmartyTask.php b/buildscripts/phing/classes/phing/tasks/ext/SmartyTask.php new file mode 100644 index 00000000..69c7b8f8 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/SmartyTask.php @@ -0,0 +1,610 @@ +<?php + +/* + * $Id: 1fe8b2aa2668db628554e59b3099520c0e1c03e4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/BuildException.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * A phing task for generating output by using Smarty. + * + * This is based on the TexenTask from Apache's Velocity engine. This class + * was originally proted in order to provide a template compiling system for + * Torque. + * + * TODO: + * - Add Path / useClasspath support? + * + * @author Hans Lellelid <hans@xmpl.org> (SmartyTask) + * @author Jason van Zyl <jvanzyl@apache.org> (TexenTask) + * @author Robert Burrell Donkin <robertdonkin@mac.com> + * @version $Id: 1fe8b2aa2668db628554e59b3099520c0e1c03e4 $ + * @package phing.tasks.ext + */ +class SmartyTask extends Task { + + /** + * Smarty template engine. + * @var Smarty + */ + protected $context; + + /** + * Variables that are assigned to the context on parse/compile. + * @var array + */ + protected $properties = array(); + + /** + * This is the control template that governs the output. + * It may or may not invoke the services of worker + * templates. + * @var string + */ + protected $controlTemplate; + + /** + * This is where Velocity will look for templates + * using the file template loader. + * @var string + */ + protected $templatePath; + + /** + * This is where texen will place all the output + * that is a product of the generation process. + * @var string + */ + protected $outputDirectory; + + /** + * This is the file where the generated text + * will be placed. + * @var string + */ + protected $outputFile; + + /** + * <p> + * These are properties that are fed into the + * initial context from a properties file. This + * is simply a convenient way to set some values + * that you wish to make available in the context. + * </p> + * <p> + * These values are not critical, like the template path + * or output path, but allow a convenient way to + * set a value that may be specific to a particular + * generation task. + * </p> + * <p> + * For example, if you are generating scripts to allow + * user to automatically create a database, then + * you might want the <code>$databaseName</code> + * to be placed + * in the initial context so that it is available + * in a script that might look something like the + * following: + * <code><pre> + * #!bin/sh + * + * echo y | mysqladmin create $databaseName + * </pre></code> + * The value of <code>$databaseName</code> isn't critical to + * output, and you obviously don't want to change + * the ant task to simply take a database name. + * So initial context values can be set with + * properties file. + * + * @var array + */ + protected $contextProperties; + + /** + * Smarty compiles templates before parsing / replacing tokens in them. + * By default it will try ./templates_c, but you may wish to override this. + * @var string + */ + protected $compilePath; + + /** + * Whether to force Smarty to recompile templates. + * Smarty does check file modification time, but you can set this + * to be *sure* that the template will be compiled (of course it will + * be slower if you do). + * @var boolean + */ + protected $forceCompile = false; + + /** + * Smarty can use config files. + * This tells Smarty where to look for the config files. + * @var string + */ + protected $configPath; + + /** + * Customize the left delimiter for Smarty tags. + * @var string + */ + protected $leftDelimiter; + + /** + * Customize the right delimiter for Smarty tags. + * @var string + */ + protected $rightDelimiter; + + // ----------------------------------------------------------------------- + // The following getters & setters are used by phing to set properties + // specified in the XML for the smarty task. + // ----------------------------------------------------------------------- + + public function init() { + include_once 'Smarty.class.php'; + if (!class_exists('Smarty')) { + throw new BuildException("To use SmartyTask, you must have the path to Smarty.class.php on your include_path or your \$PHP_CLASSPATH environment variable."); + } + } + + /** + * [REQUIRED] Set the control template for the + * generating process. + * @param string $controlTemplate + * @return void + */ + public function setControlTemplate ($controlTemplate) { + $this->controlTemplate = $controlTemplate; + } + + /** + * Get the control template for the + * generating process. + * @return string + */ + public function getControlTemplate() { + return $this->controlTemplate; + } + + /** + * [REQUIRED] Set the path where Velocity will look + * for templates using the file template + * loader. + * @return void + * @throws Exception + */ + public function setTemplatePath($templatePath) { + $resolvedPath = ""; + $tok = strtok($templatePath, ","); + while ( $tok ) { + // resolve relative path from basedir and leave + // absolute path untouched. + $fullPath = $this->project->resolveFile($tok); + $cpath = $fullPath->getCanonicalPath(); + if ($cpath === false) { + $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath()); + } else { + $resolvedPath .= $cpath; + } + $tok = strtok(","); + if ( $tok ) { + $resolvedPath .= ","; + } + } + $this->templatePath = $resolvedPath; + } + + /** + * Get the path where Velocity will look + * for templates using the file template + * loader. + * @return string + */ + public function getTemplatePath() { + return $this->templatePath; + } + + /** + * [REQUIRED] Set the output directory. It will be + * created if it doesn't exist. + * @param PhingFile $outputDirectory + * @return void + * @throws Exception + */ + public function setOutputDirectory(PhingFile $outputDirectory) { + try { + if (!$outputDirectory->exists()) { + $this->log("Output directory does not exist, creating: " . $outputDirectory->getPath(),Project::MSG_VERBOSE); + if (!$outputDirectory->mkdirs()) { + throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath()); + } + } + $this->outputDirectory = $outputDirectory->getCanonicalPath(); + } catch (IOException $ioe) { + throw new BuildException($ioe->getMessage()); + } + } + + /** + * Get the output directory. + * @return string + */ + public function getOutputDirectory() { + return $this->outputDirectory; + } + + /** + * [REQUIRED] Set the output file for the + * generation process. + * @return void + */ + public function setOutputFile($outputFile) { + $this->outputFile = $outputFile; + } + + /** + * Get the output file for the + * generation process. + * @return string + */ + public function getOutputFile() { + return $this->outputFile; + } + + /** + * Set the path Smarty uses as a "cache" for compiled templates. + * @param string $compilePath + */ + public function setCompilePath($compilePath) { + $this->compilePath = $compilePath; + } + + /** + * Get the path Smarty uses for compiling templates. + * @return string + */ + public function getCompilePath() { + return $this->compilePath; + } + + /** + * Set whether Smarty should always recompile tempaltes. + * @param boolean $force + * @return void + */ + public function setForceCompile($force) { + $this->forceCompile = (boolean) $force; + } + + /** + * Get whether Smarty should always recompile template. + * @return boolean + */ + public function getForceCompile() { + return $this->forceCompile; + } + + /** + * Set where Smarty looks for config files. + * @param string $configPath + * @return void + */ + public function setConfigPath($configPath) { + $this->configPath = $configPath; + } + + /** + * Get the path that Smarty uses for looking for config files. + * @return string + */ + public function getConfigPath() { + return $this->configPath; + } + + /** + * Set Smarty template left delimiter. + * @param string $delim + * @return void + */ + public function setLeftDelimiter($delim) { + $this->leftDelimiter = $delim; + } + + /** + * Get Smarty template right delimiter + * @return string + */ + public function getLeftDelimiter() { + return $this->leftDelimiter; + } + + /** + * Set Smarty template right delimiter. + * @param string $delim + * @return void + */ + public function setRightDelimiter($delim) { + $this->rightDelimiter = $delim; + } + + /** + * Get Smarty template right delimiter + * @return string + */ + public function getRightDelimiter() { + return $this->rightDelimiter; + } + + + /** + * Set the context properties that will be + * fed into the initial context be the + * generating process starts. + * @param string $file + * @return void + */ + public function setContextProperties($file) { + + $sources = explode(",", $file); + $this->contextProperties = new Properties(); + + // Always try to get the context properties resource + // from a file first. Templates may be taken from a JAR + // file but the context properties resource may be a + // resource in the filesystem. If this fails than attempt + // to get the context properties resource from the + // classpath. + for ($i=0, $sourcesLength=count($sources); $i < $sourcesLength; $i++) { + $source = new Properties(); + + try { + + // resolve relative path from basedir and leave + // absolute path untouched. + $fullPath = $this->project->resolveFile($sources[$i]); + $this->log("Using contextProperties file: " . $fullPath->__toString()); + $source->load($fullPath); + + } catch (Exception $e) { + + throw new BuildException("Context properties file " . $sources[$i] . + " could not be found in the file system!"); + + } + + $keys = $source->keys(); + + foreach ($keys as $key) { + $name = $key; + $value = $this->project->replaceProperties($source->getProperty($name)); + $this->contextProperties->setProperty($name, $value); + } + } + } + + /** + * Get the context properties that will be + * fed into the initial context be the + * generating process starts. + * @return Properties + */ + public function getContextProperties() { + return $this->contextProperties; + } + + // --------------------------------------------------------------- + // End of XML setters & getters + // --------------------------------------------------------------- + + + /** + * Creates a Smarty object. + * + * @return Smarty initialized (cleared) Smarty context. + * @throws Exception the execute method will catch + * and rethrow as a <code>BuildException</code> + */ + public function initControlContext() { + $this->context->clear_all_assign(); + return $this->context; + } + + /** + * Execute the input script with Velocity + * + * @throws BuildException + * BuildExceptions are thrown when required attributes are missing. + * Exceptions thrown by Velocity are rethrown as BuildExceptions. + */ + public function main() { + + // Make sure the template path is set. + if (empty($this->templatePath)) { + throw new BuildException("The template path needs to be defined!"); + } + + // Make sure the control template is set. + if ($this->controlTemplate === null) { + throw new BuildException("The control template needs to be defined!"); + } + + // Make sure the output directory is set. + if ($this->outputDirectory === null) { + throw new BuildException("The output directory needs to be defined!"); + } + + // Make sure there is an output file. + if ($this->outputFile === null) { + throw new BuildException("The output file needs to be defined!"); + } + + // Setup Smarty runtime. + + // Smarty uses one object to store properties and to store + // the context for the template (unlike Velocity). We setup this object, calling it + // $this->context, and then initControlContext simply zeros out + // any assigned variables. + $this->context = new Smarty(); + + if ($this->compilePath !== null) { + $this->log("Using compilePath: " . $this->compilePath); + $this->context->compile_dir = $this->compilePath; + } + + if ($this->configPath !== null) { + $this->log("Using configPath: " . $this->configPath); + $this->context->config_dir = $this->configPath; + } + + if ($this->forceCompile !== null) { + $this->context->force_compile = $this->forceCompile; + } + + if ($this->leftDelimiter !== null) { + $this->context->left_delimiter = $this->leftDelimiter; + } + + if ($this->rightDelimiter !== null) { + $this->context->right_delimiter = $this->rightDelimiter; + } + + if ($this->templatePath !== null) { + $this->log("Using templatePath: " . $this->templatePath); + $this->context->template_dir = $this->templatePath; + } + + $smartyCompilePath = new PhingFile($this->context->compile_dir); + if (!$smartyCompilePath->exists()) { + $this->log("Compile directory does not exist, creating: " . $smartyCompilePath->getPath(), Project::MSG_VERBOSE); + if (!$smartyCompilePath->mkdirs()) { + throw new BuildException("Smarty needs a place to compile templates; specify a 'compilePath' or create ".$this->context->compile_dir); + } + } + + // Make sure the output directory exists, if it doesn't + // then create it. + $file = new PhingFile($this->outputDirectory); + if (!$file->exists()) { + $this->log("Output directory does not exist, creating: " . $file->getAbsolutePath()); + $file->mkdirs(); + } + + $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile; + $this->log("Generating to file " . $path); + + $writer = new FileWriter($path); + + // The generator and the output path should + // be placed in the init context here and + // not in the generator class itself. + $c = $this->initControlContext(); + + // Set any variables that need to always + // be loaded + $this->populateInitialContext($c); + + // Feed all the options into the initial + // control context so they are available + // in the control/worker templates. + if ($this->contextProperties !== null) { + + foreach($this->contextProperties->keys() as $property) { + + $value = $this->contextProperties->getProperty($property); + + // Special exception (from Texen) + // for properties ending in file.contents: + // in that case we dump the contents of the file + // as the "value" for the Property. + if (StringHelper::endsWith("file.contents", $property)) { + // pull in contents of file specified + + $property = substr($property, 0, strpos($property, "file.contents") - 1); + + // reset value, and then + // read in teh contents of the file into that var + $value = ""; + $f = new PhingFile($this->project->resolveFile($value)->getCanonicalPath()); + if ($f->exists()) { + try { + $fr = new FileReader($f); + $fr->readInto($value); + } catch (Exception $e) { + throw $e; + } + } + + } // if ends with file.contents + + if (StringHelper::isBoolean($value)) { + $value = StringHelper::booleanValue($value); + } + + $c->assign($property, $value); + + } // foreach property + + } // if contextProperties !== null + + try { + //$c->display($this->controlTemplate); + $writer->write($c->fetch($this->controlTemplate)); + $writer->close(); + } catch (IOException $ioe) { + $writer->close(); + throw new BuildException("Cannot write parsed template."); + } + + $this->cleanup(); + } + + /** + * <p>Place useful objects into the initial context.</p> + * + * <p>TexenTask places <code>Date().toString()</code> into the + * context as <code>$now</code>. Subclasses who want to vary the + * objects in the context should override this method.</p> + * + * <p><code>$generator</code> is not put into the context in this + * method.</p> + * + * @param context The context to populate, as retrieved from + * {@link #initControlContext()}. + * @return void + * @throws Exception Error while populating context. The {@link + * #execute()} method will catch and rethrow as a + * <code>BuildException</code>. + */ + protected function populateInitialContext(Smarty $context) { + } + + /** + * A hook method called at the end of {@link #execute()} which can + * be overridden to perform any necessary cleanup activities (such + * as the release of database connections, etc.). By default, + * does nothing. + * @return void + * @throws Exception Problem cleaning up. + */ + protected function cleanup() { + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/SshTask.php b/buildscripts/phing/classes/phing/tasks/ext/SshTask.php new file mode 100644 index 00000000..9c2349a8 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/SshTask.php @@ -0,0 +1,224 @@ +<?php +/* + * $Id: e0fe77ed287d359bd7449d459769370e6192417f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Execute commands on a remote host using ssh. + * + * @author Johan Van den Brande <johan@vandenbrande.com> + * @version $Id: e0fe77ed287d359bd7449d459769370e6192417f $ + * @package phing.tasks.ext + */ +class SshTask extends Task { + + private $host = ""; + private $port = 22; + private $username = ""; + private $password = ""; + private $command = ""; + private $pubkeyfile = ''; + private $privkeyfile = ''; + private $privkeyfilepassphrase = ''; + + /** + * The name of the property to capture (any) output of the command + * @var string + */ + private $property = ""; + + /** + * Whether to display the output of the command + * @var boolean + */ + private $display = true; + + public function setHost($host) + { + $this->host = $host; + } + + public function getHost() + { + return $this->host; + } + + public function setPort($port) + { + $this->port = $port; + } + + public function getPort() + { + return $this->port; + } + + public function setUsername($username) + { + $this->username = $username; + } + + public function getUsername() + { + return $this->username; + } + + public function setPassword($password) + { + $this->password = $password; + } + + public function getPassword() + { + return $this->password; + } + + /** + * Sets the public key file of the user to scp + */ + public function setPubkeyfile($pubkeyfile) + { + $this->pubkeyfile = $pubkeyfile; + } + + /** + * Returns the pubkeyfile + */ + public function getPubkeyfile() + { + return $this->pubkeyfile; + } + + /** + * Sets the private key file of the user to scp + */ + public function setPrivkeyfile($privkeyfile) + { + $this->privkeyfile = $privkeyfile; + } + + /** + * Returns the private keyfile + */ + public function getPrivkeyfile() + { + return $this->privkeyfile; + } + + /** + * Sets the private key file passphrase of the user to scp + */ + public function setPrivkeyfilepassphrase($privkeyfilepassphrase) + { + $this->privkeyfilepassphrase = $privkeyfilepassphrase; + } + + /** + * Returns the private keyfile passphrase + */ + public function getPrivkeyfilepassphrase($privkeyfilepassphrase) + { + return $this->privkeyfilepassphrase; + } + + public function setCommand($command) + { + $this->command = $command; + } + + public function getCommand() + { + return $this->command; + } + + /** + * Sets the name of the property to capture (any) output of the command + * @param string $property + */ + public function setProperty($property) + { + $this->property = $property; + } + + /** + * Sets whether to display the output of the command + * @param boolean $display + */ + public function setDisplay($display) + { + $this->display = (boolean) $display; + } + + public function init() + { + } + + public function main() + { + if (!function_exists('ssh2_connect')) { + throw new BuildException("To use SshTask, you need to install the PHP SSH2 extension."); + } + + $this->connection = ssh2_connect($this->host, $this->port); + if (is_null($this->connection)) { + throw new BuildException("Could not establish connection to " . $this->host . ":" . $this->port . "!"); + } + + $could_auth = null; + if ( $this->pubkeyfile ) { + $could_auth = ssh2_auth_pubkey_file($this->connection, $this->username, $this->pubkeyfile, $this->privkeyfile, $this->privkeyfilepassphrase); + } else { + $could_auth = ssh2_auth_password($this->connection, $this->username, $this->password); + } + if (!$could_auth) { + throw new BuildException("Could not authenticate connection!"); + } + + $stream = ssh2_exec($this->connection, $this->command); + if (!$stream) { + throw new BuildException("Could not execute command!"); + } + + $this->log("Executing command {$this->command}", Project::MSG_VERBOSE); + + stream_set_blocking($stream, true); + $result = stream_get_contents($stream); + + if (!strlen($result)) { + $stderr_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR); + stream_set_blocking($stderr_stream, true); + $result = stream_get_contents($stderr_stream); + } + + if ($this->display) { + print($result); + } + + if (!empty($this->property)) { + $this->project->setProperty($this->property, $result); + } + + fclose($stream); + if (isset($stderr_stream)) { + fclose($stderr_stream); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php b/buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php new file mode 100644 index 00000000..564fdef4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php @@ -0,0 +1,96 @@ +<?php +require_once "phing/types/DataType.php"; + + +/** + * Implementation of console argument + * + * @author nuno costa <nuno@francodacosta.com> + * @license GPL + */ +class Arg extends DataType +{ + private $name = null; + private $value = null; + private $quotes = false; + + /** + * Gets the argment name + * @return String + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the argument name + * @param String $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the argument value + * @return String + */ + public function getValue() + { + return $this->value; + } + + /** + * Sets the argument value + * @param String $value + */ + public function setValue($value) + { + $this->value = $value; + } + + /** + * Should the argument value be enclosed in double quotes + * @return boolean + */ + public function getQuotes() + { + return $this->quotes; + } + + /** + * Should the argument value be enclosed in double quotes + * @param boolean $quotes + */ + public function setQuotes( $quotes) + { + $this->quotes = $quotes; + } + + /** + * Transforms the argument object into a string, takes into consideration + * the quotes and the argument value + * @return String + */ + public function __toString() + { + $name = ""; + $value = ""; + $quote = $this->getQuotes() ? '"' : ''; + + if (!is_null($this->getValue())) { + $value = $quote . $this->getValue() . $quote ; + } + + if (!is_null($this->getName())) { + $name = '--' . $this->getName(); + } + + if (strlen($name) > 0 && strlen($value) > 0) { + $value = '=' . $value; + } + return $name . ' ' . $value; + } + +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php b/buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php new file mode 100644 index 00000000..64c1f02b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php @@ -0,0 +1,113 @@ +<?php +require_once "phing/Task.php"; +require_once dirname(__FILE__) . "/Arg.php"; +/** + * Symfony Console Task + * @author nuno costa <nuno@francodacosta.com> + * @license GPL + * + */ +class SymfonyConsoleTask extends Task +{ + + /** + * + * @var Array of Arg a collection of Arg objects + */ + private $args = array(); + + /** + * + * @var string the Symfony console command to execute + */ + private $command = null; + + /** + * + * @var string path to symfony console application + */ + private $console = 'app/console'; + + + /** + * sets the symfony console command to execute + * @param string $command + */ + public function setCommand($command) + { + $this->command = $command; + } + + /** + * return the symfony console command to execute + * @return String + */ + public function getCommand() + { + return $this->command; + } + + /** + * sets the path to symfony console application + * @param string $console + */ + public function setConsole($console) + { + $this->console = $console; + } + + /** + * returns the path to symfony console application + * @return string + */ + public function getConsole() + { + return $this->console; + } + + /** + * appends an arg tag to the arguments stack + * + * @return Arg Argument object + */ + + public function createArg() + { + $num = array_push($this->args, new Arg());
+ return $this->args[$num-1]; + } + + /** + * return the argumments passed to this task + * @return array of Arg() + */ + public function getArgs() + { + return $this->args; + } + + + /** + * Gets the command string to be executed + * @return string + */ + public function getCmdString() { + $cmd = array(
+ $this->console,
+ $this->command,
+ implode(' ', $this->args)
+ );
+ $cmd = implode(' ', $cmd); + return $cmd; + } + /** + * executes the synfony consile application + */ + public function main() + { + $cmd = $this->getCmdString(); + + $this->log("executing $cmd"); + passthru ($cmd); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/SymlinkTask.php b/buildscripts/phing/classes/phing/tasks/ext/SymlinkTask.php new file mode 100644 index 00000000..57738398 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/SymlinkTask.php @@ -0,0 +1,309 @@ +<?php + +/* + * $Id: 6efb50d5b7cb94f2f22db6e876010e718aa25b22 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once "phing/Task.php"; + +/** + * Generates symlinks based on a target / link combination. + * Can also symlink contents of a directory, individually + * + * Single target symlink example: + * <code> + * <symlink target="/some/shared/file" link="${project.basedir}/htdocs/my_file" /> + * </code> + * + * Symlink entire contents of directory + * + * This will go through the contents of "/my/shared/library/*" + * and create a symlink for each entry into ${project.basedir}/library/ + * <code> + * <symlink link="${project.basedir}/library"> + * <fileset dir="/my/shared/library"> + * <include name="*" /> + * </fileset> + * </symlink> + * </code> + * + * @author Andrei Serdeliuc <andrei@serdeliuc.ro> + * @extends Task + * @version $ID$ + * @package phing.tasks.ext + */ +class SymlinkTask extends Task +{ + /** + * What we're symlinking from + * + * (default value: null) + * + * @var string + * @access private + */ + private $_target = null; + + /** + * Symlink location + * + * (default value: null) + * + * @var string + * @access private + */ + private $_link = null; + + /** + * Collection of filesets + * Used when linking contents of a directory + * + * (default value: array()) + * + * @var array + * @access private + */ + private $_filesets = array(); + + /** + * Whether to override the symlink if it exists but points + * to a different location + * + * (default value: false) + * + * @var boolean + * @access private + */ + private $_overwrite = false; + + /** + * setter for _target + * + * @access public + * @param string $target + * @return void + */ + public function setTarget($target) + { + $this->_target = $target; + } + + /** + * setter for _link + * + * @access public + * @param string $link + * @return void + */ + public function setLink($link) + { + $this->_link = $link; + } + + /** + * creator for _filesets + * + * @access public + * @return FileSet + */ + public function createFileset() + { + $num = array_push($this->_filesets, new FileSet()); + return $this->_filesets[$num-1]; + } + + /** + * setter for _overwrite + * + * @access public + * @param boolean $overwrite + * @return void + */ + public function setOverwrite($overwrite) + { + $this->_overwrite = $overwrite; + } + + /** + * getter for _target + * + * @access public + * @return string + */ + public function getTarget() + { + if($this->_target === null) { + throw new BuildException('Target not set'); + } + + return $this->_target; + } + + /** + * getter for _link + * + * @access public + * @return string + */ + public function getLink() + { + if($this->_link === null) { + throw new BuildException('Link not set'); + } + + return $this->_link; + } + + /** + * getter for _filesets + * + * @access public + * @return array + */ + public function getFilesets() + { + return $this->_filesets; + } + + /** + * getter for _overwrite + * + * @access public + * @return boolean + */ + public function getOverwrite() + { + return $this->_overwrite; + } + + /** + * Generates an array of directories / files to be linked + * If _filesets is empty, returns getTarget() + * + * @access protected + * @return array|string + */ + protected function getMap() + { + $fileSets = $this->getFilesets(); + + // No filesets set + // We're assuming single file / directory + if(empty($fileSets)) { + return $this->getTarget(); + } + + $targets = array(); + + foreach($fileSets as $fs) { + if(!($fs instanceof FileSet)) { + continue; + } + + // We need a directory to store the links + if(!is_dir($this->getLink())) { + throw new BuildException('Link must be an existing directory when using fileset'); + } + + $fromDir = $fs->getDir($this->getProject())->getAbsolutePath(); + + if(!is_dir($fromDir)) { + $this->log('Directory doesn\'t exist: ' . $fromDir, Project::MSG_WARN); + continue; + } + + $fsTargets = array(); + + $ds = $fs->getDirectoryScanner($this->getProject()); + + $fsTargets = array_merge( + $fsTargets, + $ds->getIncludedDirectories(), + $ds->getIncludedFiles() + ); + + // Add each target to the map + foreach($fsTargets as $target) { + if(!empty($target)) { + $targets[$target] = $fromDir . DIRECTORY_SEPARATOR . $target; + } + } + } + + return $targets; + } + + /** + * Main entry point for task + * + * @access public + * @return bool + */ + public function main() + { + $map = $this->getMap(); + + // Single file symlink + if(is_string($map)) { + return $this->symlink($map, $this->getLink()); + } + + // Multiple symlinks + foreach($map as $name => $targetPath) { + $this->symlink($targetPath, $this->getLink() . DIRECTORY_SEPARATOR . $name); + } + + return true; + } + + /** + * Create the actual link + * + * @access protected + * @param string $target + * @param string $link + * @return bool + */ + protected function symlink($target, $link) + { + $fs = FileSystem::getFileSystem(); + + if (is_link($link) && readlink($link) == $target) { + $this->log('Link exists: ' . $link, Project::MSG_INFO); + return true; + } elseif (file_exists($link)) { + if (!$this->getOverwrite()) { + $this->log('Not overwriting existing link ' . $link, Project::MSG_ERR); + return false; + } + + if (is_link($link) || is_file($link)) { + $fs->unlink($link); + $this->log('Link removed: ' . $link, Project::MSG_INFO); + } else { + $fs->rmdir($link, true); + $this->log('Directory removed: ' . $link, Project::MSG_INFO); + } + } + + $this->log('Linking: ' . $target . ' to ' . $link, Project::MSG_INFO); + + return $fs->symlink($target, $link); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/TarTask.php b/buildscripts/phing/classes/phing/tasks/ext/TarTask.php new file mode 100644 index 00000000..95d915d7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/TarTask.php @@ -0,0 +1,445 @@ +<?php +/* + * $Id: c3ac5fcdf4d7cdb199d57b021e3f015c9c7fd3f8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; +include_once 'phing/util/SourceFileScanner.php'; +include_once 'phing/mappers/MergeMapper.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * Creates a tar archive using PEAR Archive_Tar. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Stefano Mazzocchi <stefano@apache.org> (Ant) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @author Magesh Umasankar + * @version $Id: c3ac5fcdf4d7cdb199d57b021e3f015c9c7fd3f8 $ + * @package phing.tasks.ext + */ +class TarTask extends MatchingTask { + + const TAR_NAMELEN = 100; + + const WARN = "warn"; + const FAIL = "fail"; + const OMIT = "omit"; + + private $tarFile; + private $baseDir; + private $includeEmpty = true; // Whether to include empty dirs in the TAR + + private $longFileMode = "warn"; + + private $filesets = array(); + private $fileSetFiles = array(); + + /** + * Indicates whether the user has been warned about long files already. + */ + private $longWarningGiven = false; + + /** + * Compression mode. Available options "gzip", "bzip2", "none" (null). + */ + private $compression = null; + + /** + * File path prefix in the tar archive + * + * @var string + */ + private $prefix = null; + + /** + * Ensures that PEAR lib exists. + */ + public function init() { + include_once 'Archive/Tar.php'; + if (!class_exists('Archive_Tar')) { + throw new BuildException("You must have installed the PEAR Archive_Tar class in order to use TarTask."); + } + } + + /** + * Add a new fileset + * @return FileSet + */ + public function createTarFileSet() { + $this->fileset = new TarFileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + + /** + * Add a new fileset. Alias to createTarFileSet() for backwards compatibility. + * @return FileSet + * @see createTarFileSet() + */ + public function createFileSet() { + $this->fileset = new TarFileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + + /** + * Set is the name/location of where to create the tar file. + * @param PhingFile $destFile The output of the tar + */ + public function setDestFile(PhingFile $destFile) { + $this->tarFile = $destFile; + } + + /** + * This is the base directory to look in for things to tar. + * @param PhingFile $baseDir + */ + public function setBasedir(PhingFile $baseDir) { + $this->baseDir = $baseDir; + } + + /** + * Set the include empty dirs flag. + * @param boolean Flag if empty dirs should be tarred too + * @return void + * @access public + */ + public function setIncludeEmptyDirs($bool) { + $this->includeEmpty = (boolean) $bool; + } + + /** + * Set how to handle long files, those with a path>100 chars. + * Optional, default=warn. + * <p> + * Allowable values are + * <ul> + * <li> truncate - paths are truncated to the maximum length + * <li> fail - paths greater than the maximim cause a build exception + * <li> warn - paths greater than the maximum cause a warning and GNU is used + * <li> gnu - GNU extensions are used for any paths greater than the maximum. + * <li> omit - paths greater than the maximum are omitted from the archive + * </ul> + */ + public function setLongfile($mode) { + $this->longFileMode = $mode; + } + + /** + * Set compression method. + * Allowable values are + * <ul> + * <li> none - no compression + * <li> gzip - Gzip compression + * <li> bzip2 - Bzip2 compression + * </ul> + */ + public function setCompression($mode) { + switch($mode) { + case "gzip": + $this->compression = "gz"; + break; + case "bzip2": + $this->compression = "bz2"; + break; + case "none": + $this->compression = null; + break; + default: + $this->log("Ignoring unknown compression mode: ".$mode, Project::MSG_WARN); + $this->compression = null; + } + } + + /** + * Sets the file path prefix for file in the tar file. + * + * @param string $prefix Prefix + * + * @return void + */ + public function setPrefix($prefix) { + $this->prefix = $prefix; + } + + /** + * do the work + * @throws BuildException + */ + public function main() { + + if ($this->tarFile === null) { + throw new BuildException("tarfile attribute must be set!", $this->getLocation()); + } + + if ($this->tarFile->exists() && $this->tarFile->isDirectory()) { + throw new BuildException("tarfile is a directory!", $this->getLocation()); + } + + if ($this->tarFile->exists() && !$this->tarFile->canWrite()) { + throw new BuildException("Can not write to the specified tarfile!", $this->getLocation()); + } + + // shouldn't need to clone, since the entries in filesets + // themselves won't be modified -- only elements will be added + $savedFileSets = $this->filesets; + + try { + if ($this->baseDir !== null) { + if (!$this->baseDir->exists()) { + throw new BuildException("basedir '" . (string) $this->baseDir . "' does not exist!", $this->getLocation()); + } + if (empty($this->filesets)) { // if there weren't any explicit filesets specivied, then + // create a default, all-inclusive fileset using the specified basedir. + $mainFileSet = new TarFileSet($this->fileset); + $mainFileSet->setDir($this->baseDir); + $this->filesets[] = $mainFileSet; + } + } + + if (empty($this->filesets)) { + throw new BuildException("You must supply either a basedir " + . "attribute or some nested filesets.", + $this->getLocation()); + } + + // check if tar is out of date with respect to each fileset + if($this->tarFile->exists()) { + $upToDate = true; + foreach($this->filesets as $fs) { + $files = $fs->getFiles($this->project, $this->includeEmpty); + if (!$this->archiveIsUpToDate($files, $fs->getDir($this->project))) { + $upToDate = false; + } + for ($i=0, $fcount=count($files); $i < $fcount; $i++) { + if ($this->tarFile->equals(new PhingFile($fs->getDir($this->project), $files[$i]))) { + throw new BuildException("A tar file cannot include itself", $this->getLocation()); + } + } + } + if ($upToDate) { + $this->log("Nothing to do: " . $this->tarFile->__toString() . " is up to date.", Project::MSG_INFO); + return; + } + } + + $this->log("Building tar: " . $this->tarFile->__toString(), Project::MSG_INFO); + + $tar = new Archive_Tar($this->tarFile->getAbsolutePath(), $this->compression); + + // print errors + $tar->setErrorHandling(PEAR_ERROR_PRINT); + + foreach($this->filesets as $fs) { + $files = $fs->getFiles($this->project, $this->includeEmpty); + if (count($files) > 1 && strlen($fs->getFullpath()) > 0) { + throw new BuildException("fullpath attribute may only " + . "be specified for " + . "filesets that specify a " + . "single file."); + } + $fsBasedir = $fs->getDir($this->project); + $filesToTar = array(); + for ($i=0, $fcount=count($files); $i < $fcount; $i++) { + $f = new PhingFile($fsBasedir, $files[$i]); + $filesToTar[] = $f->getAbsolutePath(); + $this->log("Adding file " . $f->getPath() . " to archive.", Project::MSG_VERBOSE); + } + $tar->addModify($filesToTar, $this->prefix, $fsBasedir->getAbsolutePath()); + } + + + } catch (IOException $ioe) { + $msg = "Problem creating TAR: " . $ioe->getMessage(); + $this->filesets = $savedFileSets; + throw new BuildException($msg, $ioe, $this->getLocation()); + } + + $this->filesets = $savedFileSets; + } + + /** + * @param array $files array of filenames + * @param PhingFile $dir + * @return boolean + */ + protected function archiveIsUpToDate($files, $dir) { + $sfs = new SourceFileScanner($this); + $mm = new MergeMapper(); + $mm->setTo($this->tarFile->getAbsolutePath()); + return count($sfs->restrict($files, $dir, null, $mm)) == 0; + } + +} + + +/** + * This is a FileSet with the option to specify permissions. + * + * Permissions are currently not implemented by PEAR Archive_Tar, + * but hopefully they will be in the future. + * + * @package phing.tasks.ext + */ +class TarFileSet extends FileSet { + + private $files = null; + + private $mode = 0100644; + + private $userName = ""; + private $groupName = ""; + private $prefix = ""; + private $fullpath = ""; + private $preserveLeadingSlashes = false; + + /** + * Get a list of files and directories specified in the fileset. + * @return array a list of file and directory names, relative to + * the baseDir for the project. + */ + public function getFiles(Project $p, $includeEmpty = true) { + + if ($this->files === null) { + + $ds = $this->getDirectoryScanner($p); + $this->files = $ds->getIncludedFiles(); + + if ($includeEmpty) { + + // first any empty directories that will not be implicitly added by any of the files + $implicitDirs = array(); + foreach($this->files as $file) { + $implicitDirs[] = dirname($file); + } + + $incDirs = $ds->getIncludedDirectories(); + + // we'll need to add to that list of implicit dirs any directories + // that contain other *directories* (and not files), since otherwise + // we get duplicate directories in the resulting tar + foreach($incDirs as $dir) { + foreach($incDirs as $dircheck) { + if (!empty($dir) && $dir == dirname($dircheck)) { + $implicitDirs[] = $dir; + } + } + } + + $implicitDirs = array_unique($implicitDirs); + + // Now add any empty dirs (dirs not covered by the implicit dirs) + // to the files array. + + foreach($incDirs as $dir) { // we cannot simply use array_diff() since we want to disregard empty/. dirs + if ($dir != "" && $dir != "." && !in_array($dir, $implicitDirs)) { + // it's an empty dir, so we'll add it. + $this->files[] = $dir; + } + } + } // if $includeEmpty + + } // if ($this->files===null) + + return $this->files; + } + + /** + * A 3 digit octal string, specify the user, group and + * other modes in the standard Unix fashion; + * optional, default=0644 + * @param string $octalString + */ + public function setMode($octalString) { + $octal = (int) $octalString; + $this->mode = 0100000 | $octal; + } + + public function getMode() { + return $this->mode; + } + + /** + * The username for the tar entry + * This is not the same as the UID, which is + * not currently set by the task. + */ + public function setUserName($userName) { + $this->userName = $userName; + } + + public function getUserName() { + return $this->userName; + } + + /** + * The groupname for the tar entry; optional, default="" + * This is not the same as the GID, which is + * not currently set by the task. + */ + public function setGroup($groupName) { + $this->groupName = $groupName; + } + + public function getGroup() { + return $this->groupName; + } + + /** + * If the prefix attribute is set, all files in the fileset + * are prefixed with that path in the archive. + * optional. + */ + public function setPrefix($prefix) { + $this->prefix = $prefix; + } + + public function getPrefix() { + return $this->prefix; + } + + /** + * If the fullpath attribute is set, the file in the fileset + * is written with that path in the archive. The prefix attribute, + * if specified, is ignored. It is an error to have more than one file specified in + * such a fileset. + */ + public function setFullpath($fullpath) { + $this->fullpath = $fullpath; + } + + public function getFullpath() { + return $this->fullpath; + } + + /** + * Flag to indicates whether leading `/'s should + * be preserved in the file names. + * Optional, default is <code>false</code>. + * @return void + */ + public function setPreserveLeadingSlashes($b) { + $this->preserveLeadingSlashes = (boolean) $b; + } + + public function getPreserveLeadingSlashes() { + return $this->preserveLeadingSlashes; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/UntarTask.php b/buildscripts/phing/classes/phing/tasks/ext/UntarTask.php new file mode 100644 index 00000000..74777322 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/UntarTask.php @@ -0,0 +1,89 @@ +<?php
+/*
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+require_once 'phing/tasks/ext/ExtractBaseTask.php';
+
+/**
+ * Extracts one or several tar archives using PEAR Archive_Tar
+ *
+ * @author Joakim Bodin <joakim.bodin+phing@gmail.com>
+ * @version $Id: f77612833e0415725e7b816a0515db4a4f0f4c93 $
+ * @package phing.tasks.ext
+ * @since 2.2.0
+ */
+class UntarTask extends ExtractBaseTask {
+
+ /**
+ * Ensures that PEAR lib exists.
+ */
+ public function init() {
+ include_once 'Archive/Tar.php';
+ if (!class_exists('Archive_Tar')) {
+ throw new BuildException("You must have installed the PEAR Archive_Tar class in order to use UntarTask.");
+ }
+ }
+
+ protected function extractArchive(PhingFile $tarfile)
+ {
+ $this->log("Extracting tar file: " . $tarfile->__toString() . ' to ' . $this->todir->__toString(), Project::MSG_INFO);
+
+ try {
+ $tar = $this->initTar($tarfile);
+ if(!$tar->extractModify($this->todir->getAbsolutePath(), $this->removepath)) {
+ throw new BuildException('Failed to extract tar file: ' . $tarfile->getAbsolutePath());
+ }
+ } catch (IOException $ioe) {
+ $msg = "Could not extract tar file: " . $ioe->getMessage();
+ throw new BuildException($msg, $ioe, $this->getLocation());
+ }
+ }
+
+ protected function listArchiveContent(PhingFile $tarfile)
+ {
+ $tar = $this->initTar($tarfile);
+ return $tar->listContent();
+ }
+
+ /**
+ * Init a Archive_Tar class with correct compression for the given file.
+ *
+ * @param PhingFile $tarfile
+ * @return Archive_Tar the tar class instance
+ */
+ private function initTar(PhingFile $tarfile)
+ {
+ $compression = null;
+ $tarfileName = $tarfile->getName();
+ $mode = strtolower(substr($tarfileName, strrpos($tarfileName, '.')));
+
+ $compressions = array(
+ 'gz' => array('.gz', '.tgz',),
+ 'bz2' => array('.bz2',),
+ );
+ foreach ($compressions as $algo => $ext) {
+ if (array_search($mode, $ext) !== false) {
+ $compression = $algo;
+ break;
+ }
+ }
+
+ return new Archive_Tar($tarfile->getAbsolutePath(), $compression);
+ }
+}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/UnzipTask.php b/buildscripts/phing/classes/phing/tasks/ext/UnzipTask.php new file mode 100644 index 00000000..ef7c3e7d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/UnzipTask.php @@ -0,0 +1,77 @@ +<?php +/* + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/ExtractBaseTask.php'; +require_once 'phing/system/io/FileSystem.php'; + +/** + * Extracts one or several zip archives using ZipArchive class. + * + * @author Joakim Bodin <joakim.bodin+phing@gmail.com> + * @author George Miroshnikov <laggy.luke@gmail.com> + * @version $Id: 9c4afd9af5e81250ca6c7afbc6e646c2a0f0148c $ + * @package phing.tasks.ext + */ +class UnzipTask extends ExtractBaseTask +{ + /** + * Extract archive content into $this->todir directory + * @param PhingFile Zip file to extract + * @return boolean + */ + protected function extractArchive(PhingFile $zipfile) + { + $this->log("Extracting zip: " . $zipfile->__toString() . ' to ' . $this->todir->__toString(), Project::MSG_INFO); + + $zip = new ZipArchive(); + + $result = $zip->open($zipfile->getAbsolutePath()); + if (!$result) { + $this->log("Unable to open zipfile " . $zipfile->__toString(), Project::MSG_ERR); + return false; + } + + $result = $zip->extractTo($this->todir->getAbsolutePath()); + if (!$result) { + $this->log("Unable to extract zipfile " . $zipfile->__toString(), Project::MSG_ERR); + return false; + } + + return true; + } + + /** + * List archive content + * @param PhingFile Zip file to list content + * @return array List of files inside $zipfile + */ + protected function listArchiveContent(PhingFile $zipfile) + { + $zip = new ZipArchive(); + $zip->open($zipfile->getAbsolutePath()); + + $content = array(); + for ($i = 0; $i < $zip->numFiles; $i++) { + $content[] = $zip->getNameIndex($i); + } + return $content; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/VersionTask.php b/buildscripts/phing/classes/phing/tasks/ext/VersionTask.php new file mode 100755 index 00000000..b8268870 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/VersionTask.php @@ -0,0 +1,217 @@ +<?php +/* + * $Id: 7fb6793b55e9c1c8c7b3cd8a87b694d720b32749 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ +require_once 'phing/Task.php'; + +/** + * VersionTask + * + * Increments a three-part version number from a given file + * and writes it back to the file. + * Incrementing is based on given releasetype, which can be one + * of Major, Minor and Bugfix. + * Resulting version number is also published under supplied property. + * + * @author Mike Wittje <mw@mike.wittje.de> + * @version $Id: 7fb6793b55e9c1c8c7b3cd8a87b694d720b32749 $ $Rev $Id$ $Author$ + * @package phing.tasks.ext + */ +class VersionTask extends Task +{ + /** + * Property for Releasetype + * @var string $releasetype + */ + private $releasetype; + + /** + * Property for File + * @var PhingFile file + */ + private $file; + + /** + * Property to be set + * @var string $property + */ + private $property; + + /* Allowed Releastypes */ + const RELEASETYPE_MAJOR = 'MAJOR'; + const RELEASETYPE_MINOR = 'MINOR'; + const RELEASETYPE_BUGFIX = 'BUGFIX'; + + /** + * Set Property for Releasetype (Minor, Major, Bugfix) + * @param string $releasetype + */ + public function setReleasetype($releasetype) + { + $this->releasetype = strtoupper($releasetype); + } + + /** + * Set Property for File containing versioninformation + * @param PhingFile $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Set name of property to be set + * @param $property + * @return void + */ + public function setProperty($property) + { + $this->property = $property; + } + + /** + * Main-Method for the Task + * + * @return void + * @throws BuildException + */ + public function main() + { + // check supplied attributes + $this->checkReleasetype(); + $this->checkFile(); + $this->checkProperty(); + + // read file + $filecontent = trim(file_get_contents($this->file)); + + // get new version + $newVersion = $this->getVersion($filecontent); + + // write new Version to file + file_put_contents($this->file, $newVersion . "\n"); + + // publish new version number as property + $this->project->setProperty($this->property, $newVersion); + + } + + /** + * Returns new version number corresponding to Release type + * + * @param string $filecontent + * @return string + */ + private function getVersion($filecontent) + { + // init + $newVersion = ''; + + // Extract version + list($major, $minor, $bugfix) = explode(".", $filecontent); + + // Return new version number + switch ($this->releasetype) { + case self::RELEASETYPE_MAJOR: + $newVersion = sprintf("%d.%d.%d", ++$major, + 0, + 0); + break; + + case self::RELEASETYPE_MINOR: + $newVersion = sprintf("%d.%d.%d", $major, + ++$minor, + 0); + break; + + case self::RELEASETYPE_BUGFIX: + $newVersion = sprintf("%d.%d.%d", $major, + $minor, + ++$bugfix); + break; + } + + return $newVersion; + } + + + /** + * checks releasetype attribute + * @return void + * @throws BuildException + */ + private function checkReleasetype() + { + // check Releasetype + if (is_null($this->releasetype)) { + throw new BuildException('releasetype attribute is required', $this->location); + } + // known releasetypes + $releaseTypes = array( + self::RELEASETYPE_MAJOR, + self::RELEASETYPE_MINOR, + self::RELEASETYPE_BUGFIX + ); + + if (!in_array($this->releasetype, $releaseTypes)) { + throw new BuildException(sprintf('Unknown Releasetype %s..Must be one of Major, Minor or Bugfix', + $this->releasetype), $this->location); + } + } + + /** + * checks file attribute + * @return void + * @throws BuildException + */ + private function checkFile() + { + // check File + if ($this->file === null || + strlen($this->file) == 0) { + throw new BuildException('You must specify a file containing the version number', $this->location); + } + + $content = file_get_contents($this->file); + if (strlen($content) == 0) { + throw new BuildException(sprintf('Supplied file %s is empty', $this->file), $this->location); + } + + // check for three-part number + $split = explode('.', $content); + if (count($split) !== 3) { + throw new BuildException('Unknown version number format', $this->location); + } + + } + + /** + * checks property attribute + * @return void + * @throws BuildException + */ + private function checkProperty() + { + if (is_null($this->property) || + strlen($this->property) === 0) { + throw new BuildException('Property for publishing version number is not set', $this->location); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/XmlLintTask.php b/buildscripts/phing/classes/phing/tasks/ext/XmlLintTask.php new file mode 100644 index 00000000..15200d37 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/XmlLintTask.php @@ -0,0 +1,179 @@ +<?php +/* + * $Id: 6261b2f19844b353c379c46daf3ffb13b7a2ddb8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * A XML lint task. Checking syntax of one or more XML files against an XML Schema using the DOM extension. + * + * @author Knut Urdalen <knut.urdalen@telio.no> + * @version $Id: 6261b2f19844b353c379c46daf3ffb13b7a2ddb8 $ + * @package phing.tasks.ext + */ +class XmlLintTask extends Task { + + protected $file; // the source file (from xml attribute) + protected $schema; // the schema file (from xml attribute) + protected $filesets = array(); // all fileset objects assigned to this task + protected $useRNG = false; + + protected $haltonfailure = true; + + /** + * File to be performed syntax check on + * + * @param PhingFile $file + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * XML Schema Description file to validate against + * + * @param PhingFile $schema + */ + public function setSchema(PhingFile $schema) { + $this->schema = $schema; + } + + + /** + * Use RNG instead of DTD schema validation + * + * @param bool $bool + */ + public function setUseRNG($bool) { + $this->useRNG = (boolean)$bool; + } + + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Sets the haltonfailure attribute + * + * @param bool $haltonfailure + */ + public function setHaltonfailure($haltonfailure) { + $this->haltonfailure = (bool) $haltonfailure; + } + + /** + * Execute lint check against PhingFile or a FileSet + */ + public function main() { + if(isset($this->schema) && !file_exists($this->schema->getPath())) { + throw new BuildException("Schema file not found: ".$this->schema->getPath()); + } + if(!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + set_error_handler(array($this, 'errorHandler')); + if($this->file instanceof PhingFile) { + $this->lint($this->file->getPath()); + } else { // process filesets + $project = $this->getProject(); + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getPath(); + foreach($files as $file) { + $this->lint($dir.DIRECTORY_SEPARATOR.$file); + } + } + } + restore_error_handler(); + } + + protected function logError($message) { + if ($this->haltonfailure) { + throw new BuildException($message); + } else { + $this->log($message, Project::MSG_ERR); + } + } + + /** + * Performs validation + * + * @param string $file + * @return void + */ + protected function lint($file) { + if(file_exists($file)) { + if(is_readable($file)) { + $dom = new DOMDocument(); + if ($dom->load($file) === false) { + $error = libxml_get_last_error(); + $this->logError($file.' is not well-formed (See messages above)'); + } else { + if(isset($this->schema)) { + if( $this->useRNG ) { + if($dom->relaxNGValidate($this->schema->getPath())) { + $this->log($file.' validated with RNG grammar', Project::MSG_INFO); + } else { + $this->logError($file.' fails to validate (See messages above)'); + } + } else { + if($dom->schemaValidate($this->schema->getPath())) { + $this->log($file.' validated with schema', Project::MSG_INFO); + } else { + $this->logError($file.' fails to validate (See messages above)'); + } + } + } else { + $this->log($file.' is well-formed (not validated due to missing schema specification)', Project::MSG_INFO); + } + } + } else { + $this->logError('Permission denied to read file: '.$file); + } + } else { + $this->logError('File not found: '.$file); + } + } + + /** + * Local error handler to catch validation errors and log them through Phing + * + * @param int $level + * @param string $message + * @param string $file + * @param int $line + */ + public function errorHandler($level, $message, $file, $line, $context) { + $matches = array(); + preg_match('/^.*\(\): (.*)$/', $message, $matches); + $this->log($matches[1], Project::MSG_ERR); + } + +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/XmlPropertyTask.php b/buildscripts/phing/classes/phing/tasks/ext/XmlPropertyTask.php new file mode 100755 index 00000000..d32cd78e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/XmlPropertyTask.php @@ -0,0 +1,273 @@ +<?php + +/* + * $Id: c7a3e7eff0b94828f9ec634c3612d89f2740fead $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/tasks/system/PropertyTask.php'; + +/** + * Task for setting properties from an XML file in buildfiles. + * + * @author Jonathan Bond-Caron <jbondc@openmv.com> + * @version $Id: c7a3e7eff0b94828f9ec634c3612d89f2740fead $ + * @package phing.tasks.ext + * @since 2.4.0 + * @link http://ant.apache.org/manual/CoreTasks/xmlproperty.html + */ +class XmlPropertyTask extends PropertyTask { + + private $_keepRoot = true; + private $_collapseAttr = false; + private $_delimiter = ','; + private $_required = false; + + /** Set a file to use as the source for properties. */ + public function setFile($file) { + if (is_string($file)) { + $file = new PhingFile($file); + } + $this->file = $file; + } + + /** Get the PhingFile that is being used as property source. */ + public function getFile() { + return $this->file; + } + + /** + * Prefix to apply to properties loaded using <code>file</code>. + * A "." is appended to the prefix if not specified. + * @param string $prefix prefix string + * @return void + * @since 2.0 + */ + public function setPrefix($prefix) { + $this->prefix = $prefix; + if (!StringHelper::endsWith(".", $prefix)) { + $this->prefix .= "."; + } + } + + /** + * @return string + * @since 2.0 + */ + public function getPrefix() { + return $this->prefix; + } + + /** + * Keep the xml root tag as the first value in the property name + * + * @param bool $yesNo + */ + public function setKeepRoot($yesNo) { + $this->_keepRoot = (bool)$yesNo; + } + + /** + * @return bool + */ + public function getKeepRoot() { + return $this->_keepRoot; + } + + /** + * Treat attributes as nested elements. + * + * @param bool $yesNo + */ + public function setCollapseAttributes($yesNo) { + $this->_collapseAttr = (bool)$yesNo; + } + + /** + * @return bool + */ + public function getCollapseAttributes() { + return $this->_collapseAttr; + } + + /** + * Delimiter for splitting multiple values. + * + * @param string $d + */ + public function setDelimiter($d) { + $this->_delimiter = $d; + } + + /** + * @return string + */ + public function getDelimiter() { + return $this->_delimiter; + } + + /** + * File required or not. + * + * @param string $d + */ + public function setRequired($d) { + $this->_required = $d; + } + + /** + * @return string + */ + public function getRequired() { + return $this->_required; + } + + /** + * set the property in the project to the value. + * if the task was give a file or env attribute + * here is where it is loaded + */ + public function main() { + + if ($this->file === null ) { + throw new BuildException("You must specify file to load properties from", $this->getLocation()); + } + + $this->loadFile($this->file); + } + + /** + * load properties from an XML file. + * @param PhingFile $file + */ + protected function loadFile(PhingFile $file) { + $props = new Properties(); + $this->log("Loading ". $file->getAbsolutePath(), Project::MSG_INFO); + try { // try to load file + if ($file->exists()) { + + $this->addProperties($this->_getProperties($file)); + + } else { + if ($this->getRequired()){ + throw new BuildException("Could not load required properties file.", $ioe); + } else { + $this->log("Unable to find property file: ". $file->getAbsolutePath() ."... skipped", Project::MSG_WARN); + } + } + } catch (IOException $ioe) { + throw new BuildException("Could not load properties from file.", $ioe); + } + } + + /** + * Parses an XML file and returns properties + * + * @param string $filePath + * + * @return Properties + */ + protected function _getProperties($filePath) { + + // load() already made sure that file is readable + // but we'll double check that when reading the file into + // an array + + if (($lines = @file($filePath)) === false) { + throw new IOException("Unable to parse contents of $filePath"); + } + + $prop = new Properties; + + $xml = simplexml_load_file($filePath); + + if($xml === false) + throw new IOException("Unable to parse XML file $filePath"); + + $path = array(); + + if($this->_keepRoot) { + $path[] = dom_import_simplexml($xml)->tagName; + + $prefix = implode('.', $path); + + if (!empty($prefix)) + $prefix .= '.'; + + // Check for attributes + foreach($xml->attributes() as $attribute => $val) { + if($this->_collapseAttr) + $prop->setProperty($prefix . "$attribute", (string)$val); + else + $prop->setProperty($prefix . "($attribute)", (string)$val); + } + } + + $this->_addNode($xml, $path, $prop); + + return $prop; + } + + /** + * Adds an XML node + * + * @param SimpleXMLElement $node + * @param array $path Path to this node + * @param Properties $prop Properties will be added as they are found (by reference here) + * + * @return void + */ + protected function _addNode($node, $path, $prop) { + foreach($node as $tag => $value) { + + $prefix = implode('.', $path); + + if (!empty($prefix) > 0) + $prefix .= '.'; + + // Check for attributes + foreach($value->attributes() as $attribute => $val) { + if($this->_collapseAttr) + $prop->setProperty($prefix . "$tag.$attribute", (string)$val); + else + $prop->setProperty($prefix . "$tag($attribute)", (string)$val); + } + + // Add tag + if(count($value->children())) { + $this->_addNode($value, array_merge($path, array($tag)), $prop); + } else { + $val = (string)$value; + + /* Check for * and ** on 'exclude' and 'include' tag / ant seems to do this? could use FileSet here + if($tag == 'exclude') { + }*/ + + // When property already exists, i.e. multiple xml tag + // <project> + // <exclude>file/a.php</exclude> + // <exclude>file/a.php</exclude> + // </project> + // + // Would be come project.exclude = file/a.php,file/a.php + $p = empty($prefix) ? $tag : $prefix . $tag; + $prop->append($p, (string)$val, $this->_delimiter); + } + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php b/buildscripts/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php new file mode 100644 index 00000000..5093fabe --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php @@ -0,0 +1,207 @@ +<?php +/* + * $Id: 5b7e3fb304bb5f406c919407d6881449a70b8a28 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * ZendCodeAnalyzerTask analyze PHP source code using the ZendCodeAnalyzer included in Zend Studio 5.1 + * + * Available warnings: + * <b>zend-error</b> - %s(line %d): %s + * <b>oneline-comment</b> - One-line comment ends with tag. + * <b>bool-assign</b> - Assignment seen where boolean expression is expected. Did you mean '==' instead of '='? + * <b>bool-print</b> - Print statement used when boolean expression is expected. + * <b>bool-array</b> - Array used when boolean expression is expected. + * <b>bool-object</b> - Object used when boolean expression is expected. + * <b>call-time-ref</b> - Call-time reference is deprecated. Define function as accepting parameter by reference instead. + * <b>if-if-else</b> - In if-if-else construction else relates to the closest if. Use braces to make the code clearer. + * <b>define-params</b> - define() requires two or three parameters. + * <b>define-const</b> - First parameter for define() should be string. Maybe you forgot quotes? + * <b>break-var</b> - Break/continue with variable is dangerous - break level can be out of scope. + * <b>break-depth</b> - Break/continue with depth more than current nesting level. + * <b>var-once</b> - Variable '%s' encountered only once. May be a typo? + * <b>var-arg-unused</b> - Function argument '%s' is never used. + * <b>var-global-unused</b> - Global variable '%s' is defined but never used. + * <b>var-use-before-def</b> - Variable '%s' is used before it was assigned. + * <b>var-use-before-def-global</b> - Global variable '%s' is used without being assigned. You are probably relying on register_globals feature of PHP. Note that this feature is off by default. + * <b>var-no-global</b> - PHP global variable '%s' is used as local. Maybe you wanted to define '%s' as global? + * <b>var-value-unused</b> - Value assigned to variable '%s' is never used + * <b>var-ref-notmodified</b> - Function parameter '%s' is passed by reference but never modified. Consider passing by value. + * <b>return-empty-val</b> - Function '%s' has both empty return and return with value. + * <b>return-empty-used</b> - Function '%s' has empty return but return value is used. + * <b>return-noref</b> - Function '%s' returns reference but the value is not assigned by reference. Maybe you meant '=&' instead of '='? + * <b>return-end-used</b> - Control reaches the end of function '%s'(file %s, line %d) but return value is used. + * <b>sprintf-miss-args</b> - Missing arguments for sprintf: format reqires %d arguments but %d are supplied. + * <b>sprintf-extra-args</b> - Extra arguments for sprintf: format reqires %d arguments but %d are supplied. + * <b>unreach-code</b> - Unreachable code in function '%s'. + * <b>include-var</b> - include/require with user-accessible variable can be dangerous. Consider using constant instead. + * <b>non-object</b> - Variable '%s' used as object, but has different type. + * <b>bad-escape</b> - Bad escape sequence: \%c, did you mean \\%c? + * <b>empty-cond</b> - Condition without a body + * <b>expr-unused</b> - Expression result is never used + * + * @author Knut Urdalen <knut.urdalen@gmail.com> + * @version $Id: 5b7e3fb304bb5f406c919407d6881449a70b8a28 $ + * @package phing.tasks.ext + */ +class ZendCodeAnalyzerTask extends Task +{ + protected $analyzerPath = ""; // Path to ZendCodeAnalyzer binary + protected $file = ""; // the source file (from xml attribute) + protected $filesets = array(); // all fileset objects assigned to this task + protected $counter = 0; + protected $disable = array(); + protected $enable = array(); + + private $haltonwarning = false; + + /** + * File to be analyzed + * + * @param PhingFile $file + */ + public function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Path to ZendCodeAnalyzer binary + * + * @param string $analyzerPath + */ + public function setAnalyzerPath($analyzerPath) { + $this->analyzerPath = $analyzerPath; + } + + /** + * Disable warning levels. Seperate warning levels with ',' + * + * @param string $disable + */ + public function setDisable($disable) { + $this->disable = explode(",", $disable); + } + + /** + * Enable warning levels. Seperate warning levels with ',' + * + * @param string $enable + */ + public function setEnable($enable) { + $this->enable = explode(",", $enable); + } + + /** + * Sets the haltonwarning flag + * @param boolean $value + */ + public function setHaltonwarning($value) + { + $this->haltonwarning = $value; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @return FileSet The created fileset object + */ + public function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Analyze against PhingFile or a FileSet + */ + public function main() { + if(!isset($this->analyzerPath)) { + throw new BuildException("Missing attribute 'analyzerPath'"); + } + + if(!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + if($this->file instanceof PhingFile) { + $this->analyze($this->file->getPath()); + } else { // process filesets + $project = $this->getProject(); + + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $files = $ds->getIncludedFiles(); + $dir = $fs->getDir($this->project)->getPath(); + + foreach($files as $file) { + $this->analyze($dir.DIRECTORY_SEPARATOR.$file); + } + } + } + + $this->log("Number of findings: ".$this->counter, Project::MSG_INFO); + } + + /** + * Analyze file + * + * @param string $file + * @return void + */ + protected function analyze($file) { + if(file_exists($file)) { + if(is_readable($file)) { + // Construct shell command + $cmd = $this->analyzerPath." "; + + foreach($this->enable as $enable) { // Enable warning levels + $cmd .= " --enable $enable "; + } + + foreach($this->disable as $disable) { // Disable warning levels + $cmd .= " --disable $disable "; + } + + $cmd .= "$file 2>&1"; + + // Execute command + $result = shell_exec($cmd); + $result = explode("\n", $result); + + for($i=2, $size=count($result); $i<($size-1); $i++) { + $this->counter++; + $this->log($result[$i], Project::MSG_WARN); + } + + $total = count($result) - 3; + + if ($total > 0 && $this->haltonwarning) { + throw new BuildException('zendcodeanalyzer detected ' . $total . ' warning' . ($total > 1 ? 's' : '') . ' in ' . $file); + } + } + else + { + throw new BuildException('Permission denied: '.$file); + } + } else { + throw new BuildException('File not found: '.$file); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ZipTask.php b/buildscripts/phing/classes/phing/tasks/ext/ZipTask.php new file mode 100755 index 00000000..72bf42fd --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ZipTask.php @@ -0,0 +1,301 @@ +<?php +/* + * $Id: 6997b3f3abffedf1b2efabc40c3b2d012b2379cb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; +include_once 'phing/util/SourceFileScanner.php'; +include_once 'phing/mappers/MergeMapper.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * Creates a zip archive using PHP ZipArchive extension/ + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 6997b3f3abffedf1b2efabc40c3b2d012b2379cb $ + * @package phing.tasks.ext + * @since 2.1.0 + */ +class ZipTask extends MatchingTask { + + /** + * @var PhingFile + */ + private $zipFile; + + /** + * @var PhingFile + */ + private $baseDir; + + /** + * Whether to include empty dirs in the archive. + */ + private $includeEmpty = true; + + private $filesets = array(); + private $fileSetFiles = array(); + + /** + * File path prefix in zip archive + * + * @var string + */ + private $prefix = null; + + /** + * Add a new fileset. + * @return FileSet + */ + public function createFileSet() { + $this->fileset = new ZipFileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + + /** + * Set is the name/location of where to create the zip file. + * @param PhingFile $destFile The output of the zip + */ + public function setDestFile(PhingFile $destFile) { + $this->zipFile = $destFile; + } + + /** + * This is the base directory to look in for things to zip. + * @param PhingFile $baseDir + */ + public function setBasedir(PhingFile $baseDir) { + $this->baseDir = $baseDir; + } + + /** + * Sets the file path prefix for file in the zip file. + * + * @param string $prefix Prefix + * + * @return void + */ + public function setPrefix($prefix) { + $this->prefix = $prefix; + } + + /** + * Set the include empty dirs flag. + * @param boolean Flag if empty dirs should be tarred too + * @return void + * @access public + */ + public function setIncludeEmptyDirs($bool) { + $this->includeEmpty = (boolean) $bool; + } + + /** + * do the work + * @throws BuildException + */ + public function main() { + + if ($this->zipFile === null) { + throw new BuildException("zipfile attribute must be set!", $this->getLocation()); + } + + if ($this->zipFile->exists() && $this->zipFile->isDirectory()) { + throw new BuildException("zipfile is a directory!", $this->getLocation()); + } + + if ($this->zipFile->exists() && !$this->zipFile->canWrite()) { + throw new BuildException("Can not write to the specified zipfile!", $this->getLocation()); + } + + // shouldn't need to clone, since the entries in filesets + // themselves won't be modified -- only elements will be added + $savedFileSets = $this->filesets; + + try { + if ($this->baseDir !== null) { + if (!$this->baseDir->exists()) { + throw new BuildException("basedir '" . (string) $this->baseDir . "' does not exist!", $this->getLocation()); + } + + if (empty($this->filesets)) + { + // add the main fileset to the list of filesets to process. + $mainFileSet = new ZipFileSet($this->fileset); + $mainFileSet->setDir($this->baseDir); + $this->filesets[] = $mainFileSet; + } + } + + if (empty($this->filesets)) { + throw new BuildException("You must supply either a basedir " + . "attribute or some nested filesets.", + $this->getLocation()); + } + + // check if zip is out of date with respect to each + // fileset + $upToDate = true; + foreach($this->filesets as $fs) { + $files = $fs->getFiles($this->project, $this->includeEmpty); + if (!$this->archiveIsUpToDate($files, $fs->getDir($this->project))) { + $upToDate = false; + } + for ($i=0, $fcount=count($files); $i < $fcount; $i++) { + if ($this->zipFile->equals(new PhingFile($fs->getDir($this->project), $files[$i]))) { + throw new BuildException("A zip file cannot include itself", $this->getLocation()); + } + } + } + + if ($upToDate) { + $this->log("Nothing to do: " . $this->zipFile->__toString() . " is up to date.", Project::MSG_INFO); + return; + } + + $this->log("Building zip: " . $this->zipFile->__toString(), Project::MSG_INFO); + + $zip = new ZipArchive(); + $res = $zip->open($this->zipFile->getAbsolutePath(), ZIPARCHIVE::CREATE); + + if ($res !== true) + { + throw new Exception("ZipArchive::open() failed with code " . $res); + } + + foreach($this->filesets as $fs) { + $fsBasedir = (null != $this->baseDir) ? $this->baseDir : + $fs->getDir($this->project); + + $files = $fs->getFiles($this->project, $this->includeEmpty); + + $filesToZip = array(); + for ($i=0, $fcount=count($files); $i < $fcount; $i++) { + $f = new PhingFile($fsBasedir, $files[$i]); + + $pathInZip = $this->prefix + . $f->getPathWithoutBase($fsBasedir); + + $pathInZip = str_replace('\\', '/', $pathInZip); + + if ($f->isDirectory()) { + if ($pathInZip != '.') { + $zip->addEmptyDir($pathInZip); + } + } else { + $zip->addFile($f->getPath(), $pathInZip); + } + $this->log("Adding " . $f->getPath() . " as " . $pathInZip . " to archive.", Project::MSG_VERBOSE); + } + } + + $zip->close(); + } catch (IOException $ioe) { + $msg = "Problem creating ZIP: " . $ioe->getMessage(); + $this->filesets = $savedFileSets; + throw new BuildException($msg, $ioe, $this->getLocation()); + } + + $this->filesets = $savedFileSets; + } + + /** + * @param array $files array of filenames + * @param PhingFile $dir + * @return boolean + */ + protected function archiveIsUpToDate($files, $dir) { + $sfs = new SourceFileScanner($this); + $mm = new MergeMapper(); + $mm->setTo($this->zipFile->getAbsolutePath()); + return count($sfs->restrict($files, $dir, null, $mm)) == 0; + } + +} + + + + +/** + * This is a FileSet with the to specify permissions. + * + * Permissions are currently not implemented by PEAR Archive_Tar, + * but hopefully they will be in the future. + * + * @package phing.tasks.ext + */ +class ZipFileSet extends FileSet { + + private $files = null; + + /** + * Get a list of files and directories specified in the fileset. + * @return array a list of file and directory names, relative to + * the baseDir for the project. + */ + public function getFiles(Project $p, $includeEmpty = true) { + + if ($this->files === null) { + + $ds = $this->getDirectoryScanner($p); + $this->files = $ds->getIncludedFiles(); + + // build a list of directories implicitly added by any of the files + $implicitDirs = array(); + foreach($this->files as $file) { + $implicitDirs[] = dirname($file); + } + + $incDirs = $ds->getIncludedDirectories(); + + // we'll need to add to that list of implicit dirs any directories + // that contain other *directories* (and not files), since otherwise + // we get duplicate directories in the resulting tar + foreach($incDirs as $dir) { + foreach($incDirs as $dircheck) { + if (!empty($dir) && $dir == dirname($dircheck)) { + $implicitDirs[] = $dir; + } + } + } + + $implicitDirs = array_unique($implicitDirs); + + $emptyDirectories = array(); + + if ($includeEmpty) { + // Now add any empty dirs (dirs not covered by the implicit dirs) + // to the files array. + + foreach($incDirs as $dir) { // we cannot simply use array_diff() since we want to disregard empty/. dirs + if ($dir != "" && $dir != "." && !in_array($dir, $implicitDirs)) { + // it's an empty dir, so we'll add it. + $emptyDirectories[] = $dir; + } + } + } // if $includeEmpty + + $this->files = array_merge($implicitDirs, $emptyDirectories, $this->files); + sort($this->files); + } // if ($this->files===null) + + return $this->files; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php b/buildscripts/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php new file mode 100644 index 00000000..d8438b3e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php @@ -0,0 +1,439 @@ +<?php +/* + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * ApiGen task (http://apigen.org). + * + * @package phing.tasks.ext.apigen + * @author Martin Srank <martin@smasty.net> + * @author Jaroslav Hanslík <kukulich@kukulich.cz> + * @since 2.4.10 + */ +class ApiGenTask extends Task +{ + /** + * Default ApiGen executable name. + * + * @var string + */ + private $executable = 'apigen'; + + /** + * Default options for ApiGen. + * + * @var array + */ + private $options = array( + 'progressbar' => false, + 'colors' => false, + 'update-check' => false + ); + + /** + * Sets the ApiGen executable name. + * + * @param string $executable + */ + public function setExecutable($executable) + { + $this->executable = (string) $executable; + } + + /** + * Sets the config file name. + * + * @param string $config + */ + public function setConfig($config) + { + $this->options['config'] = (string) $config; + } + + /** + * Sets source files or directories. + * + * @param string $source + */ + public function setSource($source) + { + $this->options['source'] = explode(',', $source); + } + + /** + * Sets the destination directory. + * + * @param string $destination + */ + public function setDestination($destination) + { + $this->options['destination'] = (string) $destination; + } + + /** + * Sets list of allowed file extensions. + * + * @param string $extensions + */ + public function setExtensions($extensions) + { + $this->options['extensions'] = explode(',', $extensions); + } + + /** + * Sets masks (case sensitive) to exclude files or directories from processing. + * + * @param string $exclude + */ + public function setExclude($exclude) + { + $this->options['exclude'] = explode(',', $exclude); + } + + /** + * Sets masks to exclude elements from documentation generating. + * + * @param string $skipDocPath + */ + public function setSkipDocPath($skipDocPath) + { + $this->options['skip-doc-path'] = explode(',', $skipDocPath); + } + + /** + * Sets a name prefix to exclude elements from documentation generating. + * + * @param string $skipDocPrefix + */ + public function setSkipDocPrefix($skipDocPrefix) + { + $this->options['skip-doc-prefix'] = explode(',', $skipDocPrefix); + } + + /** + * Sets the character set of source files. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->options['charset'] = explode(',', $charset); + } + + /** + * Sets the main project name prefix. + * + * @param string $main + */ + public function setMain($main) + { + $this->options['main'] = (string) $main; + } + + /** + * Sets the title of generated documentation. + * + * @param string $title + */ + public function setTitle($title) + { + $this->options['title'] = (string) $title; + } + + /** + * Sets the documentation base URL. + * + * @param string $baseUrl + */ + public function setBaseUrl($baseUrl) + { + $this->options['base-url'] = (string) $baseUrl; + } + + /** + * Sets the Google Custom Search ID. + * + * @param string $googleCseId + */ + public function setGoogleCseId($googleCseId) + { + $this->options['google-cse-id'] = (string) $googleCseId; + } + + /** + * Sets the Google Custom Search label. + * + * @param string $googleCseLabel + */ + public function setGoogleCseLabel($googleCseLabel) + { + $this->options['google-cse-label'] = (string) $googleCseLabel; + } + + /** + * Sets the Google Analytics tracking code. + * + * @param string $googleAnalytics + */ + public function setGoogleAnalytics($googleAnalytics) + { + $this->options['google-analytics'] = (string) $googleAnalytics; + } + + /** + * Sets the template config file name. + * + * @param string $templateConfig + */ + public function setTemplateConfig($templateConfig) + { + $this->options['template-config'] = (string) $templateConfig; + } + + /** + * Sets a list of HTML tags allowed in the documentation. + * + * @param string $allowedHtml + */ + public function setAllowedHtml($allowedHtml) + { + $this->options['allowed-html'] = (string) $allowedHtml; + } + + /** + * Sets how elements should be grouped in the menu. + * + * @param string $groups + */ + public function setGroups($groups) + { + $this->options['groups'] = (string) $groups; + } + + /** + * Sets element types for search input autocomplete. + * + * @param string $autocomplete + */ + public function setAutocomplete($autocomplete) + { + $this->options['autocomplete'] = (string) $autocomplete; + } + + /** + * Sets the element access levels. + * + * Documentation only for methods and properties with the given access level will be generated. + * + * @param string $accessLevels + */ + public function setAccessLevels($accessLevels) + { + $this->options['access-levels'] = (string) $accessLevels; + } + + /** + * Sets if documentation for elements marked as internal and internal documentation parts should be generated. + * + * @param boolean $internal + */ + public function setInternal($internal) + { + $this->options['internal'] = (bool) $internal; + } + + /** + * Sets if documentation for PHP internal classes should be generated. + * + * @param boolean $php + */ + public function setPhp($php) + { + $this->options['php'] = (bool) $php; + } + + /** + * Sets if tree view of classes, interfaces, traits and exceptions should be generated. + * + * @param boolean $tree + */ + public function setTree($tree) + { + $this->options['tree'] = (bool) $tree; + } + + /** + * Sets if documentation for deprecated elements should be generated. + * + * @param boolean $deprecated + */ + public function setDeprecated($deprecated) + { + $this->options['deprecated'] = (bool) $deprecated; + } + + /** + * Sets if documentation of tasks should be generated. + * + * @param boolean $todo + */ + public function setTodo($todo) + { + $this->options['todo'] = (bool) $todo; + } + + /** + * Sets if highlighted source code files should be generated. + * + * @param boolean $sourceCode + */ + public function setSourceCode($sourceCode) + { + $this->options['source-code'] = (bool) $sourceCode; + } + + /** + * Sets if a link to download documentation as a ZIP archive should be generated. + * + * @param boolean $download + */ + public function setDownload($download) + { + $this->options['download'] = (bool) $download; + } + + /** + * Sets a file name for checkstyle report of poorly documented elements. + * + * @param string $report + */ + public function setReport($report) + { + $this->options['report'] = (string) $report; + } + + /** + * Sets if the destination directory should be wiped out first. + * + * @param boolean $wipeout + */ + public function setWipeout($wipeout) + { + $this->options['wipeout'] = (bool) $wipeout; + } + + /** + * Enables/disables scaning and generating messages. + * + * @param boolean $quiet + */ + public function setQuiet($quiet) + { + $this->options['quiet'] = (bool) $quiet; + } + + /** + * Enables/disables the check for ApiGen updates. + * + * @param boolean $updateCheck + */ + public function setUpdateCheck($updateCheck) + { + $this->options['update-check'] = (bool) $updateCheck; + } + + /** + * Enables/disables the debug mode. + * + * @param boolean $debug + */ + public function setDebug($debug) + { + $this->options['debug'] = (bool) $debug; + } + + /** + * Runs ApiGen. + * + * @throws BuildException If something is wrong. + * @see Task::main() + */ + public function main() + { + if ('apigen' !== $this->executable && !is_file($this->executable)) { + throw new BuildException(sprintf('Executable %s not found', $this->executable), $this->getLocation()); + } + + if (!empty($this->options['config'])) { + // Config check + if (!is_file($this->options['config'])) { + throw new BuildException(sprintf('Config file %s doesn\'t exist', $this->options['config']), $this->getLocation()); + } + } else { + // Source check + if (empty($this->options['source'])) { + throw new BuildException('Source is not set', $this->getLocation()); + } + // Destination check + if (empty($this->options['destination'])) { + throw new BuildException('Destination is not set', $this->getLocation()); + } + } + + // Source check + if (!empty($this->options['source'])) { + foreach ($this->options['source'] as $source) { + if (!file_exists($source)) { + throw new BuildException(sprintf('Source %s doesn\'t exist', $source), $this->getLocation()); + } + } + } + + // Execute ApiGen + exec(escapeshellcmd($this->executable) . ' ' . $this->constructArguments(), $output, $return); + + $logType = 0 === $return ? Project::MSG_INFO : Project::MSG_ERR; + foreach ($output as $line) { + $this->log($line, $logType); + } + } + + /** + * Generates command line arguments for the ApiGen executable. + * + * @return string + */ + protected function constructArguments() + { + $args = array(); + foreach ($this->options as $option => $value) { + if (is_bool($value)) { + $args[] = '--' . $option . '=' . ($value ? 'yes' : 'no'); + } elseif (is_array($value)) { + foreach ($value as $v) { + $args[] = '--' . $option . '=' . escapeshellarg($v); + } + } else { + $args[] = '--' . $option . '=' . escapeshellarg($value); + } + } + return implode(' ', $args); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php new file mode 100755 index 00000000..71eba460 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php @@ -0,0 +1,154 @@ +<?php +/** + * $Id: 83f3748d0690f9fc69c2618191f272e5661c0501 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/util/Properties.php'; + +/** + * Saves coverage output of the test to a specified database + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 83f3748d0690f9fc69c2618191f272e5661c0501 $ + * @package phing.tasks.ext.coverage + * @since 2.1.0 + */ +class CoverageMerger +{ + private static function mergeCodeCoverage($left, $right) + { + $coverageMerged = array(); + + reset($left); + reset($right); + + while (current($left) !== false && current($right) !== false) { + $linenr_left = key($left); + $linenr_right = key($right); + + if ($linenr_left < $linenr_right) { + $coverageMerged[$linenr_left] = current($left); + next($left); + } elseif ($linenr_right < $linenr_left) { + $coverageMerged[$linenr_right] = current($right); + next($right); + } else { + if ((current($left) < 0) || (current($right) < 0)) { + $coverageMerged[$linenr_right] = current($right); + } else { + $coverageMerged[$linenr_right] = current($left) + current($right); + } + + next($left); + next($right); + } + } + + while (current($left) !== false) { + $coverageMerged[key($left)] = current($left); + next($left); + } + + while (current($right) !== false) { + $coverageMerged[key($right)] = current($right); + next($right); + } + + return $coverageMerged; + } + + /** + * @param Project $project + * @return Properties + * @throws BuildException + */ + protected static function _getDatabase($project) + { + $coverageDatabase = $project->getProperty('coverage.database'); + + if (!$coverageDatabase) { + throw new BuildException("Property coverage.database is not set - please include coverage-setup in your build file"); + } + + $database = new PhingFile($coverageDatabase); + + $props = new Properties(); + $props->load($database); + + return $props; + } + + public static function getWhiteList($project) + { + $whitelist = array(); + $props = self::_getDatabase($project); + + foreach ($props->getProperties() as $property) { + $data = unserialize($property); + $whitelist[] = $data['fullname']; + } + + return $whitelist; + } + + public static function merge($project, $codeCoverageInformation) + { + $props = self::_getDatabase($project); + + $coverageTotal = $codeCoverageInformation; + + foreach ($coverageTotal as $filename => $data) { + $ignoreLines = PHP_CodeCoverage_Util::getLinesToBeIgnored($filename); + + $lines = array(); + $filename = strtolower($filename); + + if ($props->getProperty($filename) != null) { + foreach ($data as $_line => $_data) { + if (is_array($_data)) { + $count = count($_data); + } else if(isset($ignoreLines[$_line])) { + // line is marked as ignored + $count = 1; + } else if ($_data == -1) { + // not executed + $count = -1; + } else if ($_data == -2) { + // dead code + $count = -2; + } + + $lines[$_line] = $count; + } + + ksort($lines); + + $file = unserialize($props->getProperty($filename)); + $left = $file['coverage']; + + $coverageMerged = CoverageMerger::mergeCodeCoverage($left, $lines); + + $file['coverage'] = $coverageMerged; + $props->setProperty($filename, serialize($file)); + } + } + + $props->store(); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php new file mode 100755 index 00000000..fd141cb5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php @@ -0,0 +1,92 @@ +<?php +/** + * $Id: 324ec42a8015e3b82e90ee3bfaad1bc069fec409 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; +require_once 'phing/system/util/Properties.php'; +require_once 'phing/tasks/ext/coverage/CoverageMerger.php'; + +/** + * Merges code coverage snippets into a code coverage database + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 324ec42a8015e3b82e90ee3bfaad1bc069fec409 $ + * @package phing.tasks.ext.coverage + * @since 2.1.0 + */ +class CoverageMergerTask extends Task +{ + /** the list of filesets containing the .php filename rules */ + private $filesets = array(); + + /** + * Add a new fileset containing the .php files to process + * + * @param FileSet the new fileset containing .php files + */ + function addFileSet(FileSet $fileset) + { + $this->filesets[] = $fileset; + } + + /** + * Iterate over all filesets and return all the filenames. + * + * @return array an array of filenames + */ + private function getFilenames() + { + $files = array(); + + foreach ($this->filesets as $fileset) + { + $ds = $fileset->getDirectoryScanner($this->project); + $ds->scan(); + + $includedFiles = $ds->getIncludedFiles(); + + foreach ($includedFiles as $file) + { + $fs = new PhingFile(basename($ds->getBaseDir()), $file); + + $files[] = $fs->getAbsolutePath(); + } + } + + return $files; + } + + function main() + { + $files = $this->getFilenames(); + + $this->log("Merging " . count($files) . " coverage files"); + + foreach ($files as $file) + { + $coverageInformation = unserialize(file_get_contents($file)); + + CoverageMerger::merge($this->project, array($coverageInformation)); + } + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php new file mode 100755 index 00000000..dbfc3093 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php @@ -0,0 +1,564 @@ +<?php +/** + * $Id: 564bbde3ec5084ed2db570958548af2b9d1c1127 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; +require_once 'phing/system/util/Properties.php'; +require_once 'phing/tasks/ext/phpunit/PHPUnitUtil.php'; +require_once 'phing/tasks/ext/coverage/CoverageReportTransformer.php'; + +/** + * Transforms information in a code coverage database to XML + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 564bbde3ec5084ed2db570958548af2b9d1c1127 $ + * @package phing.tasks.ext.coverage + * @since 2.1.0 + */ +class CoverageReportTask extends Task +{ + private $outfile = "coverage.xml"; + + private $transformers = array(); + + /** the classpath to use (optional) */ + private $classpath = NULL; + + /** the path to the GeSHi library (optional) */ + private $geshipath = ""; + + /** the path to the GeSHi language files (optional) */ + private $geshilanguagespath = ""; + + function setClasspath(Path $classpath) + { + if ($this->classpath === null) + { + $this->classpath = $classpath; + } + else + { + $this->classpath->append($classpath); + } + } + + function createClasspath() + { + $this->classpath = new Path(); + return $this->classpath; + } + + function setGeshiPath($path) + { + $this->geshipath = $path; + } + + function setGeshiLanguagesPath($path) + { + $this->geshilanguagespath = $path; + } + + function __construct() + { + $this->doc = new DOMDocument(); + $this->doc->encoding = 'UTF-8'; + $this->doc->formatOutput = true; + $this->doc->appendChild($this->doc->createElement('snapshot')); + } + + function setOutfile($outfile) + { + $this->outfile = $outfile; + } + + /** + * Generate a report based on the XML created by this task + */ + function createReport() + { + $transformer = new CoverageReportTransformer($this); + $this->transformers[] = $transformer; + return $transformer; + } + + protected function getPackageElement($packageName) + { + $packages = $this->doc->documentElement->getElementsByTagName('package'); + + foreach ($packages as $package) + { + if ($package->getAttribute('name') == $packageName) + { + return $package; + } + } + + return NULL; + } + + protected function addClassToPackage($classname, $element) + { + $packageName = PHPUnitUtil::getPackageName($classname); + + $package = $this->getPackageElement($packageName); + + if ($package === NULL) + { + $package = $this->doc->createElement('package'); + $package->setAttribute('name', $packageName); + $this->doc->documentElement->appendChild($package); + } + + $package->appendChild($element); + } + + /** + * Adds a subpackage to their package + * + * @param string $packageName The name of the package + * @param string $subpackageName The name of the subpackage + * + * @author Benjamin Schultz <bschultz@proqrent.de> + * @return void + */ + protected function addSubpackageToPackage($packageName, $subpackageName) + { + $package = $this->getPackageElement($packageName); + $subpackage = $this->getSubpackageElement($subpackageName); + + if ($package === null) { + $package = $this->doc->createElement('package'); + $package->setAttribute('name', $packageName); + $this->doc->documentElement->appendChild($package); + } + + if ($subpackage === null) { + $subpackage = $this->doc->createElement('subpackage'); + $subpackage->setAttribute('name', $subpackageName); + } + + $package->appendChild($subpackage); + } + + /** + * Returns the subpackage element + * + * @param string $subpackageName The name of the subpackage + * + * @author Benjamin Schultz <bschultz@proqrent.de> + * @return DOMNode|null null when no DOMNode with the given name exists + */ + protected function getSubpackageElement($subpackageName) + { + $subpackages = $this->doc->documentElement->getElementsByTagName('subpackage'); + + foreach ($subpackages as $subpackage) { + if ($subpackage->getAttribute('name') == $subpackageName) { + return $subpackage; + } + } + + return null; + } + + /** + * Adds a class to their subpackage + * + * @param string $classname The name of the class + * @param DOMNode $element The dom node to append to the subpackage element + * + * @author Benjamin Schultz <bschultz@proqrent.de> + * @return void + */ + protected function addClassToSubpackage($classname, $element) + { + $subpackageName = PHPUnitUtil::getSubpackageName($classname); + + $subpackage = $this->getSubpackageElement($subpackageName); + + if ($subpackage === null) { + $subpackage = $this->doc->createElement('subpackage'); + $subpackage->setAttribute('name', $subpackageName); + $this->doc->documentElement->appendChild($subpackage); + } + + $subpackage->appendChild($element); + } + + protected function stripDiv($source) + { + $openpos = strpos($source, "<div"); + $closepos = strpos($source, ">", $openpos); + + $line = substr($source, $closepos + 1); + + $tagclosepos = strpos($line, "</div>"); + + $line = substr($line, 0, $tagclosepos); + + return $line; + } + + protected function highlightSourceFile($filename) + { + if ($this->geshipath) + { + require_once $this->geshipath . '/geshi.php'; + + $source = file_get_contents($filename); + + $geshi = new GeSHi($source, 'php', $this->geshilanguagespath); + + $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); + + $geshi->enable_strict_mode(true); + + $geshi->enable_classes(true); + + $geshi->set_url_for_keyword_group(3, ''); + + $html = $geshi->parse_code(); + + $lines = preg_split("#</?li>#", $html); + + // skip first and last line + array_pop($lines); + array_shift($lines); + + $lines = array_filter($lines); + + $lines = array_map(array($this, 'stripDiv'), $lines); + + return $lines; + } + else + { + $lines = file($filename); + + for ($i = 0; $i < count($lines); $i++) + { + $line = $lines[$i]; + + $line = rtrim($line); + + if (function_exists('mb_check_encoding') && mb_check_encoding($line, 'UTF-8')) { + $lines[$i] = $line; + } + else if (function_exists('mb_convert_encoding')) + { + $lines[$i] = mb_convert_encoding($line, 'UTF-8'); + } + else + { + $lines[$i] = utf8_encode($line); + } + } + + return $lines; + } + } + + protected function transformSourceFile($filename, $coverageInformation, $classStartLine = 1) + { + $sourceElement = $this->doc->createElement('sourcefile'); + $sourceElement->setAttribute('name', basename($filename)); + + /** + * Add original/full filename to document + */ + $sourceElement->setAttribute('sourcefile', $filename); + + $filelines = $this->highlightSourceFile($filename); + + $linenr = 1; + + foreach ($filelines as $line) + { + $lineElement = $this->doc->createElement('sourceline'); + $lineElement->setAttribute('coveredcount', (isset($coverageInformation[$linenr]) ? $coverageInformation[$linenr] : '0')); + + if ($linenr == $classStartLine) + { + $lineElement->setAttribute('startclass', 1); + } + + $textnode = $this->doc->createTextNode($line); + $lineElement->appendChild($textnode); + + $sourceElement->appendChild($lineElement); + + $linenr++; + } + + return $sourceElement; + } + + /** + * Transforms the coverage information + * + * @param string $filename The filename + * @param array $coverageInformation Array with covergae information + * + * @author Michiel Rook <mrook@php.net> + * @author Benjamin Schultz <bschultz@proqrent.de> + * @return void + */ + protected function transformCoverageInformation($filename, $coverageInformation) + { + $classes = PHPUnitUtil::getDefinedClasses($filename, $this->classpath); + + if (is_array($classes)) + { + foreach ($classes as $classname) + { + $reflection = new ReflectionClass($classname); + + $methods = $reflection->getMethods(); + + $classElement = $this->doc->createElement('class'); + $classElement->setAttribute('name', $reflection->getName()); + + $packageName = PHPUnitUtil::getPackageName($reflection->getName()); + $subpackageName = PHPUnitUtil::getSubpackageName($reflection->getName()); + + if ($subpackageName !== null) { + $this->addSubpackageToPackage($packageName, $subpackageName); + $this->addClassToSubpackage($reflection->getName(), $classElement); + } else { + $this->addClassToPackage($reflection->getName(), $classElement); + } + + $classStartLine = $reflection->getStartLine(); + + $methodscovered = 0; + $methodcount = 0; + + // Strange PHP5 reflection bug, classes without parent class or implemented interfaces seem to start one line off + if ($reflection->getParentClass() == NULL && count($reflection->getInterfaces()) == 0) + { + unset($coverageInformation[$classStartLine + 1]); + } + else + { + unset($coverageInformation[$classStartLine]); + } + + // Remove out-of-bounds info + unset($coverageInformation[0]); + + reset($coverageInformation); + + foreach ($methods as $method) + { + // PHP5 reflection considers methods of a parent class to be part of a subclass, we don't + if ($method->getDeclaringClass()->getName() != $reflection->getName()) + { + continue; + } + + // small fix for XDEBUG_CC_UNUSED + if (isset($coverageInformation[$method->getStartLine()])) + { + unset($coverageInformation[$method->getStartLine()]); + } + + if (isset($coverageInformation[$method->getEndLine()])) + { + unset($coverageInformation[$method->getEndLine()]); + } + + if ($method->isAbstract()) + { + continue; + } + + $linenr = key($coverageInformation); + + while ($linenr !== null && $linenr < $method->getStartLine()) + { + next($coverageInformation); + $linenr = key($coverageInformation); + } + + $methodCoveredCount = 0; + $methodTotalCount = 0; + + $methodHasCoveredLine = false; + + while ($linenr !== null && $linenr <= $method->getEndLine()) { + $methodTotalCount++; + $methodHasCoveredLine = true; + + // set covered when CODE is other than -1 (not executed) + if ($coverageInformation[$linenr] > 0 || $coverageInformation[$linenr] == -2) { + $methodCoveredCount++; + } + + next($coverageInformation); + $linenr = key($coverageInformation); + } + + if (($methodTotalCount == $methodCoveredCount) && $methodHasCoveredLine) { + $methodscovered++; + } + + $methodcount++; + } + + $statementcount = count(array_filter( + $coverageInformation, + create_function('$var', 'return ($var != -2);') + )); + + $statementscovered = count(array_filter( + $coverageInformation, + create_function('$var', 'return ($var >= 0);') + )); + + $classElement->appendChild($this->transformSourceFile($filename, $coverageInformation, $classStartLine)); + + $classElement->setAttribute('methodcount', $methodcount); + $classElement->setAttribute('methodscovered', $methodscovered); + $classElement->setAttribute('statementcount', $statementcount); + $classElement->setAttribute('statementscovered', $statementscovered); + $classElement->setAttribute('totalcount', $methodcount + $statementcount); + $classElement->setAttribute('totalcovered', $methodscovered + $statementscovered); + } + } + } + + protected function calculateStatistics() + { + $packages = $this->doc->documentElement->getElementsByTagName('package'); + + $totalmethodcount = 0; + $totalmethodscovered = 0; + + $totalstatementcount = 0; + $totalstatementscovered = 0; + + foreach ($packages as $package) { + $methodcount = 0; + $methodscovered = 0; + + $statementcount = 0; + $statementscovered = 0; + + $subpackages = $package->getElementsByTagName('subpackage'); + + foreach ($subpackages as $subpackage) { + $subpackageMethodCount = 0; + $subpackageMethodsCovered = 0; + + $subpackageStatementCount = 0; + $subpackageStatementsCovered = 0; + + $subpackageClasses = $subpackage->getElementsByTagName('class'); + + foreach ($subpackageClasses as $subpackageClass) { + $subpackageMethodCount += $subpackageClass->getAttribute('methodcount'); + $subpackageMethodsCovered += $subpackageClass->getAttribute('methodscovered'); + + $subpackageStatementCount += $subpackageClass->getAttribute('statementcount'); + $subpackageStatementsCovered += $subpackageClass->getAttribute('statementscovered'); + } + + $subpackage->setAttribute('methodcount', $subpackageMethodCount); + $subpackage->setAttribute('methodscovered', $subpackageMethodsCovered); + + $subpackage->setAttribute('statementcount', $subpackageStatementCount); + $subpackage->setAttribute('statementscovered', $subpackageStatementsCovered); + + $subpackage->setAttribute('totalcount', $subpackageMethodCount + $subpackageStatementCount); + $subpackage->setAttribute('totalcovered', $subpackageMethodsCovered + $subpackageStatementsCovered); + } + + $classes = $package->getElementsByTagName('class'); + + foreach ($classes as $class) { + $methodcount += $class->getAttribute('methodcount'); + $methodscovered += $class->getAttribute('methodscovered'); + + $statementcount += $class->getAttribute('statementcount'); + $statementscovered += $class->getAttribute('statementscovered'); + } + + $package->setAttribute('methodcount', $methodcount); + $package->setAttribute('methodscovered', $methodscovered); + + $package->setAttribute('statementcount', $statementcount); + $package->setAttribute('statementscovered', $statementscovered); + + $package->setAttribute('totalcount', $methodcount + $statementcount); + $package->setAttribute('totalcovered', $methodscovered + $statementscovered); + + $totalmethodcount += $methodcount; + $totalmethodscovered += $methodscovered; + + $totalstatementcount += $statementcount; + $totalstatementscovered += $statementscovered; + } + + $this->doc->documentElement->setAttribute('methodcount', $totalmethodcount); + $this->doc->documentElement->setAttribute('methodscovered', $totalmethodscovered); + + $this->doc->documentElement->setAttribute('statementcount', $totalstatementcount); + $this->doc->documentElement->setAttribute('statementscovered', $totalstatementscovered); + + $this->doc->documentElement->setAttribute('totalcount', $totalmethodcount + $totalstatementcount); + $this->doc->documentElement->setAttribute('totalcovered', $totalmethodscovered + $totalstatementscovered); + } + + function main() + { + $coverageDatabase = $this->project->getProperty('coverage.database'); + + if (!$coverageDatabase) + { + throw new BuildException("Property coverage.database is not set - please include coverage-setup in your build file"); + } + + $database = new PhingFile($coverageDatabase); + + $this->log("Transforming coverage report"); + + $props = new Properties(); + $props->load($database); + + foreach ($props->keys() as $filename) + { + $file = unserialize($props->getProperty($filename)); + + $this->transformCoverageInformation($file['fullname'], $file['coverage']); + } + + $this->calculateStatistics(); + + $this->doc->save($this->outfile); + + foreach ($this->transformers as $transformer) + { + $transformer->setXmlDocument($this->doc); + $transformer->transform(); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php new file mode 100755 index 00000000..cc37800f --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php @@ -0,0 +1,176 @@ +<?php +/** + * $Id: c1667521b5959687560a1bf015905d627785a3c6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/FileWriter.php'; +require_once 'phing/util/ExtendedFileStream.php'; + +/** + * Transform a Phing/Xdebug code coverage xml report. + * The default transformation generates an html report in framed style. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: c1667521b5959687560a1bf015905d627785a3c6 $ + * @package phing.tasks.ext.coverage + * @since 2.1.0 + */ +class CoverageReportTransformer +{ + private $task = NULL; + private $styleDir = ""; + + /** + * @var PhingFile + */ + private $toDir = ""; + + private $document = NULL; + + /** title of the project, used in the coverage report */ + private $title = ""; + + /** + * Whether to use the sorttable JavaScript library, defaults to false + * See {@link http://www.kryogenix.org/code/browser/sorttable/)} + * + * @var boolean + */ + private $useSortTable = false; + + function __construct(Task $task) + { + $this->task = $task; + } + + function setStyleDir($styleDir) + { + $this->styleDir = $styleDir; + } + + function setToDir(PhingFile $toDir) + { + $this->toDir = $toDir; + } + + function setXmlDocument($document) + { + $this->document = $document; + } + + /** + * Setter for title parameter + */ + function setTitle($title) { + $this->title = $title; + } + + /** + * Sets whether to use the sorttable JavaScript library, defaults to false + * See {@link http://www.kryogenix.org/code/browser/sorttable/)} + * + * @param boolean $useSortTable + */ + public function setUseSortTable($useSortTable) + { + $this->useSortTable = (boolean) $useSortTable; + } + + function transform() + { + if (!$this->toDir->exists()) + { + throw new BuildException("Directory '" . $this->toDir . "' does not exist"); + } + + $xslfile = $this->getStyleSheet(); + + $xsl = new DOMDocument(); + $xsl->load($xslfile->getAbsolutePath()); + + $proc = new XSLTProcessor(); + if (defined('XSL_SECPREF_WRITE_FILE')) + { + if (version_compare(PHP_VERSION,'5.4',"<")) + { + ini_set("xsl.security_prefs", XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY); + } + else + { + $proc->setSecurityPrefs(XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY); + } + } + + $proc->importStyleSheet($xsl); + + ExtendedFileStream::registerStream(); + + $toDir = (string) $this->toDir; + + // urlencode() the path if we're on Windows + if (FileSystem::getFileSystem()->getSeparator() == '\\') { + $toDir = urlencode($toDir); + } + + // no output for the framed report + // it's all done by extension... + $proc->setParameter('', 'output.dir', $toDir); + + $proc->setParameter('', 'output.sorttable', $this->useSortTable); + $proc->setParameter('', 'document.title', $this->title); + $proc->transformToXML($this->document); + + ExtendedFileStream::unregisterStream(); + } + + private function getStyleSheet() + { + $xslname = "coverage-frames.xsl"; + + if ($this->styleDir) + { + $file = new PhingFile($this->styleDir, $xslname); + } + else + { + $path = Phing::getResourcePath("phing/etc/$xslname"); + + if ($path === NULL) + { + $path = Phing::getResourcePath("etc/$xslname"); + + if ($path === NULL) + { + throw new BuildException("Could not find $xslname in resource path"); + } + } + + $file = new PhingFile($path); + } + + if (!$file->exists()) + { + throw new BuildException("Could not find file " . $file->getPath()); + } + + return $file; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php new file mode 100755 index 00000000..889a9042 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php @@ -0,0 +1,164 @@ +<?php +/** + * $Id: da84ff4b224cdf3a8061e02d782320ccc492c253 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; +require_once 'phing/system/util/Properties.php'; +require_once 'phing/tasks/ext/coverage/CoverageMerger.php'; + +/** + * Initializes a code coverage database + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: da84ff4b224cdf3a8061e02d782320ccc492c253 $ + * @package phing.tasks.ext.coverage + * @since 2.1.0 + */ +class CoverageSetupTask extends Task +{ + /** the list of filesets containing the .php filename rules */ + private $filesets = array(); + + /** Any filelists of files containing the .php filenames */ + private $filelists = array(); + + /** the filename of the coverage database */ + private $database = "coverage.db"; + + /** the classpath to use (optional) */ + private $classpath = NULL; + + /** + * Add a new fileset containing the .php files to process + * + * @param FileSet the new fileset containing .php files + */ + function addFileSet(FileSet $fileset) + { + $this->filesets[] = $fileset; + } + + /** + * Supports embedded <filelist> element. + * @return FileList + */ + function createFileList() { + $num = array_push($this->filelists, new FileList()); + return $this->filelists[$num-1]; + } + + /** + * Sets the filename of the coverage database to use + * + * @param string the filename of the database + */ + function setDatabase($database) + { + $this->database = $database; + } + + function setClasspath(Path $classpath) + { + if ($this->classpath === null) + { + $this->classpath = $classpath; + } + else + { + $this->classpath->append($classpath); + } + } + + function createClasspath() + { + $this->classpath = new Path(); + return $this->classpath; + } + + /** + * Iterate over all filesets and return the filename of all files. + * + * @return array an array of (basedir, filenames) pairs + */ + private function getFilenames() + { + $files = array(); + + foreach($this->filelists as $fl) { + try { + $list = $fl->getFiles($this->project); + foreach($list as $file) { + $fs = new PhingFile(strval($fl->getDir($this->project)), $file); + $files[] = array('key' => strtolower($fs->getAbsolutePath()), 'fullname' => $fs->getAbsolutePath()); + } + } catch (BuildException $be) { + $this->log($be->getMessage(), Project::MSG_WARN); + } + } + + + foreach ($this->filesets as $fileset) + { + $ds = $fileset->getDirectoryScanner($this->project); + $ds->scan(); + + $includedFiles = $ds->getIncludedFiles(); + + foreach ($includedFiles as $file) + { + $fs = new PhingFile(realpath($ds->getBaseDir()), $file); + + $files[] = array('key' => strtolower($fs->getAbsolutePath()), 'fullname' => $fs->getAbsolutePath()); + } + } + + return $files; + } + + function init() + { + } + + function main() + { + $files = $this->getFilenames(); + + $this->log("Setting up coverage database for " . count($files) . " files"); + + $props = new Properties(); + + foreach ($files as $file) + { + $fullname = $file['fullname']; + $filename = $file['key']; + + $props->setProperty($filename, serialize(array('fullname' => $fullname, 'coverage' => array()))); + } + + $dbfile = new PhingFile($this->database); + + $props->store($dbfile); + + $this->project->setProperty('coverage.database', $dbfile->getAbsolutePath()); + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php new file mode 100644 index 00000000..d9afbb00 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php @@ -0,0 +1,458 @@ +<?php +/** + * $Id: ed00d6f1d05bb5dc7c9967c9ec67fa6f958682ec $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/util/Properties.php'; +require_once 'phing/types/Excludes.php'; + +/** + * Stops the build if any of the specified coverage threshold was not reached + * + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: ed00d6f1d05bb5dc7c9967c9ec67fa6f958682ec $ + * @package phing.tasks.ext.coverage + * @since 2.4.1 + */ +class CoverageThresholdTask extends Task +{ + /** + * Holds an optional classpath + * + * @var Path + */ + private $_classpath = null; + + /** + * Holds the exclusions + * + * @var Excludes + */ + private $_excludes = null; + + /** + * Holds an optional database file + * + * @var PhingFile + */ + private $_database = null; + + /** + * Holds the coverage threshold for the entire project + * + * @var integer + */ + private $_perProject = 25; + + /** + * Holds the coverage threshold for any class + * + * @var integer + */ + private $_perClass = 25; + + /** + * Holds the coverage threshold for any method + * + * @var integer + */ + private $_perMethod = 25; + + /** + * Holds the minimum found coverage value for a class + * + * @var integer + */ + private $_minClassCoverageFound = null; + + /** + * Holds the minimum found coverage value for a method + * + * @var integer + */ + private $_minMethodCoverageFound = null; + + /** + * Number of statements in the entire project + * + * @var integer + */ + private $_projectStatementCount = 0; + + /** + * Number of covered statements in the entire project + * + * @var integer + */ + private $_projectStatementsCovered = 0; + + /** + * Whether to enable detailed logging + * + * @var boolean + */ + private $_verbose = false; + + /** + * Sets an optional classpath + * + * @param Path $classpath The classpath + */ + public function setClasspath(Path $classpath) + { + if ($this->_classpath === null) { + $this->_classpath = $classpath; + } else { + $this->_classpath->append($classpath); + } + } + + /** + * Sets the optional coverage database to use + * + * @param PhingFile The database file + */ + public function setDatabase(PhingFile $database) + { + $this->_database = $database; + } + + /** + * Create classpath object + * + * @return Path + */ + public function createClasspath() + { + $this->classpath = new Path(); + return $this->classpath; + } + + /** + * Sets the coverage threshold for entire project + * + * @param integer $threshold Coverage threshold for entire project + */ + public function setPerProject($threshold) + { + $this->_perProject = $threshold; + } + + /** + * Sets the coverage threshold for any class + * + * @param integer $threshold Coverage threshold for any class + */ + public function setPerClass($threshold) + { + $this->_perClass = $threshold; + } + + /** + * Sets the coverage threshold for any method + * + * @param integer $threshold Coverage threshold for any method + */ + public function setPerMethod($threshold) + { + $this->_perMethod = $threshold; + } + + /** + * Sets whether to enable detailed logging or not + * + * @param boolean $verbose + */ + public function setVerbose($verbose) + { + $this->_verbose = StringHelper::booleanValue($verbose); + } + + /** + * Filter covered statements + * + * @param integer $var Coverage CODE/count + * @return boolean + */ + protected function filterCovered($var) + { + return ($var >= 0 || $var === -2); + } + + /** + * Create excludes object + * + * @return Excludes + */ + public function createExcludes() + { + $this->_excludes = new Excludes($this->project); + return $this->_excludes; + } + + /** + * Calculates the coverage threshold + * + * @param string $filename The filename to analyse + * @param array $coverageInformation Array with coverage information + */ + protected function calculateCoverageThreshold($filename, $coverageInformation) + { + $classes = PHPUnitUtil::getDefinedClasses($filename, $this->_classpath); + + if (is_array($classes)) { + foreach ($classes as $className) { + // Skip class if excluded from coverage threshold validation + if ($this->_excludes !== null) { + if (in_array($className, $this->_excludes->getExcludedClasses())) { + continue; + } + } + + $reflection = new ReflectionClass($className); + $classStartLine = $reflection->getStartLine(); + + // Strange PHP5 reflection bug, classes without parent class + // or implemented interfaces seem to start one line off + if ($reflection->getParentClass() === null + && count($reflection->getInterfaces()) === 0 + ) { + unset($coverageInformation[$classStartLine + 1]); + } else { + unset($coverageInformation[$classStartLine]); + } + + reset($coverageInformation); + + $methods = $reflection->getMethods(); + + foreach ($methods as $method) { + // PHP5 reflection considers methods of a parent class + // to be part of a subclass, we don't + if ($method->getDeclaringClass()->getName() != $reflection->getName()) { + continue; + } + + // Skip method if excluded from coverage threshold validation + if ($this->_excludes !== null) { + $excludedMethods = $this->_excludes->getExcludedMethods(); + + if (isset($excludedMethods[$className])) { + if (in_array($method->getName(), $excludedMethods[$className]) + || in_array($method->getName() . '()', $excludedMethods[$className]) + ) { + continue; + } + } + } + + $methodStartLine = $method->getStartLine(); + $methodEndLine = $method->getEndLine(); + + // small fix for XDEBUG_CC_UNUSED + if (isset($coverageInformation[$methodStartLine])) { + unset($coverageInformation[$methodStartLine]); + } + + if (isset($coverageInformation[$methodEndLine])) { + unset($coverageInformation[$methodEndLine]); + } + + if ($method->isAbstract()) { + continue; + } + + $lineNr = key($coverageInformation); + + while ($lineNr !== null && $lineNr < $methodStartLine) { + next($coverageInformation); + $lineNr = key($coverageInformation); + } + + $methodStatementsCovered = 0; + $methodStatementCount = 0; + + while ($lineNr !== null && $lineNr <= $methodEndLine) { + $methodStatementCount++; + + $lineCoverageInfo = $coverageInformation[$lineNr]; + // set covered when CODE is other than -1 (not executed) + if ($lineCoverageInfo > 0 || $lineCoverageInfo === -2) { + $methodStatementsCovered++; + } + + next($coverageInformation); + $lineNr = key($coverageInformation); + } + + if ($methodStatementCount > 0) { + $methodCoverage = ( $methodStatementsCovered + / $methodStatementCount) * 100; + } else { + $methodCoverage = 0; + } + + if ($methodCoverage < $this->_perMethod + && !$method->isAbstract() + ) { + throw new BuildException( + 'The coverage (' . round($methodCoverage, 2) . '%) ' + . 'for method "' . $method->getName() . '" is lower' + . ' than the specified threshold (' + . $this->_perMethod . '%), see file: "' + . $filename . '"' + ); + } elseif ($methodCoverage < $this->_perMethod + && $method->isAbstract() + && $this->_verbose === true + ) { + $this->log( + 'Skipped coverage threshold for abstract method "' + . $method->getName() . '"' + ); + } + + // store the minimum coverage value for logging (see #466) + if ($this->_minMethodCoverageFound !== null) { + if ($this->_minMethodCoverageFound > $methodCoverage) { + $this->_minMethodCoverageFound = $methodCoverage; + } + } else { + $this->_minMethodCoverageFound = $methodCoverage; + } + } + + $classStatementCount = count($coverageInformation); + $classStatementsCovered = count( + array_filter( + $coverageInformation, + array($this, 'filterCovered') + ) + ); + + if ($classStatementCount > 0) { + $classCoverage = ( $classStatementsCovered + / $classStatementCount) * 100; + } else { + $classCoverage = 0; + } + + if ($classCoverage < $this->_perClass + && !$reflection->isAbstract() + ) { + throw new BuildException( + 'The coverage (' . round($classCoverage, 2) . '%) for class "' + . $reflection->getName() . '" is lower than the ' + . 'specified threshold (' . $this->_perClass . '%), ' + . 'see file: "' . $filename . '"' + ); + } elseif ($classCoverage < $this->_perClass + && $reflection->isAbstract() + && $this->_verbose === true + ) { + $this->log( + 'Skipped coverage threshold for abstract class "' + . $reflection->getName() . '"' + ); + } + + // store the minimum coverage value for logging (see #466) + if ($this->_minClassCoverageFound !== null) { + if ($this->_minClassCoverageFound > $classCoverage) { + $this->_minClassCoverageFound = $classCoverage; + } + } else { + $this->_minClassCoverageFound = $classCoverage; + } + + $this->_projectStatementCount += $classStatementCount; + $this->_projectStatementsCovered += $classStatementsCovered; + } + } + } + + public function main() + { + if ($this->_database === null) { + $coverageDatabase = $this->project + ->getProperty('coverage.database'); + + if (! $coverageDatabase) { + throw new BuildException( + 'Either include coverage-setup in your build file or set ' + . 'the "database" attribute' + ); + } + + $database = new PhingFile($coverageDatabase); + } else { + $database = $this->_database; + } + + $this->log( + 'Calculating coverage threshold: min. ' + . $this->_perProject . '% per project, ' + . $this->_perClass . '% per class and ' + . $this->_perMethod . '% per method is required' + ); + + $props = new Properties(); + $props->load($database); + + foreach ($props->keys() as $filename) { + $file = unserialize($props->getProperty($filename)); + + // Skip file if excluded from coverage threshold validation + if ($this->_excludes !== null) { + if (in_array($file['fullname'], $this->_excludes->getExcludedFiles())) { + continue; + } + } + + $this->calculateCoverageThreshold( + $file['fullname'], + $file['coverage'] + ); + } + + if ($this->_projectStatementCount > 0) { + $coverage = ( $this->_projectStatementsCovered + / $this->_projectStatementCount) * 100; + } else { + $coverage = 0; + } + + if ($coverage < $this->_perProject) { + throw new BuildException( + 'The coverage (' . round($coverage, 2) . '%) for the entire project ' + . 'is lower than the specified threshold (' + . $this->_perProject . '%)' + ); + } + + $this->log( + 'Passed coverage threshold. Minimum found coverage values are: ' + . round($coverage, 2) . '% per project, ' + . round($this->_minClassCoverageFound, 2) . '% per class and ' + . round($this->_minMethodCoverageFound, 2) . '% per method' + ); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php b/buildscripts/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php new file mode 100755 index 00000000..1ea3d5ba --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php @@ -0,0 +1,592 @@ +<?php +/* + * $Id: f8f62d67a784faced2621d2ffc3b1c92e8703b05 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/creole/CreoleTask.php'; +include_once 'phing/system/io/StringReader.php'; + +/** + * Executes a series of SQL statements on a database using Creole. + * + * <p>Statements can + * either be read in from a text file using the <i>src</i> attribute or from + * between the enclosing SQL tags.</p> + * + * <p>Multiple statements can be provided, separated by semicolons (or the + * defined <i>delimiter</i>). Individual lines within the statements can be + * commented using either --, // or REM at the start of the line.</p> + * + * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be + * turned on or off whilst executing the statements. If auto-commit is turned + * on each statement will be executed and committed. If it is turned off the + * statements will all be executed as one transaction.</p> + * + * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs + * during the execution of one of the statements. + * The possible values are: <b>continue</b> execution, only show the error; + * <b>stop</b> execution and commit transaction; + * and <b>abort</b> execution and transaction and fail task.</p> + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Jeff Martin <jeff@custommonkey.org> (Ant) + * @author Michael McCallum <gholam@xtra.co.nz> (Ant) + * @author Tim Stephenson <tim.stephenson@sybase.com> (Ant) + * @package phing.tasks.ext.creole + * @version $Id: f8f62d67a784faced2621d2ffc3b1c92e8703b05 $ + */ +class CreoleSQLExecTask extends CreoleTask { + + private $goodSql = 0; + private $totalSql = 0; + + const DELIM_ROW = "row"; + const DELIM_NORMAL = "normal"; + + /** + * Database connection + */ + private $conn = null; + + /** + * files to load + */ + private $filesets = array(); + + /** + * all filterchains objects assigned to this task + */ + private $filterChains = array(); + + /** + * SQL statement + */ + private $statement = null; + + /** + * SQL input file + */ + private $srcFile = null; + + /** + * SQL input command + */ + private $sqlCommand = ""; + + /** + * SQL transactions to perform + */ + private $transactions = array(); + + /** + * SQL Statement delimiter + */ + private $delimiter = ";"; + + /** + * The delimiter type indicating whether the delimiter will + * only be recognized on a line by itself + */ + private $delimiterType = "normal"; // can't use constant just defined + + /** + * Print SQL results. + */ + private $print = false; + + /** + * Print header columns. + */ + private $showheaders = true; + + /** + * Results Output file. + */ + private $output = null; + + + /** + * Action to perform if an error is found + **/ + private $onError = "abort"; + + /** + * Encoding to use when reading SQL statements from a file + */ + private $encoding = null; + + /** + * Append to an existing file or overwrite it? + */ + private $append = false; + + /** + * Set the name of the SQL file to be run. + * Required unless statements are enclosed in the build file + */ + public function setSrc(PhingFile $srcFile) { + $this->srcFile = $srcFile; + } + + /** + * Set an inline SQL command to execute. + * NB: Properties are not expanded in this text. + */ + public function addText($sql) { + $this->sqlCommand .= $sql; + } + + /** + * Adds a set of files (nested fileset attribute). + */ + public function addFileset(FileSet $set) { + $this->filesets[] = $set; + } + + /** + * Creates a filterchain + * + * @access public + * @return object The created filterchain object + */ + function createFilterChain() { + $num = array_push($this->filterChains, new FilterChain($this->project)); + return $this->filterChains[$num-1]; + } + + /** + * Add a SQL transaction to execute + */ + public function createTransaction() { + $t = new SQLExecTransaction($this); + $this->transactions[] = $t; + return $t; + } + + /** + * Set the file encoding to use on the SQL files read in + * + * @param encoding the encoding to use on the files + */ + public function setEncoding($encoding) { + $this->encoding = $encoding; + } + + /** + * Set the statement delimiter. + * + * <p>For example, set this to "go" and delimitertype to "ROW" for + * Sybase ASE or MS SQL Server.</p> + * + * @param delimiter + */ + public function setDelimiter($delimiter) + { + $this->delimiter = $delimiter; + } + + /** + * Set the Delimiter type for this sql task. The delimiter type takes two + * values - normal and row. Normal means that any occurence of the delimiter + * terminate the SQL command whereas with row, only a line containing just + * the delimiter is recognized as the end of the command. + * + * @param string $delimiterType + */ + public function setDelimiterType($delimiterType) + { + $this->delimiterType = $delimiterType; + } + + /** + * Set the print flag. + * + * @param boolean $print + */ + public function setPrint($print) + { + $this->print = (boolean) $print; + } + + /** + * Print headers for result sets from the + * statements; optional, default true. + * @param boolean $showheaders + */ + public function setShowheaders($showheaders) { + $this->showheaders = (boolean) $showheaders; + } + + /** + * Set the output file; + * optional, defaults to the console. + * @param PhingFile $output + */ + public function setOutput(PhingFile $output) { + $this->output = $output; + } + + /** + * whether output should be appended to or overwrite + * an existing file. Defaults to false. + * @param $append + */ + public function setAppend($append) { + $this->append = (boolean) $append; + } + + + /** + * Action to perform when statement fails: continue, stop, or abort + * optional; default "abort" + */ + public function setOnerror($action) { + $this->onError = $action; + } + + /** + * Load the sql file and then execute it + * @throws BuildException + */ + public function main() { + + $savedTransaction = array(); + for($i=0,$size=count($this->transactions); $i < $size; $i++) { + $savedTransaction[] = clone $this->transactions[$i]; + } + + $savedSqlCommand = $this->sqlCommand; + + $this->sqlCommand = trim($this->sqlCommand); + + try { + if ($this->srcFile === null && $this->sqlCommand === "" + && empty($this->filesets)) { + if (count($this->transactions) === 0) { + throw new BuildException("Source file or fileset, " + . "transactions or sql statement " + . "must be set!", $this->location); + } + } + + if ($this->srcFile !== null && !$this->srcFile->exists()) { + throw new BuildException("Source file does not exist!", $this->location); + } + + // deal with the filesets + for ($i = 0,$size=count($this->filesets); $i < $size; $i++) { + $fs = $this->filesets[$i]; + $ds = $fs->getDirectoryScanner($this->project); + $srcDir = $fs->getDir($this->project); + + $srcFiles = $ds->getIncludedFiles(); + + // Make a transaction for each file + for ($j=0, $size=count($srcFiles); $j < $size; $j++) { + $t = $this->createTransaction(); + $t->setSrc(new PhingFile($srcDir, $srcFiles[$j])); + } + } + + // Make a transaction group for the outer command + $t = $this->createTransaction(); + if ($this->srcFile) $t->setSrc($this->srcFile); + $t->addText($this->sqlCommand); + $this->conn = $this->getConnection(); + + try { + + $this->statement = $this->conn->createStatement(); + + $out = null; + + try { + + if ($this->output !== null) { + $this->log("Opening output file " . $this->output, Project::MSG_VERBOSE); + $out = new BufferedWriter(new FileWriter($this->output->getAbsolutePath(), $this->append)); + } + + // Process all transactions + for ($i=0,$size=count($this->transactions); $i < $size; $i++) { + $this->transactions[$i]->runTransaction($out); + if (!$this->isAutocommit()) { + $this->log("Commiting transaction", Project::MSG_VERBOSE); + $this->conn->commit(); + } + } + if ($out) $out->close(); + } catch (Exception $e) { + if ($out) $out->close(); + throw $e; + } + } catch (IOException $e) { + if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { + try { + $this->conn->rollback(); + } catch (SQLException $ex) {} + } + throw new BuildException($e->getMessage(), $this->location); + } catch (SQLException $e){ + if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { + try { + $this->conn->rollback(); + } catch (SQLException $ex) {} + } + throw new BuildException($e->getMessage(), $this->location); + } + + $this->log($this->goodSql . " of " . $this->totalSql . + " SQL statements executed successfully"); + } catch (Exception $e) { + $this->transactions = $savedTransaction; + $this->sqlCommand = $savedSqlCommand; + throw $e; + } + // finally { + $this->transactions = $savedTransaction; + $this->sqlCommand = $savedSqlCommand; + + } + + + /** + * read in lines and execute them + * @throws SQLException, IOException + */ + public function runStatements(Reader $reader, $out = null) { + $sql = ""; + $line = ""; + + $buffer = ''; + + if ((is_array($this->filterChains)) && (!empty($this->filterChains))) { + $in = FileUtils::getChainedReader(new BufferedReader($reader), $this->filterChains, $this->getProject()); + while(-1 !== ($read = $in->read())) { // -1 indicates EOF + $buffer .= $read; + } + $lines = explode("\n", $buffer); + } else { + $in = new BufferedReader($reader); + + while (($line = $in->readLine()) !== null) { + $lines[] = $line; + } + } + + try { + foreach ($lines as $line) { + $line = trim($line); + $line = ProjectConfigurator::replaceProperties($this->project, $line, + $this->project->getProperties()); + + if (StringHelper::startsWith("//", $line) || + StringHelper::startsWith("--", $line) || + StringHelper::startsWith("#", $line)) { + continue; + } + + if (strlen($line) > 4 + && strtoupper(substr($line,0, 4)) == "REM ") { + continue; + } + + $sql .= " " . $line; + $sql = trim($sql); + + // SQL defines "--" as a comment to EOL + // and in Oracle it may contain a hint + // so we cannot just remove it, instead we must end it + if (strpos($line, "--") !== false) { + $sql .= "\n"; + } + + if ($this->delimiterType == self::DELIM_NORMAL + && StringHelper::endsWith($this->delimiter, $sql) + || $this->delimiterType == self::DELIM_ROW + && $line == $this->delimiter) { + $this->log("SQL: " . $sql, Project::MSG_VERBOSE); + $this->execSQL(StringHelper::substring($sql, 0, strlen($sql) - strlen($this->delimiter)), $out); + $sql = ""; + } + } + + // Catch any statements not followed by ; + if ($sql !== "") { + $this->execSQL($sql, $out); + } + } catch (SQLException $e) { + throw new BuildException("Error running statements", $e); + } + } + + + /** + * Exec the sql statement. + * @throws SQLException + */ + protected function execSQL($sql, $out = null) { + // Check and ignore empty statements + if (trim($sql) == "") { + return; + } + + try { + $this->totalSql++; + if (!$this->statement->execute($sql)) { + $this->log($this->statement->getUpdateCount() . " rows affected", Project::MSG_VERBOSE); + } else { + if ($this->print) { + $this->printResults($out); + } + } + + $this->goodSql++; + + } catch (SQLException $e) { + $this->log("Failed to execute: " . $sql, Project::MSG_ERR); + if ($this->onError != "continue") { + throw new BuildException("Failed to execute SQL", $e); + } + $this->log($e->getMessage(), Project::MSG_ERR); + } + } + + /** + * print any results in the statement. + * @throws SQLException + */ + protected function printResults($out = null) { + + $rs = null; + do { + $rs = $this->statement->getResultSet(); + + if ($rs !== null) { + + $this->log("Processing new result set.", Project::MSG_VERBOSE); + + $line = ""; + + $colsprinted = false; + + while ($rs->next()) { + $fields = $rs->getRow(); + + if (!$colsprinted && $this->showheaders) { + $first = true; + foreach($fields as $fieldName => $ignore) { + if ($first) $first = false; else $line .= ","; + $line .= $fieldName; + } + if ($out !== null) { + $out->write($line); + $out->newLine(); + } else { + print($line.PHP_EOL); + } + $line = ""; + $colsprinted = true; + } // if show headers + + $first = true; + foreach($fields as $columnValue) { + + if ($columnValue != null) { + $columnValue = trim($columnValue); + } + + if ($first) { + $first = false; + } else { + $line .= ","; + } + $line .= $columnValue; + } + + if ($out !== null) { + $out->write($line); + $out->newLine(); + } else { + print($line . PHP_EOL); + } + $line = ""; + + } // while rs->next() + } + } while ($this->statement->getMoreResults()); + print(PHP_EOL); + if ($out !== null) $out->newLine(); + } +} + + +/** + * "Inner" class that contains the definition of a new transaction element. + * Transactions allow several files or blocks of statements + * to be executed using the same JDBC connection and commit + * operation in between. + * + * @package phing.tasks.ext.creole + */ +class SQLExecTransaction { + + private $tSrcFile = null; + private $tSqlCommand = ""; + private $parent; + + function __construct($parent) + { + // Parent is required so that we can log things ... + $this->parent = $parent; + } + + public function setSrc(PhingFile $src) + { + $this->tSrcFile = $src; + } + + public function addText($sql) + { + $this->tSqlCommand .= $sql; + } + + /** + * @throws IOException, SQLException + */ + public function runTransaction($out = null) + { + if (!empty($this->tSqlCommand)) { + $this->parent->log("Executing commands", Project::MSG_INFO); + $this->parent->runStatements(new StringReader($this->tSqlCommand), $out); + } + + if ($this->tSrcFile !== null) { + $this->parent->log("Executing file: " . $this->tSrcFile->getAbsolutePath(), + Project::MSG_INFO); + + $reader = new FileReader($this->tSrcFile); + + $this->parent->runStatements($reader, $out); + $reader->close(); + } + } +} + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/creole/CreoleTask.php b/buildscripts/phing/classes/phing/tasks/ext/creole/CreoleTask.php new file mode 100755 index 00000000..6cff2033 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/creole/CreoleTask.php @@ -0,0 +1,242 @@ +<?php + +/* + * $Id: 91a6dbbd682e6afa8befa95f01ae5fbaed11a72a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/types/Reference.php'; + +/** + * Handles Creole configuration needed by SQL type tasks. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Nick Chalko <nick@chalko.com> (Ant) + * @author Jeff Martin <jeff@custommonkey.org> (Ant) + * @author Michael McCallum <gholam@xtra.co.nz> (Ant) + * @author Tim Stephenson <tim.stephenson@sybase.com> (Ant) + * @version $Id$ + * @package phing.tasks.system + */ +abstract class CreoleTask extends Task { + + /** + * Used for caching loaders / driver. This is to avoid + * getting an OutOfMemoryError when calling this task + * multiple times in a row. + * + * NOT IMPLEMENTED YET + */ + private static $loaderMap = array(); + + private $caching = true; + + /** + * Autocommit flag. Default value is false + */ + private $autocommit = false; + + /** + * [optional] Classpath to Creole driver to use. + * @param string + */ + private $driver; + + /** + * DB url. + */ + private $url; + + /** + * User name. + */ + private $userId; + + /** + * Password + */ + private $password; + + /** + * RDBMS Product needed for this SQL. + **/ + private $rdbms; + + /** + * Initialize CreoleTask. + * This method includes any necessary Creole libraries and triggers + * appropriate error if they cannot be found. This is not done in header + * because we may want this class to be loaded w/o triggering an error. + */ + function init() { + include_once 'creole/Creole.php'; + if (!class_exists('Creole')) { + throw new Exception("Creole task depends on Creole classes being on include_path. (i.e. include of 'creole/Creole.php' failed.)"); + } + } + + /** + * Caching loaders / driver. This is to avoid + * getting an OutOfMemoryError when calling this task + * multiple times in a row; default: true + * @param $enable + */ + public function setCaching($enable) { + $this->caching = $enable; + } + + /** + * Sets the database connection URL; required. + * @param url The url to set + */ + public function setUrl($url) { + $this->url = $url; + } + + /** + * Set the Creole driver to be used. + * + * @param string $driver driver class name + */ + public function setDriver($driver) + { + $this->driver = $driver; + } + + /** + * Sets the password; required. + * @param password The password to set + */ + public function setPassword($password) { + $this->password = $password; + } + + /** + * Auto commit flag for database connection; + * optional, default false. + * @param autocommit The autocommit to set + */ + public function setAutocommit($autocommit) { + $this->autocommit = $autocommit; + } + + /** + * Sets the version string, execute task only if + * rdbms version match; optional. + * @param version The version to set + */ + public function setVersion($version) { + $this->version = $version; + } + + protected function getLoaderMap() { + return self::$loaderMap; + } + + + /** + * Creates a new Connection as using the driver, url, userid and password specified. + * The calling method is responsible for closing the connection. + * @return Connection the newly created connection. + * @throws BuildException if the UserId/Password/Url is not set or there is no suitable driver or the driver fails to load. + */ + protected function getConnection() { + + if ($this->url === null) { + throw new BuildException("Url attribute must be set!", $this->location); + } + + try { + + $this->log("Connecting to " . $this->getUrl(), Project::MSG_VERBOSE); + $info = new Properties(); + + $dsn = Creole::parseDSN($this->url); + + if (!isset($dsn["username"]) && $this->userId === null) { + throw new BuildException("Username must be in URL or userid attribute must be set.", $this->location); + } + + if ($this->userId) { + $dsn["username"] = $this->getUserId(); + } + + if ($this->password) { + $dsn["password"] = $this->getPassword(); + } + + if ($this->driver) { + Creole::registerDriver($dsn['phptype'], $this->driver); + } + + $conn = Creole::getConnection($dsn); + $conn->setAutoCommit($this->autocommit); + return $conn; + + } catch (SQLException $e) { + throw new BuildException($e->getMessage(), $this->location); + } + + } + + public function isCaching($value) { + $this->caching = $value; + } + + /** + * Gets the autocommit. + * @return Returns a boolean + */ + public function isAutocommit() { + return $this->autocommit; + } + + /** + * Gets the url. + * @return Returns a String + */ + public function getUrl() { + return $this->url; + } + + /** + * Gets the userId. + * @return Returns a String + */ + public function getUserId() { + return $this->userId; + } + + /** + * Set the user name for the connection; required. + * @param userId The userId to set + */ + public function setUserid($userId) { + $this->userId = $userId; + } + + /** + * Gets the password. + * @return Returns a String + */ + public function getPassword() { + return $this->password; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php new file mode 100755 index 00000000..a5cc23ff --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php @@ -0,0 +1,436 @@ +<?php +/* + * $Id: 035d43c0c50ca9567e9c8a016fef6a3053164acb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php'; + + +/** + * Generate SQL script for db using dbdeploy schema version table + * and delta scripts + * + * <dbdeploy url="mysql:host=localhost;dbname=test" + * userid="dbdeploy" password="dbdeploy" dir="db" outputfile=""> + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ +class DbDeployTask extends Task +{ + /** + * The tablename to use from the database for storing all changes + * This cannot be changed + * + * @var string + */ + public static $TABLE_NAME = 'changelog'; + + /** + * Connection string for the database connection + * + * @var string + */ + protected $url; + + /** + * The userid for the database connection + * + * @var string + */ + protected $userid; + + /** + * The password of the database user + * + * @var string + */ + protected $password; + + /** + * Path to the directory that holds the database patch files + * + * @var string + */ + protected $dir; + + /** + * Output file for performing all database patches of this deployment + * Contains all the SQL statements that need to be executed + * + * @var string + */ + protected $outputFile = 'dbdeploy_deploy.sql'; + + /** + * Outputfile for undoing the database patches of this deployment + * Contains all the SQL statements that need to be executed + * + * @var string + */ + protected $undoOutputFile = 'dbdeploy_undo.sql'; + + /** + * The deltaset that's being used + * + * @var string + */ + protected $deltaSet = 'Main'; + + /** + * The number of the last change to apply + * + * @var int + */ + protected $lastChangeToApply = 999; + + /** + * Contains the object for the DBMS that is used + * + * @var object + */ + protected $dbmsSyntax = null; + + /** + * Array with all change numbers that are applied already + * + * @var array + */ + protected $appliedChangeNumbers = array(); + + /** + * Checkall attribute + * False means dbdeploy will only apply patches that have a higher number + * than the last patchnumber that was applied + * True means dbdeploy will apply all changes that aren't applied + * already (in ascending order) + * + * @var int + */ + protected $checkall = false; + + /** + * The main function for the task + * + * @throws BuildException + * @return void + */ + public function main() + { + try { + // get correct DbmsSyntax object + $dbms = substr($this->url, 0, strpos($this->url, ':')); + $dbmsSyntaxFactory = new DbmsSyntaxFactory($dbms); + $this->dbmsSyntax = $dbmsSyntaxFactory->getDbmsSyntax(); + + // figure out which revisions are in the db already + $this->appliedChangeNumbers = $this->getAppliedChangeNumbers(); + $this->log('Current db revision: '.$this->getLastChangeAppliedInDb()); + $this->log('Checkall: ' . $this->checkall); + + $this->deploy(); + + } catch (Exception $e) { + throw new BuildException($e); + } + } + + /** + * Get the numbers of all the patches that are already applied according to + * the changelog table in the database + * + * @return array + */ + protected function getAppliedChangeNumbers() + { + if (count($this->appliedChangeNumbers) == 0) { + $this->log('Getting applied changed numbers from DB: ' . $this->url); + $appliedChangeNumbers = array(); + $dbh = new PDO($this->url, $this->userid, $this->password); + $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $sql = "SELECT * + FROM " . DbDeployTask::$TABLE_NAME . " + WHERE delta_set = '$this->deltaSet' + ORDER BY change_number"; + foreach ($dbh->query($sql) as $change) { + $appliedChangeNumbers[] = $change['change_number']; + } + $this->appliedChangeNumbers = $appliedChangeNumbers; + } + return $this->appliedChangeNumbers; + } + + /** + * Get the number of the last patch applied to the database + * + * @return int|mixed The highest patch number that is applied in the db + */ + protected function getLastChangeAppliedInDb() + { + return (count($this->appliedChangeNumbers) > 0) + ? max($this->appliedChangeNumbers) : 0; + } + + /** + * Create the deploy and undo deploy outputfiles + * + * @return void + */ + protected function deploy() + { + // create deploy outputfile + $this->createOutputFile($this->outputFile, false); + + // create undo deploy outputfile + $this->createOutputFile($this->undoOutputFile, true); + } + + /** + * Generate the sql for doing/undoing the deployment and write it to a file + * + * @param string $file + * @param bool $undo + * @return void + */ + protected function createOutputFile($file, $undo = false) + { + $fileHandle = fopen($file, "w+"); + $sql = $this->generateSql($undo); + fwrite($fileHandle, $sql); + } + + /** + * Generate the sql for doing/undoing this deployment + * + * @param bool $undo + * @return string The sql + */ + protected function generateSql($undo = false) + { + $sql = ''; + $lastChangeAppliedInDb = $this->getLastChangeAppliedInDb(); + $files = $this->getDeltasFilesArray(); + $this->sortFiles($files, $undo); + + foreach ($files as $fileChangeNumber => $fileName) { + if ($this->fileNeedsToBeRead($fileChangeNumber, $lastChangeAppliedInDb)) { + $sql .= '-- Fragment begins: ' . $fileChangeNumber . ' --' . "\n"; + + if (!$undo) { + $sql .= 'INSERT INTO ' . DbDeployTask::$TABLE_NAME . ' + (change_number, delta_set, start_dt, applied_by, description)' . + ' VALUES (' . $fileChangeNumber . ', \'' . $this->deltaSet . '\', ' . + $this->dbmsSyntax->generateTimestamp() . + ', \'dbdeploy\', \'' . $fileName . '\');' . "\n"; + } + + // read the file + $fullFileName = $this->dir . '/' . $fileName; + $fh = fopen($fullFileName, 'r'); + $contents = fread($fh, filesize($fullFileName)); + // allow construct with and without space added + $split = strpos($contents, '-- //@UNDO'); + if ($split === false) + $split = strpos($contents, '--//@UNDO'); + + if ($undo) { + $sql .= substr($contents, $split + 10) . "\n"; + $sql .= 'DELETE FROM ' . DbDeployTask::$TABLE_NAME . ' + WHERE change_number = ' . $fileChangeNumber . ' + AND delta_set = \'' . $this->deltaSet . '\';' . "\n"; + } else { + $sql .= substr($contents, 0, $split); + $sql .= 'UPDATE ' . DbDeployTask::$TABLE_NAME . ' + SET complete_dt = ' . $this->dbmsSyntax->generateTimestamp() . ' + WHERE change_number = ' . $fileChangeNumber . ' + AND delta_set = \'' . $this->deltaSet . '\';' . "\n"; + } + + $sql .= '-- Fragment ends: ' . $fileChangeNumber . ' --' . "\n"; + } + } + + return $sql; + } + + /** + * Get a list of all the patch files in the patch file directory + * + * @return array + */ + protected function getDeltasFilesArray() + { + $files = array(); + $baseDir = realpath($this->dir); + $dh = opendir($baseDir); + $fileChangeNumberPrefix = ''; + while (($file = readdir($dh)) !== false) { + if (preg_match('[\d+]', $file, $fileChangeNumberPrefix)) { + $files[intval($fileChangeNumberPrefix[0])] = $file; + } + } + return $files; + } + + /** + * Sort files in the patch files directory (ascending or descending depending on $undo boolean) + * + * @param array $files + * @param bool $undo + * @return void + */ + protected function sortFiles(&$files, $undo) + { + if ($undo) { + krsort($files); + } else { + ksort($files); + } + } + + /** + * Determine if this patch file need to be deployed + * (using fileChangeNumber, lastChangeAppliedInDb and $this->checkall) + * + * @param int $fileChangeNumber + * @param string $lastChangeAppliedInDb + * @return bool True or false if patch file needs to be deployed + */ + protected function fileNeedsToBeRead($fileChangeNumber, $lastChangeAppliedInDb) + { + if ($this->checkall) { + return (!in_array($fileChangeNumber, $this->appliedChangeNumbers)); + } else { + return ($fileChangeNumber > $lastChangeAppliedInDb && $fileChangeNumber <= $this->lastChangeToApply); + } + } + + /** + * Set the url for the database connection + * + * @param string $url + * @return void + */ + public function setUrl($url) + { + $this->url = $url; + } + + /** + * Set the userid for the database connection + * + * @param string $userid + * @return void + */ + public function setUserId($userid) + { + $this->userid = $userid; + } + + /** + * Set the password for the database connection + * + * @param string $password + * @return void + */ + public function setPassword($password) + { + $this->password = $password; + } + + /** + * Set the directory where to find the patchfiles + * + * @param string $dir + * @return void + */ + public function setDir($dir) + { + $this->dir = $dir; + } + + /** + * Set the outputfile which contains all patch sql statements for this deployment + * + * @param string $outputFile + * @return void + */ + public function setOutputFile($outputFile) + { + $this->outputFile = $outputFile; + } + + /** + * Set the undo outputfile which contains all undo statements for this deployment + * + * @param string $undoOutputFile + * @return void + */ + public function setUndoOutputFile($undoOutputFile) + { + $this->undoOutputFile = $undoOutputFile; + } + + /** + * Set the lastchangetoapply property + * + * @param int $lastChangeToApply + * @return void + */ + public function setLastChangeToApply($lastChangeToApply) + { + $this->lastChangeToApply = $lastChangeToApply; + } + + /** + * Set the deltaset property + * + * @param string $deltaSet + * @return void + */ + public function setDeltaSet($deltaSet) + { + $this->deltaSet = $deltaSet; + } + + /** + * Set the checkall property + * + * @param bool $checkall + * @return void + */ + public function setCheckAll($checkall) + { + $this->checkall = (int)$checkall; + } + + /** + * Add a new fileset. + * @return FileSet + */ + public function createFileSet() + { + $this->fileset = new FileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php new file mode 100755 index 00000000..f20d2df4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php @@ -0,0 +1,34 @@ +<?php +/* + * $Id: 40826765e423da7500094b84f0025f75c8fdde87 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class for generating necessary server-specific SQL commands + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ + +abstract class DbmsSyntax +{ + public abstract function generateTimestamp(); +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php new file mode 100755 index 00000000..1cc163f5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php @@ -0,0 +1,67 @@ +<?php +/* + * $Id: 0efe41b73233dd4396055518a125e4ff642693c5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/dbdeploy/DbmsSyntax.php'; + +/** + * Factory for generating dbms-specific syntax-generating objects + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ + +class DbmsSyntaxFactory +{ + private $dbms; + + public function __construct($dbms) + { + $this->dbms = $dbms; + } + + public function getDbmsSyntax() + { + switch ($this->dbms){ + case('sqlite') : + require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php'; + return new DbmsSyntaxSQLite(); + case('mysql'): + require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php'; + return new DbmsSyntaxMysql(); + case 'odbc': + case('mssql'): + case 'dblib': + require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php'; + return new DbmsSyntaxMsSql(); + case('pgsql'): + require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php'; + return new DbmsSyntaxPgSQL(); + case 'oci': + require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php'; + return new DbmsSyntaxOracle(); + default: + throw new Exception($this->dbms . ' is not supported by dbdeploy task.'); + } + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php new file mode 100755 index 00000000..0b505d8e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php @@ -0,0 +1,37 @@ +<?php +/* + * $Id: 10012c2626ef9befc8c18efa5898704e269d6164 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class for generating necessary server-specific SQL commands + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ + +class DbmsSyntaxMsSql extends DbmsSyntax +{ + public function generateTimestamp() + { + return "DATEDIFF(s, '19700101', GETDATE())"; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php new file mode 100755 index 00000000..86bf8ae0 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php @@ -0,0 +1,37 @@ +<?php +/* + * $Id: 3a8bab5e99f20e29f5c7dfe7d02f7a91fb8ccecd $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class for generating necessary server-specific SQL commands + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ + +class DbmsSyntaxMysql extends DbmsSyntax +{ + public function generateTimestamp() + { + return "NOW()"; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php new file mode 100755 index 00000000..15b0b0a0 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php @@ -0,0 +1,37 @@ +<?php +/* + * $Id: be6f99d787b94f7f2c1c8e359def3d465386bba9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class for generating necessary server-specific SQL commands + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ + +class DbmsSyntaxOracle extends DbmsSyntax +{ + public function generateTimestamp() + { + return "(sysdate - to_date('01-JAN-1970','DD-MON-YYYY')) * (86400)"; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php new file mode 100755 index 00000000..6c07931c --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php @@ -0,0 +1,36 @@ +<?php +/* + * $Id: 17845f06b311dbe508e7cb1244cd849a1d3fcada $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class for generating necessary server-specific SQL commands + * + * @author R�my BREUILS + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ +class DbmsSyntaxPgSQL extends DbmsSyntax +{ + public function generateTimestamp() + { + return "NOW()"; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php new file mode 100755 index 00000000..32aa023e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php @@ -0,0 +1,37 @@ +<?php +/* + * $Id: a48723b4c3ef1e5c15417f5ea6e495960e5e018b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class for generating necessary server-specific SQL commands + * + * @author Luke Crouch at SourceForge (http://sourceforge.net) + * @version $Id$ + * @package phing.tasks.ext.dbdeploy + */ + +class DbmsSyntaxSQLite extends DbmsSyntax +{ + public function generateTimestamp() + { + return "strftime('%s','now')"; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/docblox/DocBloxTask.php b/buildscripts/phing/classes/phing/tasks/ext/docblox/DocBloxTask.php new file mode 100755 index 00000000..0c1556c8 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/docblox/DocBloxTask.php @@ -0,0 +1,221 @@ +<?php +/* + * $Id: eaa494390770adc752097a412d63fb863482fd5d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/FileOutputStream.php'; + +/** + * DocBlox Task (http://www.docblox-project.org) + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: eaa494390770adc752097a412d63fb863482fd5d $ + * @since 2.4.6 + * @package phing.tasks.ext.docblox + */ +class DocBloxTask extends Task +{ + /** + * List of filesets + * @var FileSet[] + */ + private $filesets = array(); + + /** + * Destination/target directory + * @var PhingFile + */ + private $destDir = null; + + /** + * name of the template to use + * @var string + */ + private $template = "new_black"; + + /** + * Title of the project + * @var string + */ + private $title = ""; + + /** + * Force DocBlox to be quiet + * @var boolean + */ + private $quiet = true; + + /** + * Nested creator, adds a set of files (nested fileset attribute). + * + * @return FileSet + */ + public function createFileSet() + { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Sets destination/target directory + * @param PhingFile $destDir + */ + public function setDestDir(PhingFile $destDir) + { + $this->destDir = $destDir; + } + + /** + * Convenience setter (@see setDestDir) + * @param PhingFile $output + */ + public function setOutput(PhingFile $output) + { + $this->destDir = $output; + } + + /** + * Sets the template to use + * @param strings $template + */ + public function setTemplate($template) + { + $this->template = (string) $template; + } + + /** + * Sets the title of the project + * @param strings $title + */ + public function setTitle($title) + { + $this->title = (string) $title; + } + + /** + * Forces DocBlox to be quiet + * @param boolean $quiet + */ + public function setQuiet($quiet) + { + $this->quiet = (boolean) $quiet; + } + + /** + * Finds and initializes the DocBlox installation + */ + private function initializeDocBlox() + { + $docbloxPath = null; + + foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) { + $testDocBloxPath = $path . DIRECTORY_SEPARATOR . 'DocBlox' . DIRECTORY_SEPARATOR . 'src'; + + if (file_exists($testDocBloxPath)) { + $docbloxPath = $testDocBloxPath; + } + } + + if (empty($docbloxPath)) { + throw new BuildException("Please make sure DocBlox is installed and on the include_path.", $this->getLocation()); + } + + set_include_path($docbloxPath . PATH_SEPARATOR . get_include_path()); + + require_once $docbloxPath.'/DocBlox/Bootstrap.php'; + + $bootstrap = DocBlox_Bootstrap::createInstance(); + + $autoloader = $bootstrap->registerAutoloader(); + + if ($this->quiet) { + DocBlox_Core_Abstract::config()->logging->level = 'quiet'; + } else { + DocBlox_Core_Abstract::config()->logging->level = 'debug'; + } + + $bootstrap->registerPlugins($autoloader); + } + + /** + * Build a list of files (from the fileset elements) and call the DocBlox parser + * @return string + */ + private function parseFiles() + { + $parser = new DocBlox_Parser(); + + //Only initialize the dispatcher when not already done + if (is_null(DocBlox_Parser_Abstract::$event_dispatcher)) { + DocBlox_Parser_Abstract::$event_dispatcher = new sfEventDispatcher(); + } + $parser->setTitle($this->title); + + $paths = array(); + + // filesets + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $dir = $fs->getDir($this->project); + $srcFiles = $ds->getIncludedFiles(); + + foreach ($srcFiles as $file) { + $paths[] = $dir . FileSystem::getFileSystem()->getSeparator() . $file; + } + } + + $this->log("Will parse " . count($paths) . " file(s)", Project::MSG_VERBOSE); + + $files = new DocBlox_Parser_Files(); + $files->addFiles($paths); + + $parser->setPath($files->getProjectRoot()); + + return $parser->parseFiles($files); + } + + /** + * Task entry point + * @see Task::main() + */ + public function main() + { + if (empty($this->destDir)) { + throw new BuildException("You must supply the 'destdir' attribute", $this->getLocation()); + } + + if (empty($this->filesets)) { + throw new BuildException("You have not specified any files to include (<fileset>)", $this->getLocation()); + } + + $this->initializeDocBlox(); + + $xml = $this->parseFiles(); + + $this->log("Transforming...", Project::MSG_VERBOSE); + + $transformer = new DocBlox_Transformer(); + $transformer->setTemplatesPath(DocBlox_Core_Abstract::config()->paths->templates); + $transformer->setTemplates($this->template); + $transformer->setSource($xml); + $transformer->setTarget($this->destDir->getAbsolutePath()); + $transformer->execute(); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitBaseTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitBaseTask.php new file mode 100644 index 00000000..8381cc64 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitBaseTask.php @@ -0,0 +1,134 @@ +<?php +/* + * $Id: 5ffc8c9c51dfa9bd0d691a88db670cdeb5f985c1 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/BuildException.php'; + +/** + * Base class for Git tasks + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 5ffc8c9c51dfa9bd0d691a88db670cdeb5f985c1 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +abstract class GitBaseTask extends Task +{ + /** + * Bath to git binary + * @var string + */ + private $gitPath = '/usr/bin/git'; + + /** + * @var VersionControl_Git + */ + private $gitClient = null; + + /** + * Current repository directory + * @var string + */ + private $repository; + + /** + * Initialize Task. + * Check and include necessary libraries. + */ + public function init() + { + @include_once 'VersionControl/Git.php'; + if (false == class_exists('VersionControl_Git')) { + throw new BuildException("The Git tasks depend on PEAR\'s " + . "VersionControl_Git package.", $this->getLocation()); + } + } + + /** + * Set repository directory + * + * @param string $repository Repo directory + * @return GitBaseTask + */ + public function setRepository($repository) + { + $this->repository = $repository; + return $this; + } + + /** + * Get repository directory + * + * @return string + */ + public function getRepository() + { + return $this->repository; + } + + /** + * Set path to git executable + * + * @param string $gitPath New path to git repository + * @return GitBaseTask + */ + public function setGitPath($gitPath) + { + $this->gitPath = $gitPath; + return $this; + } + + /** + * Get path to git executable + * + * @return string + */ + public function getGitPath() + { + return $this->gitPath; + } + + protected function getGitClient($reset = false, $repository = null) + { + $this->gitClient = ($reset === true) ? null : $this->gitClient; + $repository = (null === $repository) + ? $this->getRepository() + : $repository; + + if(null === $this->gitClient) { + try { + $this->gitClient = new VersionControl_Git($repository); + } catch (VersionControl_Git_Exception $e) { + // re-package + throw new BuildException( + 'You must specify readable directory as repository.'); + + } + } + $this->gitClient->setGitCommandPath($this->getGitPath()); + + return $this->gitClient; + } +} + + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitBranchTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitBranchTask.php new file mode 100644 index 00000000..54a0eb20 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitBranchTask.php @@ -0,0 +1,296 @@ +<?php +/* + * $Id: 88a8737d783614bcd5acb103738fafc23c509225 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper aroung git-branch + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 88a8737d783614bcd5acb103738fafc23c509225 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitBranchTask extends GitBaseTask +{ + /** + * Branch name + * @var string + */ + private $branchname; + + /** + * New Branch name for git-branch -m | -M + * @var string + */ + private $newbranch; + + /** + * If not HEAD, specify starting point + * @var string + */ + private $startPoint; + + /** + * --set-upstream key to git-branch + * @var boolean + */ + private $setUpstream = false; + + /** + * --track key to git-branch + * @var boolean + */ + private $track = false; + + /** + * --no-track key to git-branch + * @var boolean + */ + private $noTrack = false; + + /** + * --force, -f key to git-branch + * @var boolean + */ + private $force = false; + + /** + * -d, -D, -m, -M options to git-branch + * Respective task options: + * delete, forceDelete, move, forceMove + * @var array + */ + private $extraOptions = array( + 'd' => false, + 'D' => false, + 'm' => false, + 'M' => false, + ); + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + if (null === $this->getBranchname()) { + throw new BuildException('"branchname" is required parameter'); + } + + // if we are moving branch, we need to know new name + if ($this->isMove() || $this->isForceMove()) { + if (null === $this->getNewbranch()) { + throw new BuildException('"newbranch" is required parameter'); + } + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('branch'); + $command + ->setOption('set-upstream', $this->isSetUpstream()) + ->setOption('no-track', $this->isNoTrack()) + ->setOption('force', $this->isForce()); + if ($this->isNoTrack() == false) { + $command->setOption('track', $this->getTrack()); + } + + // check extra options (delete, move) + foreach ($this->extraOptions as $option => $flag) { + if ($flag) { + $command->setOption($option, true); + } + } + + $command->addArgument($this->getBranchname()); + + if (null !== $this->getStartPoint()) { + $command->addArgument($this->getStartPoint()); + } + + if (null !== $this->getNewbranch()) { + $command->addArgument($this->getNewbranch()); + } + + $this->log('git-branch command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed.'); + } + + $this->log( + sprintf('git-branch: branch "%s" repository', $this->getRepository()), + Project::MSG_INFO); + $this->log('git-branch output: ' . trim($output), Project::MSG_INFO); + } + + public function setSetUpstream($flag) + { + $this->setUpstream = $flag; + } + + public function getSetUpstream() + { + return $this->setUpstream; + } + + public function isSetUpstream() + { + return $this->getSetUpstream(); + } + + public function setTrack($flag) + { + $this->track = $flag; + } + + public function getTrack() + { + return $this->track; + } + + public function isTrack() + { + return $this->getTrack(); + } + + public function setNoTrack($flag) + { + $this->noTrack = $flag; + } + + public function getNoTrack() + { + return $this->noTrack; + } + + public function isNoTrack() + { + return $this->getNoTrack(); + } + + public function setForce($flag) + { + $this->force = $flag; + } + + public function getForce() + { + return $this->force; + } + + public function isForce() + { + return $this->getForce(); + } + + public function setBranchname($branchname) + { + $this->branchname = $branchname; + } + + public function getBranchname() + { + return $this->branchname; + } + + public function setStartPoint($startPoint) + { + $this->startPoint = $startPoint; + } + + public function getStartPoint() + { + return $this->startPoint; + } + + public function setDelete($flag) + { + $this->extraOptions['d'] = $flag; + } + + public function getDelete() + { + return $this->extraOptions['d']; + } + + public function isDelete() + { + return $this->getDelete(); + } + + public function setForceDelete($flag) + { + $this->extraOptions['D'] = $flag; + } + + public function getForceDelete() + { + return $this->extraOptions['D']; + } + + public function setMove($flag) + { + $this->extraOptions['m'] = $flag; + } + + public function getMove() + { + return $this->extraOptions['m']; + } + + public function isMove() + { + return $this->getMove(); + } + + public function setForceMove($flag) + { + $this->extraOptions['M'] = $flag; + } + + public function getForceMove() + { + return $this->extraOptions['M']; + } + + public function isForceMove() + { + return $this->getForceMove(); + } + + public function setNewBranch($name) + { + $this->newbranch = $name; + } + + public function getNewBranch() + { + return $this->newbranch; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php new file mode 100644 index 00000000..16a2bd9f --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php @@ -0,0 +1,256 @@ +<?php +/* + * $Id: a1dcb809b44bfd34c3af154683dd2200c814e5f0 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; +/** + * Wrapper around git-checkout + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: a1dcb809b44bfd34c3af154683dd2200c814e5f0 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitCheckoutTask extends GitBaseTask +{ + /** + * Branch name + * @var string + */ + private $branchname; + + /** + * If not HEAD, specify starting point + * @var string + */ + private $startPoint; + + /** + * --force, -f key to git-checkout + * @var boolean + */ + private $force = false; + + /** + * --quiet, -q key to git-checkout + * @var boolean + */ + private $quiet = false; + + /** + * When creating a new branch, set up "upstream" configuration. + * --track key to git-checkout + * @var boolean + */ + private $track = false; + + /** + * Do not set up "upstream" configuration + * --no-track key to git-checkout + * @var boolean + */ + private $noTrack = false; + + /** + * -b, -B, -m options to git-checkout + * Respective task options: + * create, forceCreate, merge + * @var array + */ + private $extraOptions = array( + 'b' => false, + 'B' => false, + 'm' => false, + ); + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + if (null === $this->getBranchname()) { + throw new BuildException('"branchname" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('checkout'); + $command + ->setOption('no-track', $this->isNoTrack()) + ->setOption('q', $this->isQuiet()) + ->setOption('force', $this->isForce()) + ->setOption('b', $this->isCreate()) + ->setOption('B', $this->isForceCreate()) + ->setOption('m', $this->isMerge()); + if ($this->isNoTrack()) { + $command->setOption('track', $this->isTrack()); + } + + $command->addArgument($this->getBranchname()); + + if (null !== $this->getStartPoint()) { + $command->addArgument($this->getStartPoint()); + } + + $this->log('git-checkout command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed.'); + } + + $this->log( + sprintf('git-checkout: checkout "%s" repository', $this->getRepository()), + Project::MSG_INFO); + $this->log('git-checkout output: ' . trim($output), Project::MSG_INFO); + } + + public function setBranchname($branchname) + { + $this->branchname = $branchname; + } + + public function getBranchname() + { + return $this->branchname; + } + + public function setStartPoint($startPoint) + { + $this->startPoint = $startPoint; + } + + public function getStartPoint() + { + return $this->startPoint; + } + + public function setForce($flag) + { + $this->force = $flag; + } + + public function getForce() + { + return $this->force; + } + + public function isForce() + { + return $this->getForce(); + } + + public function setQuiet($flag) + { + $this->quiet = $flag; + } + + public function getQuiet() + { + return $this->quiet; + } + + public function isQuiet() + { + return $this->getQuiet(); + } + + public function setTrack($flag) + { + $this->track = $flag; + } + + public function getTrack() + { + return $this->track; + } + + public function isTrack() + { + return $this->getTrack(); + } + + public function setNoTrack($flag) + { + $this->noTrack = $flag; + } + + public function getNoTrack() + { + return $this->noTrack; + } + + public function isNoTrack() + { + return $this->getNoTrack(); + } + + public function setCreate($flag) + { + $this->extraOptions['b'] = $flag; + } + + public function getCreate() + { + return $this->extraOptions['b']; + } + + public function isCreate() + { + return $this->getCreate(); + } + + // -B flag is not found in all versions of git + // --force is present everywhere + public function setForceCreate($flag) + { + $this->setForce($flag); + } + + public function getForceCreate() + { + return $this->extraOptions['B']; + } + + public function isForceCreate() + { + return $this->getForceCreate(); + } + + public function setMerge($flag) + { + $this->extraOptions['m'] = $flag; + } + + public function getMerge() + { + return $this->extraOptions['m']; + } + + public function isMerge() + { + return $this->getMerge(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitCloneTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitCloneTask.php new file mode 100644 index 00000000..3d1eb76f --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitCloneTask.php @@ -0,0 +1,128 @@ +<?php +/* + * $Id: 0d9ce448c11e505885b9c5362f5c2d399e205f90 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; +/** + * Wrapper around git-clone + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 0d9ce448c11e505885b9c5362f5c2d399e205f90 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitCloneTask extends GitBaseTask +{ + /** + * Whether --bare key should be set for git-init + * @var string + */ + private $isBare = false; + + /** + * Path to target directory + * @var string + */ + private $targetPath; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + if (null === $this->getTargetPath()) { + throw new BuildException('"targetPath" is required parameter'); + } + + $files = @scandir($this->getTargetPath()); + if (isset($files) && is_array($files) && (count($files) > 2)) { + throw new BuildException( + sprintf( + '"%s" target directory is not empty', + $this->getTargetPath()) + ); + } + + $client = $this->getGitClient(false, getcwd()); + + try { + $client->createClone( + $this->getRepository(), + $this->isBare(), + $this->getTargetPath()); + } catch (Exception $e) { + throw new BuildException('The remote end hung up unexpectedly'); + } + + $msg = 'git-clone: cloning ' + . ($this->isBare() ? '(bare) ' : '') + . '"' . $this->getRepository() .'" repository' + . ' to "' . $this->getTargetPath() .'" directory'; + $this->log($msg, Project::MSG_INFO); + } + + /** + * Get path to target direcotry repo + * + * @return string + */ + public function getTargetPath() + { + return $this->targetPath; + } + + /** + * Set path to source repo + * + * @param string $targetPath Path to repository used as source + * @return void + */ + public function setTargetPath($targetPath) + { + $this->targetPath = $targetPath; + } + + /** + * Alias @see getBare() + * + * @return string + */ + public function isBare() + { + return $this->getBare(); + } + + public function getBare() + { + return $this->isBare; + } + + public function setBare($flag) + { + $this->isBare = (bool)$flag; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitCommitTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitCommitTask.php new file mode 100644 index 00000000..71b26ab9 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitCommitTask.php @@ -0,0 +1,179 @@ +<?php +/* + * $Id: 355a6d3cf8e182652b4acf3af0a6cd3eaa58fd02 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; +/** + * Wrapper around git-commit + * + * @package Phing.tasks.ext.git + * @author Jonathan Creasy <jonathan.creasy@gmail.com> + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitCommitTask extends GitBaseTask +{ + /** + * Path to target directory + * @var string + */ + private $targetPath; + + private $allFiles; + + private $message; + + private $files; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + if (null === $this->getTargetPath()) { + throw new BuildException('"targetPath" is required parameter'); + } + + if ($this->allFiles !== true && empty($this->files)) + { + throw new BuildException('"allFiles" cannot be false if no files are specified.'); + } + + $client = $this->getGitClient(false, $this->getTargetPath()); + + $options = Array(); + + if ($this->allFiles === true) + { + $options['all'] = true; + } + + $arguments = Array(); + if ($this->allFiles !== true && is_array($this->files)) + { + foreach($files as $file) + { + $arguments[] = $file; + } + } + + if (!empty($this->message)) + { + $arguments[] = $this->message; + } else { + $options['allow-empty-message'] = true; + } + + try { + $command = $git->Command('commit'); + $command->setArguments($arguments); + $command->setOptions($options); + $command->execute(); + } catch (Exception $e) { + throw new BuildException('The remote end hung up unexpectedly'); + } + + $msg = 'git-commit: Executed git commit '; + foreach ($options as $option=>$value) + { + + $msg .= ' --' . $options . '=' . $value; + } + + foreach ($arguments as $argument) + { + $msg .= ' ' . $argument; + } + + $this->log($msg, Project::MSG_INFO); + } + + /** + * Get path to target direcotry repo + * + * @return string + */ + public function getTargetPath() + { + return $this->targetPath; + } + + /** + * Set path to source repo + * + * @param string $targetPath Path to repository used as source + * @return void + */ + public function setTargetPath($targetPath) + { + $this->targetPath = $targetPath; + } + + /** + * Alias @see getAllFiles() + * + * @return string + */ + public function isallFiles() + { + return $this->getallFiles(); + } + + public function getallFiles() + { + return $this->allFiles; + } + + public function setallFiles($flag) + { + $this->allFiles = (bool)$flag; + } + + public function getMessage() + { + return $this->message; + } + + public function setMessage($message) + { + $this->message = $message; + } + + public function getFiles() + { + return $this->files; + } + + public function setFiles($files) + { + if (!$empty($files) && is_array($files)) + { + $this->setallfiles(false); + $this->Files = $files; + } else { + $this->Files = null; + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitFetchTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitFetchTask.php new file mode 100644 index 00000000..4b3e8a3d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitFetchTask.php @@ -0,0 +1,284 @@ +<?php +/* + * $Id: bcddbc1cd2e77003746b048568da8111b48da2fb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper aroung git-fetch + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: bcddbc1cd2e77003746b048568da8111b48da2fb $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitFetchTask extends GitBaseTask +{ + /** + * --force, -f key to git-fetch + * @var boolean + */ + private $force = false; + + /** + * --quiet, -q key to git-fetch + * @var boolean + */ + private $quiet = false; + + /** + * Fetch all remotes + * --all key to git-fetch + * @var boolean + */ + private $allRemotes = false; + + /** + * Keep downloaded pack + * --keep key to git-fetch + * @var boolean + */ + private $keepFiles = false; + + /** + * After fetching, remove any remote tracking branches which no longer + * exist on the remote. + * --prune key to git fetch + * @var boolean + */ + private $prune = false; + + /** + * Disable/enable automatic tag following + * --no-tags key to git-fetch + * @var boolean + */ + private $noTags = false; + + /** + * Fetch all tags (even not reachable from branch heads) + * --tags key to git-fetch + * @var boolean + */ + private $tags = false; + + /** + * <group> argument to git-fetch + * @var string + */ + private $group; + + /** + * <repository> argument to git-fetch + * @var string + */ + private $source = 'origin'; + + /** + * <refspec> argument to git-fetch + * @var string + */ + private $refspec; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('fetch'); + $command + ->setOption('tags', $this->isTags()) + ->setOption('no-tags', $this->isNoTags()) + ->setOption('prune', $this->isPrune()) + ->setOption('keep', $this->isKeepFiles()) + ->setOption('q', $this->isQuiet()) + ->setOption('force', $this->isForce()); + + // set operation target + if ($this->isAllRemotes()) { // --all + $command->setOption('all', true); + } elseif ($this->getGroup()) { // <group> + $command->addArgument($this->getGroup()); + } elseif ($this->getSource()) { // <repository> [<refspec>] + $command->addArgument($this->getSource()); + if ($this->getRefspec()) { + $command->addArgument($this->getRefspec()); + } + } else { + throw new BuildException('No remote repository specified'); + } + + $this->log('git-fetch command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed.'); + } + + $this->log( + sprintf('git-fetch: branch "%s" repository', $this->getRepository()), + Project::MSG_INFO); + $this->log('git-fetch output: ' . trim($output), Project::MSG_INFO); + } + + public function setForce($flag) + { + $this->force = $flag; + } + + public function getForce() + { + return $this->force; + } + + public function isForce() + { + return $this->getForce(); + } + + public function setQuiet($flag) + { + $this->quiet = $flag; + } + + public function getQuiet() + { + return $this->quiet; + } + + public function isQuiet() + { + return $this->getQuiet(); + } + + public function setAll($flag) + { + $this->allRemotes = $flag; + } + + public function getAll() + { + return $this->allRemotes; + } + + public function isAllRemotes() + { + return $this->getAll(); + } + + public function setKeep($flag) + { + $this->keepFiles = $flag; + } + + public function getKeep() + { + return $this->keepFiles; + } + + public function isKeepFiles() + { + return $this->getKeep(); + } + + public function setPrune($flag) + { + $this->prune = $flag; + } + + public function getPrune() + { + return $this->prune; + } + + public function isPrune() + { + return $this->getPrune(); + } + + public function setNoTags($flag) + { + $this->noTags = $flag; + } + + public function getNoTags() + { + return $this->noTags; + } + + public function isNoTags() + { + return $this->getNoTags(); + } + + public function setTags($flag) + { + $this->tags = $flag; + } + + public function getTags() + { + return $this->tags; + } + + public function isTags() + { + return $this->getTags(); + } + + public function setSource($source) + { + $this->source = $source; + } + + public function getSource() + { + return $this->source; + } + + public function setRefspec($spec) + { + $this->refspec = $spec; + } + + public function getRefspec() + { + return $this->refspec; + } + + public function setGroup($group) + { + $this->group = $group; + } + + public function getGroup() + { + return $this->group; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitGcTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitGcTask.php new file mode 100644 index 00000000..12de4119 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitGcTask.php @@ -0,0 +1,158 @@ +<?php +/* + * $Id: 13487520850c3a7ad71d85f02afbddfd408bfbba $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; +/** + * Wrapper around git-gc + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 13487520850c3a7ad71d85f02afbddfd408bfbba $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitGcTask extends GitBaseTask +{ + /** + * --aggressive key to git-gc + * @var boolean + */ + private $isAggressive = false; + + /** + * --auto key to git-gc + * @var boolean + */ + private $isAuto = false; + + /** + * --no-prune key to git-gc + * @var boolean + */ + private $noPrune = false; + + /** + * --prune=<date>option of git-gc + * @var string + */ + private $prune = '2.weeks.ago'; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('gc'); + $command + ->setOption('aggressive', $this->isAggressive()) + ->setOption('auto', $this->isAuto()) + ->setOption('no-prune', $this->isNoPrune()); + if ($this->isNoPrune() == false) { + $command->setOption('prune', $this->getPrune()); + } + + // suppress output + $command->setOption('q'); + + $this->log('git-gc command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed'); + } + + $this->log( + sprintf('git-gc: cleaning up "%s" repository', $this->getRepository()), + Project::MSG_INFO); + } + + /** + * @see getAggressive() + */ + public function isAggressive() + { + return $this->getAggressive(); + } + + public function getAggressive() + { + return $this->isAggressive; + } + + public function setAggressive($flag) + { + $this->isAggressive = (bool)$flag; + } + + /** + * @see getAuto() + */ + public function isAuto() + { + return $this->getAuto(); + } + + public function getAuto() + { + return $this->isAuto; + } + + public function setAuto($flag) + { + $this->isAuto = (bool)$flag; + } + + /** + * @see NoPrune() + */ + public function isNoPrune() + { + return $this->getNoPrune(); + } + + public function getNoPrune() + { + return $this->noPrune; + } + + public function setNoPrune($flag) + { + $this->noPrune = (bool)$flag; + } + + public function getPrune() + { + return $this->prune; + } + + public function setPrune($date) + { + $this->prune = $date; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitInitTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitInitTask.php new file mode 100644 index 00000000..b4654cae --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitInitTask.php @@ -0,0 +1,81 @@ +<?php +/* + * $Id: 5e66bb51f299c733e4410258f40dcf61f6e96e2f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/BuildException.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Repository initialization task + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 5e66bb51f299c733e4410258f40dcf61f6e96e2f $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitInitTask extends GitBaseTask +{ + + /** + * Whether --bare key should be set for git-init + * @var string + */ + private $isBare = false; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(); + $client->initRepository($this->isBare()); + + $msg = 'git-init: initializing ' + . ($this->isBare() ? '(bare) ' : '') + . '"' . $this->getRepository() .'" repository'; + $this->log($msg, Project::MSG_INFO); + } + + /** + * Alias @see getBare() + * + * @return string + */ + public function isBare() + { + return $this->getBare(); + } + + public function getBare() + { + return $this->isBare; + } + + public function setBare($flag) + { + $this->isBare = (bool)$flag; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitLogTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitLogTask.php new file mode 100644 index 00000000..c1d8058a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitLogTask.php @@ -0,0 +1,270 @@ +<?php +/* + * $Id: 27b94c44aa26823164ce02628de06ff8b44717f7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper aroung git-log + * + * @author Evan Kaufman <evan@digitalflophouse.com> + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 27b94c44aa26823164ce02628de06ff8b44717f7 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.5 + */ +class GitLogTask extends GitBaseTask +{ + /** + * Generate a diffstat. See --stat of git-log + * @var string|boolean + */ + private $stat = false; + + /** + * Names + status of changed files. See --name-status of git-log + * @var boolean + */ + private $nameStatus = false; + + /** + * Number of commits to show. See -<n>|-n|--max-count of git-log + * @var integer + */ + private $maxCount; + + /** + * Don't show commits with more than one parent. See --no-merges of git-log + * @var boolean + */ + private $noMerges = false; + + /** + * Commit format. See --format of git-log + * @var string + */ + private $format = 'medium'; + + /** + * Date format. See --date of git-log + * @var string + */ + private $date; + + /** + * <since> argument to git-log + * @var string + */ + private $sinceCommit; + + /** + * <until> argument to git-log + * @var string + */ + private $untilCommit = 'HEAD'; + + /** + * <path> arguments to git-log + * Accepts one or more paths delimited by PATH_SEPARATOR + * @var string + */ + private $paths; + + /** + * Property name to set with output value from git-log + * @var string + */ + private $outputProperty; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('log'); + $command + ->setOption('stat', $this->getStat()) + ->setOption('name-status', $this->isNameStatus()) + ->setOption('no-merges', $this->isNoMerges()) + ->setOption('format', $this->getFormat()); + + if (null !== $this->getMaxCount()) { + $command->setOption('max-count', $this->getMaxCount()); + } + + if (null !== $this->getDate()) { + $command->setOption('date', $this->getDate()); + } + + if (null !== $this->getSince()) { + $command->setOption('since', $this->getSince()); + } + $command->setOption('until', $this->getUntil()); + + $command->addDoubleDash(true); + if (null !== $this->getPaths()) { + $command->addDoubleDash(false); + $paths = explode(PATH_SEPARATOR, $this->getPaths()); + foreach ($paths as $path) { + $command->addArgument($path); + } + } + + $this->log('git-log command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed'); + } + + if (null !== $this->outputProperty) { + $this->project->setProperty($this->outputProperty, $output); + } + + $this->log( + sprintf('git-log: commit log for "%s" repository', $this->getRepository()), + Project::MSG_INFO); + $this->log('git-log output: ' . trim($output), Project::MSG_INFO); + } + + public function setStat($stat) + { + $this->stat = $stat; + } + + public function getStat() + { + return $this->stat; + } + + public function setNameStatus($flag) + { + $this->nameStatus = (boolean)$flag; + } + + public function getNameStatus() + { + return $this->nameStatus; + } + + public function isNameStatus() + { + return $this->getNameStatus(); + } + + public function setMaxCount($count) + { + $this->maxCount = (int)$count; + } + + public function getMaxCount() + { + return $this->maxCount; + } + + public function setNoMerges($flag) + { + $this->noMerges = (bool)$flag; + } + + public function getNoMerges() + { + return $this->noMerges; + } + + public function isNoMerges() + { + return $this->getNoMerges(); + } + + public function setFormat($format) + { + $this->format = $format; + } + + public function getFormat() + { + return $this->format; + } + + public function setDate($date) + { + $this->date = $date; + } + + public function getDate() + { + return $this->date; + } + + public function setSince($since) + { + $this->sinceCommit = $since; + } + + public function getSince() + { + return $this->sinceCommit; + } + + public function setAfter($after) + { + $this->setSince($after); + } + + public function setUntil($until) + { + $this->untilCommit = $until; + } + + public function getUntil() + { + return $this->untilCommit; + } + + public function setBefore($before) + { + $this->setUntil($before); + } + + public function setPaths($paths) + { + $this->paths = $paths; + } + + public function getPaths() + { + return $this->paths; + } + + public function setOutputProperty($prop) + { + $this->outputProperty = $prop; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitMergeTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitMergeTask.php new file mode 100644 index 00000000..7a3e17fb --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitMergeTask.php @@ -0,0 +1,258 @@ +<?php +/* + * $Id: 82fabd65e9247cb37fa3fe16c122d525db4fc697 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper aroung git-merge + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 82fabd65e9247cb37fa3fe16c122d525db4fc697 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + * @link http://www.kernel.org/pub/software/scm/git/docs/git-merge.html + */ +class GitMergeTask extends GitBaseTask +{ + /** + * <commit> of git-merge + * @var string + */ + private $remote; + + /** + * Commit message + * @var string + */ + private $message; + + /** + * Merge strategy. See -s <strategy> of git-merge + * Available strategies are: octopus ours recursive resolve subtree + * @var string + */ + private $strategy; + + /** + * -X or --strategy-option of git-merge + * @var string + */ + private $strategyOption; + + /** + * --commit key of git-merge + * @var boolean + */ + private $commit = false; + + /** + * --no-commit key of git-merge + * @var boolean + */ + private $noCommit = false; + + /** + * --ff --no-ff keys to git-merge + * @var boolean + */ + private $fastForwardCommit = false; + + /** + * --quiet, -q key to git-merge + * @var boolean + */ + private $quiet = false; + + /** + * Valid merge strategies + * @var array + */ + private $validStrategies = array( + 'octopus', 'ours', 'recursive', 'resolve', 'subtree'); + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + $remotes = trim($this->getRemote()); + if (null === $remotes || '' === $remotes) { + throw new BuildException('"remote" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('merge'); + $command + ->setOption('commit', $this->isCommit()) + ->setOption('q', $this->isQuiet()); + + if ($this->getMessage()) { + $command->setOption('message', $this->getMessage()); + } + + if (!$this->isCommit()) { + $command->setOption('no-commit', $this->isNoCommit()); + } + + if ($this->isFastForwardCommit()) { + $command->setOption('no-ff', true); + } + + $strategy = $this->getStrategy(); + if ($strategy) { + // check if strategy is valid + if (false === in_array($strategy, $this->validStrategies)) { + throw new BuildException( + "Could not find merge strategy '" . $strategy . "'\n". + "Available strategies are: " . implode(', ', $this->validStrategies)); + } + $command->setOption('strategy', $strategy); + if ($this->getStrategyOption()) { + $command->setOption( + 'strategy-option', $this->getStrategyOption()); + } + } + + $remotes = explode(' ', $this->getRemote()); + foreach ($remotes as $remote) { + $command->addArgument($remote); + } + + $this->log('git-merge command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed.'); + } + + $this->log( + sprintf('git-merge: replaying "%s" commits', $this->getRemote()), + Project::MSG_INFO); + $this->log('git-merge output: ' . trim($output), Project::MSG_INFO); + + } + + public function setRemote($remote) + { + $this->remote = $remote; + } + + public function getRemote() + { + return $this->remote; + } + + public function setMessage($message) + { + $this->message = $message; + } + + public function getMessage() + { + return $this->message; + } + + public function setStrategy($strategy) + { + $this->strategy = $strategy; + } + + public function getStrategy() + { + return $this->strategy; + } + + public function setStrategyOption($strategyOption) + { + $this->strategyOption = $strategyOption; + } + + public function getStrategyOption() + { + return $this->strategyOption; + } + + public function setQuiet($flag) + { + $this->quiet = $flag; + } + + public function getQuiet() + { + return $this->quiet; + } + + public function isQuiet() + { + return $this->getQuiet(); + } + + public function setCommit($flag) + { + $this->commit = (boolean)$flag; + } + + public function getCommit() + { + return $this->commit; + } + + public function isCommit() + { + return $this->getCommit(); + } + + public function setNoCommit($flag) + { + $this->noCommit = (boolean)$flag; + } + + public function getNoCommit() + { + return $this->noCommit; + } + + public function isNoCommit() + { + return $this->getNoCommit(); + } + + public function setFastForwardCommit($flag) + { + $this->fastForwardCommit = $flag; + } + + public function getFastForwardCommit() + { + return $this->fastForwardCommit; + } + + public function isFastForwardCommit() + { + return $this->getFastForwardCommit(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitPullTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitPullTask.php new file mode 100644 index 00000000..1f7cfcc1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitPullTask.php @@ -0,0 +1,373 @@ +<?php +/* + * $Id: f96a4faad59ab1b29abccd59d04269fe0c409084 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper aroung git-pull + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: f96a4faad59ab1b29abccd59d04269fe0c409084 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + */ +class GitPullTask extends GitBaseTask +{ + /** + * <repository> argument to git-pull + * @var string + */ + private $source = 'origin'; + + /** + * <refspec> argument to git-pull + * @var string + */ + private $refspec; + + /** + * --rebase key to git-pull + * @var boolean + */ + private $rebase = false; + + /** + * --no-rebase key to git-pull + * Allow to override --rebase (if set to default true in configuration) + * @var boolean + */ + private $noRebase = false; + + /** + * Merge strategy. See -s <strategy> of git-pull + * @var string + */ + private $strategy; + + /** + * -X or --strategy-option of git-pull + * @var string + */ + private $strategyOption; + + /** + * Fetch all remotes + * --all key to git-pull + * @var boolean + */ + private $allRemotes = false; + + /** + * --append key to git-pull + * @var boolean + */ + private $append = false; + + /** + * Keep downloaded pack + * --keep key to git-pull + * @var boolean + */ + private $keepFiles = false; + + /** + * Disable/enable automatic tag following + * --no-tags key to git-pull + * @var boolean + */ + private $noTags = false; + + /** + * Fetch all tags (even not reachable from branch heads) + * --tags key to git-pull + * @var boolean + */ + private $tags = false; + + /** + * --quiet, -q key to git-pull + * @var boolean + */ + private $quiet = true; + + /** + * --force, -f key to git-pull + * @var boolean + */ + private $force = false; + + /** + * Valid merge strategies + * @var array + */ + private $validStrategies = array( + 'octopus', 'ours', 'recursive', 'resolve', 'subtree'); + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('pull'); + $command + ->setOption('rebase', $this->isRebase()); + + if (!$this->isRebase()) { + $command->setOption('no-rebase', $this->isNoRebase()); + } + + $strategy = $this->getStrategy(); + if ($strategy) { + // check if strategy is valid + if (false === in_array($strategy, $this->validStrategies)) { + throw new BuildException( + "Could not find merge strategy '" . $strategy . "'\n". + "Available strategies are: " . implode(', ', $this->validStrategies)); + } + $command->setOption('strategy', $strategy); + if ($this->getStrategyOption()) { + $command->setOption( + 'strategy-option', $this->getStrategyOption()); + } + } + + // order of arguments is important + $command + ->setOption('tags', $this->isTags()) + ->setOption('no-tags', $this->isNoTags()) + ->setOption('keep', $this->isKeepFiles()) + ->setOption('append', $this->isAppend()) + ->setOption('q', $this->isQuiet()) + ->setOption('force', $this->isForce()); + + // set operation target + if ($this->isAllRemotes()) { // --all + $command->setOption('all', true); + $this->log('git-pull: fetching from all remotes', Project::MSG_INFO); + } elseif ($this->getSource()) { // <repository> [<refspec>] + $command->addArgument($this->getSource()); + if ($this->getRefspec()) { + $command->addArgument($this->getRefspec()); + } + $this->log( + sprintf('git-pull: pulling from %s %s', + $this->getSource(), $this->getRefspec()), + Project::MSG_INFO); + } else { + throw new BuildException('No source repository specified'); + } + + $this->log('git-pull command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed.'); + } + + $this->log('git-pull: complete', Project::MSG_INFO); + $this->log('git-pull output: ' . trim($output), Project::MSG_INFO); + + } + + public function setStrategy($strategy) + { + $this->strategy = $strategy; + } + + public function getStrategy() + { + return $this->strategy; + } + + public function setStrategyOption($strategyOption) + { + $this->strategyOption = $strategyOption; + } + + public function getStrategyOption() + { + return $this->strategyOption; + } + + public function setSource($source) + { + $this->source = $source; + } + + public function getSource() + { + return $this->source; + } + + public function setRefspec($spec) + { + $this->refspec = $spec; + } + + public function getRefspec() + { + return $this->refspec; + } + + public function setAll($flag) + { + $this->allRemotes = $flag; + } + + public function getAll() + { + return $this->allRemotes; + } + + public function isAllRemotes() + { + return $this->getAll(); + } + + public function setAppend($flag) + { + $this->append = (boolean)$flag; + } + + public function getAppend() + { + return $this->append; + } + + public function isAppend() + { + return $this->getAppend(); + } + + public function setKeep($flag) + { + $this->keepFiles = $flag; + } + + public function getKeep() + { + return $this->keepFiles; + } + + public function isKeepFiles() + { + return $this->getKeep(); + } + + public function setNoTags($flag) + { + $this->noTags = $flag; + } + + public function getNoTags() + { + return $this->noTags; + } + + public function isNoTags() + { + return $this->getNoTags(); + } + + public function setTags($flag) + { + $this->tags = $flag; + } + + public function getTags() + { + return $this->tags; + } + + public function isTags() + { + return $this->getTags(); + } + + public function setQuiet($flag) + { + $this->quiet = $flag; + } + + public function getQuiet() + { + return $this->quiet; + } + + public function isQuiet() + { + return $this->getQuiet(); + } + + public function setRebase($flag) + { + $this->rebase = (boolean)$flag; + } + + public function getRebase() + { + return $this->rebase; + } + + public function isRebase() + { + return $this->getRebase(); + } + + public function setNoRebase($flag) + { + $this->noRebase = (boolean)$flag; + } + + public function getNoRebase() + { + return $this->noRebase; + } + + public function isNoRebase() + { + return $this->getNoRebase(); + } + + public function setForce($flag) + { + $this->force = $flag; + } + + public function getForce() + { + return $this->force; + } + + public function isForce() + { + return $this->getForce(); + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitPushTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitPushTask.php new file mode 100644 index 00000000..996fa57f --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitPushTask.php @@ -0,0 +1,255 @@ +<?php +/* + * $Id: a01e7e9f6cc92e419e82b4e99e4a45de6a61eba8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper aroung git-push + * + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: a01e7e9f6cc92e419e82b4e99e4a45de6a61eba8 $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.3 + * @link http://www.kernel.org/pub/software/scm/git/docs/git-push.html + */ +class GitPushTask extends GitBaseTask +{ + /** + * Instead of naming each ref to push, specifies that all refs + * --all key to git-push + * @var boolean + */ + private $allRemotes = false; + + /** + * Mirror to remote repository + * --mirror key to git-push + * @var boolean + */ + private $mirror = false; + + /** + * Same as prefixing repos with colon + * --delete argument to git-push + * @var string + */ + private $delete = false; + + /** + * Push all refs under refs/tags + * --tags key to git-fetch + * @var boolean + */ + private $tags = false; + + /** + * <repository> argument to git-push + * @var string + */ + private $destination = 'origin'; + + /** + * <refspec> argument to git-push + * @var string + */ + private $refspec; + + /** + * --force, -f key to git-push + * @var boolean + */ + private $force = false; + + /** + * --quiet, -q key to git-push + * @var boolean + */ + private $quiet = true; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('push'); + $command + ->setOption('tags', $this->isTags()) + ->setOption('mirror', $this->isMirror()) + ->setOption('delete', $this->isDelete()) + ->setOption('q', $this->isQuiet()) + ->setOption('force', $this->isForce()); + + // set operation target + if ($this->isAllRemotes()) { // --all + $command->setOption('all', true); + $this->log('git-push: push to all refs', Project::MSG_INFO); + } elseif ($this->isMirror()) { // <repository> [<refspec>] + $command->setOption('mirror', true); + $this->log('git-push: mirror all refs', Project::MSG_INFO); + } elseif ($this->getDestination()) { // <repository> [<refspec>] + $command->addArgument($this->getDestination()); + if ($this->getRefspec()) { + $command->addArgument($this->getRefspec()); + } + $this->log( + sprintf('git-push: pushing to %s %s', + $this->getDestination(), $this->getRefspec()), + Project::MSG_INFO); + } else { + throw new BuildException('At least one destination must be provided'); + } + + $this->log('git-push command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + throw new BuildException('Task execution failed.'); + } + + $this->log('git-push: complete', Project::MSG_INFO); + if ($this->isDelete()) { + $this->log('git-push: branch delete requested', Project::MSG_INFO); + } + $this->log('git-push output: ' . trim($output), Project::MSG_INFO); + } + + public function setAll($flag) + { + $this->allRemotes = $flag; + } + + public function getAll() + { + return $this->allRemotes; + } + + public function isAllRemotes() + { + return $this->getAll(); + } + + public function setMirror($flag) + { + $this->mirror = (boolean)$flag; + } + + public function getMirror() + { + return $this->mirror; + } + + public function isMirror() + { + return $this->getMirror(); + } + + public function setDelete($flag) + { + $this->delete = (boolean)$flag; + } + + public function getDelete() + { + return $this->delete; + } + + public function isDelete() + { + return $this->getDelete(); + } + + public function setTags($flag) + { + $this->tags = $flag; + } + + public function getTags() + { + return $this->tags; + } + + public function isTags() + { + return $this->getTags(); + } + + public function setDestination($destination) + { + $this->destination = $destination; + } + + public function getDestination() + { + return $this->destination; + } + + public function setRefspec($spec) + { + $this->refspec = $spec; + } + + public function getRefspec() + { + return $this->refspec; + } + + public function setForce($flag) + { + $this->force = $flag; + } + + public function getForce() + { + return $this->force; + } + + public function isForce() + { + return $this->getForce(); + } + + public function setQuiet($flag) + { + $this->quiet = $flag; + } + + public function getQuiet() + { + return $this->quiet; + } + + public function isQuiet() + { + return $this->getQuiet(); + } + + + + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/git/GitTagTask.php b/buildscripts/phing/classes/phing/tasks/ext/git/GitTagTask.php new file mode 100644 index 00000000..864e71ba --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/git/GitTagTask.php @@ -0,0 +1,406 @@ +<?php +/* + * $Id: 127d4af12e159083935466773d1af788e46acd4e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +/** + * Wrapper around git-tag + * + * @author Evan Kaufman <evan@digitalflophouse.com> + * @author Victor Farazdagi <simple.square@gmail.com> + * @version $Id: 127d4af12e159083935466773d1af788e46acd4e $ + * @package phing.tasks.ext.git + * @see VersionControl_Git + * @since 2.4.5 + */ +class GitTagTask extends GitBaseTask +{ + /** + * Make unsigned, annotated tag object. See -a of git-tag + * @var boolean + */ + private $annotate = false; + + /** + * Make GPG-signed tag. See -s of git-tag + * @var boolean + */ + private $sign = false; + + /** + * Make GPG-signed tag, using given key. See -u of git-tag + * @var string + */ + private $keySign; + + /** + * Replace existing tag with given name. See -f of git-tag + * @var boolean + */ + private $replace = false; + + /** + * Delete existing tags with given names. See -d of git-tag + * @var boolean + */ + private $delete = false; + + /** + * Verify gpg signature of given tag names. See -v of git-tag + * @var boolean + */ + private $verify = false; + + /** + * List tags with names matching given pattern. See -l of git-tag + * @var boolean + */ + private $list = false; + + /** + * <num> specifies how many lines from the annotation, if any, are printed + * when using -l. See -n of git-tag + * @var int + */ + private $num; + + /** + * Only list tags containing specified commit. See --contains of git-tag + * @var string + */ + private $contains; + + /** + * Use given tag message. See -m of git-tag + * @var string + */ + private $message; + + /** + * Take tag message from given file. See -F of git-tag + * @var string + */ + private $file; + + /** + * <tagname> argument to git-tag + * @var string + */ + private $name; + + /** + * <commit> argument to git-tag + * @var string + */ + private $commit; + + /** + * <object> argument to git-tag + * @var string + */ + private $object; + + /** + * <pattern> argument to git-tag + * @var string + */ + private $pattern; + + /** + * Property name to set with output value from git-tag + * @var string + */ + private $outputProperty; + + /** + * The main entry point for the task + */ + public function main() + { + if (null === $this->getRepository()) { + throw new BuildException('"repository" is required parameter'); + } + + $client = $this->getGitClient(false, $this->getRepository()); + $command = $client->getCommand('tag'); + $command + ->setOption('a', $this->isAnnotate()) + ->setOption('s', $this->isSign()) + ->setOption('f', $this->isReplace()) + ->setOption('d', $this->isDelete()) + ->setOption('v', $this->isVerify()) + ->setOption('l', $this->isList()); + + if (null !== $this->getKeySign()) { + $command->setOption('u', $this->getKeySign()); + } + + if (null !== $this->getMessage()) { + $command->setOption('m', $this->getMessage()); + } + + if (null !== $this->getFile()) { + $command->setOption('F', $this->getFile()); + } + + // Use 'name' arg, if relevant + if (null != $this->getName() && false == $this->isList()) { + $command->addArgument($this->getName()); + } + + if (null !== $this->getKeySign() || $this->isAnnotate() || $this->isSign()) { + // Require a tag message or file + if (null === $this->getMessage() && null === $this->getFile()) { + throw new BuildException('"message" or "file" required to make a tag'); + } + } + + // Use 'commit' or 'object' args, if relevant + if (null !== $this->getCommit()) { + $command->addArgument($this->getCommit()); + } else if (null !== $this->getObject()) { + $command->addArgument($this->getObject()); + } + + // Customize list (-l) options + if ($this->isList()) { + if (null !== $this->getContains()) { + $command->setOption('contains', $this->getContains()); + } + if (null !== $this->getPattern()) { + $command->addArgument($this->getPattern()); + } + if (null != $this->getNum()) { + $command->setOption('n', $this->getNum()); + } + } + + $this->log('git-tag command: ' . $command->createCommandString(), Project::MSG_INFO); + + try { + $output = $command->execute(); + } catch (Exception $e) { + $this->log($e->getMessage(), Project::MSG_ERR); + throw new BuildException('Task execution failed. ' . $e->getMessage()); + } + + if (null !== $this->outputProperty) { + $this->project->setProperty($this->outputProperty, $output); + } + + $this->log( + sprintf('git-tag: tags for "%s" repository', $this->getRepository()), + Project::MSG_INFO); + $this->log('git-tag output: ' . trim($output), Project::MSG_INFO); + } + + public function setAnnotate($flag) + { + $this->annotate = (bool)$flag; + } + + public function getAnnotate() + { + return $this->annotate; + } + + public function isAnnotate() + { + return $this->getAnnotate(); + } + + public function setSign($flag) + { + $this->sign = (bool)$flag; + } + + public function getSign() + { + return $this->sign; + } + + public function isSign() + { + return $this->getSign(); + } + + public function setKeySign($keyId) + { + $this->keySign = $keyId; + } + + public function getKeySign() + { + return $this->keySign; + } + + public function setReplace($flag) + { + $this->replace = (bool)$flag; + } + + public function getReplace() + { + return $this->replace; + } + + public function isReplace() + { + return $this->getReplace(); + } + + public function setForce($flag) + { + return $this->setReplace($flag); + } + + public function setDelete($flag) + { + $this->delete = (bool)$flag; + } + + public function getDelete() + { + return $this->delete; + } + + public function isDelete() + { + return $this->getDelete(); + } + + public function setVerify($flag) + { + $this->verify = (bool)$flag; + } + + public function getVerify() + { + return $this->verify; + } + + public function isVerify() + { + return $this->getVerify(); + } + + public function setList($flag) + { + $this->list = (bool)$flag; + } + + public function getList() + { + return $this->list; + } + + public function isList() + { + return $this->getList(); + } + + public function setNum($num) + { + $this->num = (int)$num; + } + + public function getNum() + { + return $this->num; + } + + public function setContains($commit) + { + $this->contains = $commit; + } + + public function getContains() + { + return $this->contains; + } + + public function setMessage($msg) + { + $this->message = $msg; + } + + public function getMessage() + { + return $this->message; + } + + public function setFile($file) + { + $this->file = $file; + } + + public function getFile() + { + return $this->file; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setCommit($commit) + { + $this->commit = $commit; + } + + public function getCommit() + { + return $this->commit; + } + + public function setObject($object) + { + $this->object = $object; + } + + public function getObject() + { + return $this->object; + } + + public function setPattern($pattern) + { + $this->pattern = $pattern; + } + + public function getPattern() + { + return $this->pattern; + } + + public function setOutputProperty($prop) + { + $this->outputProperty = $prop; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php b/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php new file mode 100755 index 00000000..38ec99d4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php @@ -0,0 +1,43 @@ +<?php +/** + * $Id: dbbc1b4830ba43116d5b5e5d20c749598eaf62b7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Wrapper for comments for ionCube tasks + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: dbbc1b4830ba43116d5b5e5d20c749598eaf62b7 $ + * @package phing.tasks.ext.ioncube + * @since 2.2.0 + */ +class IoncubeComment +{ + private $value = ""; + + public function getValue() + { + return $this->value; + } + + public function addText($txt) + { + $this->value = trim($txt); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php b/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php new file mode 100755 index 00000000..5a31f355 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php @@ -0,0 +1,642 @@ +<?php +/** + * $Id: a6ce870b3d14be7f365468e3a272e5ac16128e93 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/ioncube/IoncubeComment.php'; + +/** + * Invokes the ionCube Encoder (PHP4 or PHP5) + * + * @author Michiel Rook <mrook@php.net> + * @author Andrew Eddie <andrew.eddie@jamboworks.com> + * @author Domenico Sgarbossa <sbraaaa@yahoo.it> + * @version $Id: a6ce870b3d14be7f365468e3a272e5ac16128e93 $ + * @package phing.tasks.ext.ioncube + * @since 2.2.0 + */ +class IoncubeEncoderTask extends Task +{ + private $ionSwitches = array(); + + private $ionOptions = array(); + + private $ionOptionsXS = array(); + + private $comments = array(); + + private $encoderName = 'ioncube_encoder'; + + private $fromDir = ''; + + private $ioncubePath = '/usr/local/ioncube'; + + private $phpVersion = '5'; + + private $targetOption = ''; + + private $toDir = ''; + + private $showCommandLine = false; + + /** + * Sets whether to show command line before it is executed + */ + function setShowCommandLine($value) + { + $this->showCommandLine = $value; + } + + /** + * Adds a comment to be used in encoded files + */ + function addComment(IoncubeComment $comment) + { + $this->comments[] = $comment; + } + + /** + * Sets the allowed server + */ + function setAllowedServer($value) + { + $this->ionOptionsXS['allowed-server'] = $value; + } + + /** + * Returns the allowed server setting + */ + function getAllowedServer() + { + return $this->ionOptionsXS['allowed-server']; + } + + /** + * Sets the binary option + */ + function setBinary($value) + { + $this->ionSwitches['binary'] = $value; + } + + /** + * Returns the binary option + */ + function getBinary() + { + return $this->ionSwitches['binary']; + } + + /** + * Sets files or folders to copy (separated by space) + */ + function setCopy($value) + { + $this->ionOptionsXS['copy'] = $value; + } + + /** + * Returns the copy setting + */ + function getCopy() + { + return $this->ionOptionsXS['copy']; + } + + /** + * Sets additional file patterns, files or directories to encode, + * or to reverse the effect of copy (separated by space) + */ + function setEncode($value) + { + $this->ionOptionsXS['encode'] = $value; + } + + /** + * Returns the encode setting + */ + function getEncode() + { + return $this->ionOptionsXS['encode']; + } + + /** + * Sets regexps of additional files to encrypt (separated by space) + */ + function setEncrypt($value) + { + $this->ionOptionsXS['encrypt'] = $value; + } + + /** + * Returns regexps of additional files to encrypt (separated by space) + */ + function getEncrypt() + { + return $this->ionOptionsXS['encrypt']; + } + + /** + * Sets a period after which the files expire + */ + function setExpirein($value) + { + $this->ionOptions['expire-in'] = $value; + } + + /** + * Returns the expireIn setting + */ + function getExpirein() + { + return $this->ionOptions['expire-in']; + } + + /** + * Sets a YYYY-MM-DD date to expire the files + */ + function setExpireon($value) + { + $this->ionOptions['expire-on'] = $value; + } + + /** + * Returns the expireOn setting + */ + function getExpireon() + { + return $this->ionOptions['expire-on']; + } + + /** + * Sets the source directory + */ + function setFromDir($value) + { + $this->fromDir = $value; + } + + /** + * Returns the source directory + */ + function getFromDir() + { + return $this->fromDir; + } + + /** + * Set files and directories to ignore entirely and exclude from the target directory + * (separated by space). + */ + function setIgnore($value) + { + $this->ionOptionsXS['ignore'] = $value; + } + + /** + * Returns the ignore setting + */ + function getIgnore() + { + return $this->ionOptionsXS['ignore']; + } + + /** + * Sets the path to the ionCube encoder + */ + function setIoncubePath($value) + { + $this->ioncubePath = $value; + } + + /** + * Returns the path to the ionCube encoder + */ + function getIoncubePath() + { + return $this->ioncubePath; + } + + /** + * Set files and directories not to be ignored (separated by space). + */ + function setKeep($value) + { + $this->ionOptionsXS['keep'] = $value; + } + + /** + * Returns the ignore setting + */ + function getKeep() + { + return $this->ionOptionsXS['keep']; + } + + /** + * Sets the path to the license file to use + */ + function setLicensePath($value) + { + $this->ionOptions['with-license'] = $value; + } + + /** + * Returns the path to the license file to use + */ + function getLicensePath() + { + return $this->ionOptions['with-license']; + } + + /** + * Sets the no-doc-comments option + */ + function setNoDocComments($value) + { + $this->ionSwitches['no-doc-comment'] = $value; + } + + /** + * Returns the no-doc-comments option + */ + function getNoDocComments() + { + return $this->ionSwitches['no-doc-comment']; + } + + /** + * Sets the obfuscate option + */ + function setObfuscate($value) + { + $this->ionOptionsXS['obfuscate'] = $value; + } + + /** + * Returns the optimize option + */ + function getObfuscate() + { + return $this->ionOptionsXS['obfuscate']; + } + + /** + * Sets the obfuscation key (required if using the obfuscate option) + */ + function setObfuscationKey($value) + { + $this->ionOptions['obfuscation-key'] = $value; + } + + /** + * Returns the optimize option + */ + function getObfuscationKey() + { + return $this->ionOptions['obfuscation-key']; + } + + /** + * Sets the optimize option + */ + function setOptimize($value) + { + $this->ionOptions['optimize'] = $value; + } + + /** + * Returns the optimize option + */ + function getOptimize() + { + return $this->ionOptions['optimize']; + } + + /** + * Sets the passphrase to use when encoding files + */ + function setPassPhrase($value) + { + $this->ionOptions['passphrase'] = $value; + } + + /** + * Returns the passphrase to use when encoding files + */ + function getPassPhrase() + { + return $this->ionOptions['passphrase']; + } + + /** + * Sets the version of PHP to use (defaults to 5) + */ + function setPhpVersion($value) + { + $this->phpVersion = $value; + } + + /** + * Returns the version of PHP to use (defaults to 5) + */ + function getPhpVersion() + { + return $this->phpVersion; + } + + /** + * Sets the target directory + */ + function setToDir($value) + { + $this->toDir = $value; + } + + /** + * Returns the target directory + */ + function getToDir() + { + return $this->toDir; + } + + /** + * Sets the without-runtime-loader-support option + */ + function setWithoutRuntimeLoaderSupport($value) + { + $this->ionSwitches['without-runtime-loader-support'] = $value; + } + + /** + * Returns the without-runtime-loader-support option + */ + function getWithoutRuntimeLoaderSupport() + { + return $this->ionSwitches['without-runtime-loader-support']; + } + + /** + * Sets the no-short-open-tags option + */ + function setNoShortOpenTags($value) + { + $this->ionSwitches['no-short-open-tags'] = $value; + } + + /** + * Returns the no-short-open-tags option + */ + function getNoShortOpenTags() + { + return $this->ionSwitches['no-short-open-tags']; + } + + /** + * Sets the ignore-deprecated-warnings option + */ + function setIgnoreDeprecatedWarnings($value) + { + $this->ionSwitches['ignore-deprecated-warnings'] = $value; + } + + /** + * Returns the ignore-deprecated-warnings option + */ + function getIgnoreDeprecatedWarnings() + { + return $this->ionSwitches['ignore-deprecated-warnings']; + } + + /** + * Sets the ignore-strict-warnings option + */ + function setIgnoreStrictWarnings($value) + { + $this->ionSwitches['ignore-strict-warnings'] = $value; + } + + /** + * Returns the ignore-strict-warnings option + */ + function getIgnoreStrictWarnings() + { + return $this->ionSwitches['ignore-strict-warnings']; + } + + /** + * Sets the allow-encoding-into-source option + */ + function setAllowEncodingIntoSource($value) + { + $this->ionSwitches['allow-encoding-into-source'] = $value; + } + + /** + * Returns the allow-encoding-into-source option + */ + function getAllowEncodingIntoSource() + { + return $this->ionSwitches['allow-encoding-into-source']; + } + + /** + * Sets the message-if-no-loader option + */ + function setMessageIfNoLoader($value) + { + $this->ionOptions['message-if-no-loader'] = $value; + } + + /** + * Returns the message-if-no-loader option + */ + function getMessageIfNoLoader() + { + return $this->ionOptions['message-if-no-loader']; + } + + /** + * Sets the action-if-no-loader option + */ + function setActionIfNoLoader($value) + { + $this->ionOptions['action-if-no-loader'] = $value; + } + + /** + * Returns the action-if-no-loader option + */ + function getActionIfNoLoader() + { + return $this->ionOptions['action-if-no-loader']; + } + + /** + * Sets the option to use when encoding target directory already exists (defaults to none) + */ + function setTargetOption($targetOption) + { + $this->targetOption = $targetOption; + } + + /** + * Returns he option to use when encoding target directory already exists (defaults to none) + */ + function getTargetOption() + { + return $this->targetOption; + } + + /** + * Sets the callback-file option + */ + function setCallbackFile($value) + { + $this->ionOptions['callback-file'] = $value; + } + + /** + * Returns the callback-file option + */ + function getCallbackFile() + { + return $this->ionOptions['callback-file']; + } + + /** + * Sets the obfuscation-exclusions-file option + */ + function setObfuscationExclusionFile($value) + { + $this->ionOptions['obfuscation-exclusion-file'] = $value; + } + + /** + * Returns the obfuscation-exclusions-file option + */ + function getObfuscationExclusionFile() + { + return $this->ionOptions['obfuscation-exclusion-file']; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $arguments = $this->constructArguments(); + + $encoder = new PhingFile($this->ioncubePath, $this->encoderName . ($this->phpVersion == 5 ? '5' : '')); + + $this->log("Running ionCube Encoder..."); + + if ($this->showCommandLine) + { + $this->log("Command line: ".$encoder->__toString() . ' ' . $arguments); + } + + exec($encoder->__toString() . ' ' . $arguments . " 2>&1", $output, $return); + + if ($return != 0) + { + throw new BuildException("Could not execute ionCube Encoder: " . implode(' ', $output)); + } + } + + /** + * Constructs an argument string for the ionCube encoder + */ + private function constructArguments() + { + $arguments = ''; + + foreach ($this->ionSwitches as $name => $value) + { + if ($value) + { + $arguments.= "--$name "; + } + } + + foreach ($this->ionOptions as $name => $value) + { + /** + * action-if-no-loader value is a php source snippet so it is + * better to handle it this way to prevent quote problems! + */ + if ($name == 'action-if-no-loader') + { + $arguments.= "--$name \"$value\" "; + } + else + { + $arguments.= "--$name '$value' "; + } + } + + foreach ($this->ionOptionsXS as $name => $value) + { + foreach (explode(' ', $value) as $arg) + { + $arguments.= "--$name '$arg' "; + } + } + + foreach ($this->comments as $comment) + { + $arguments.= "--add-comment '" . $comment->getValue() . "' "; + } + + if (!empty($this->targetOption)) + { + switch ($this->targetOption) + { + case "replace": + case "merge": + case "update": + case "rename": + { + $arguments.= "--" . $this->targetOption . "-target "; + } break; + + default: + { + throw new BuildException("Unknown target option '" . $this->targetOption . "'"); + } break; + } + } + + if ($this->fromDir != '') + { + $arguments .= $this->fromDir . ' '; + } + + if ($this->toDir != '') + { + $arguments .= "-o " . $this->toDir . ' '; + } + + return $arguments; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php b/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php new file mode 100755 index 00000000..6e7ab68a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php @@ -0,0 +1,208 @@ +<?php +/** + * $Id: 555e4853cd742e4ef733e3df4051c49cda527b73 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/ioncube/IoncubeComment.php'; + +/** + * Invokes the ionCube "make_license" program + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 555e4853cd742e4ef733e3df4051c49cda527b73 $ + * @package phing.tasks.ext.ioncube + * @since 2.2.0 + */ +class IoncubeLicenseTask extends Task +{ + private $ioncubePath = "/usr/local/ioncube"; + + private $licensePath = ""; + private $passPhrase = ""; + private $allowedServer = ""; + private $expireOn = ""; + private $expireIn = ""; + private $comments = array(); + + /** + * Sets the path to the ionCube encoder + */ + function setIoncubePath($ioncubePath) + { + $this->ioncubePath = $ioncubePath; + } + + /** + * Returns the path to the ionCube encoder + */ + function getIoncubePath() + { + return $this->ioncubePath; + } + + /** + * Sets the path to the license file to use + */ + function setLicensePath($licensePath) + { + $this->licensePath = $licensePath; + } + + /** + * Returns the path to the license file to use + */ + function getLicensePath() + { + return $this->licensePath; + } + + /** + * Sets the passphrase to use when encoding files + */ + function setPassPhrase($passPhrase) + { + $this->passPhrase = $passPhrase; + } + + /** + * Returns the passphrase to use when encoding files + */ + function getPassPhrase() + { + return $this->passPhrase; + } + + /** + * Adds a comment to be used in encoded files + */ + function addComment(IoncubeComment $comment) + { + $this->comments[] = $comment; + } + + /** + * Sets the --allowed-server option to use when generating the license + */ + function setAllowedServer($allowedServer) + { + $this->allowedServer = $allowedServer; + } + + /** + * Returns the --allowed-server option + */ + function getAllowedServer() + { + return $this->allowedServer; + } + + /** + * Sets the --expire-on option to use when generating the license + */ + function setExpireOn($expireOn) + { + $this->expireOn = $expireOn; + } + + /** + * Returns the --expire-on option + */ + function getExpireOn() + { + return $this->expireOn; + } + + /** + * Sets the --expire-in option to use when generating the license + */ + function setExpireIn($expireIn) + { + $this->expireIn = $expireIn; + } + + /** + * Returns the --expire-in option + */ + function getExpireIn() + { + return $this->expireIn; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $arguments = $this->constructArguments(); + + $makelicense = new PhingFile($this->ioncubePath, 'make_license'); + + $this->log("Running ionCube make_license..."); + + exec($makelicense->__toString() . " " . $arguments . " 2>&1", $output, $return); + + if ($return != 0) + { + throw new BuildException("Could not execute ionCube make_license: " . implode(' ', $output)); + } + } + + /** + * Constructs an argument string for the ionCube make_license + */ + private function constructArguments() + { + $arguments = ""; + + if (!empty($this->passPhrase)) + { + $arguments.= "--passphrase '" . $this->passPhrase . "' "; + } + + foreach ($this->comments as $comment) + { + $arguments.= "--header-line '" . $comment->getValue() . "' "; + } + + if (!empty($this->licensePath)) + { + $arguments.= "--o '" . $this->licensePath . "' "; + } + + if (!empty($this->allowedServer)) + { + $arguments.= "--allowed-server {" . $this->allowedServer . "} "; + } + + if (!empty($this->expireOn)) + { + $arguments.= "--expire-on " . $this->expireOn . " "; + } + + if (!empty($this->expireIn)) + { + $arguments.= "--expire-in " . $this->expireIn . " "; + } + + return $arguments; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMin.php b/buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMin.php new file mode 100644 index 00000000..44766fc1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMin.php @@ -0,0 +1,292 @@ +<?php
+/**
+ * jsmin.php - PHP implementation of Douglas Crockford's JSMin.
+ *
+ * This is pretty much a direct port of jsmin.c to PHP with just a few
+ * PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
+ * outputs to stdout, this library accepts a string as input and returns another
+ * string as output.
+ *
+ * PHP 5 or higher is required.
+ *
+ * Permission is hereby granted to use this version of the library under the
+ * same terms as jsmin.c, which has the following license:
+ *
+ * --
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * The Software shall be used for Good, not Evil.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * --
+ *
+ * @package JSMin
+ * @author Ryan Grove <ryan@wonko.com>
+ * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 1.1.1 (2008-03-02)
+ * @link http://code.google.com/p/jsmin-php/
+ */
+
+class JSMin {
+ const ORD_LF = 10;
+ const ORD_SPACE = 32;
+
+ protected $a = '';
+ protected $b = '';
+ protected $input = '';
+ protected $inputIndex = 0;
+ protected $inputLength = 0;
+ protected $lookAhead = null;
+ protected $output = '';
+
+ // -- Public Static Methods --------------------------------------------------
+
+ public static function minify($js) {
+ $jsmin = new JSMin($js);
+ return $jsmin->min();
+ }
+
+ // -- Public Instance Methods ------------------------------------------------
+
+ public function __construct($input) {
+ $this->input = str_replace("\r\n", "\n", $input);
+ $this->inputLength = strlen($this->input);
+ }
+
+ // -- Protected Instance Methods ---------------------------------------------
+
+ protected function action($d) {
+ switch($d) {
+ case 1:
+ $this->output .= $this->a;
+
+ case 2:
+ $this->a = $this->b;
+
+ if ($this->a === "'" || $this->a === '"') {
+ for (;;) {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+
+ if ($this->a === $this->b) {
+ break;
+ }
+
+ if (ord($this->a) <= self::ORD_LF) {
+ throw new JSMinException('Unterminated string literal.');
+ }
+
+ if ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ }
+ }
+ }
+
+ case 3:
+ $this->b = $this->next();
+
+ if ($this->b === '/' && (
+ $this->a === '(' || $this->a === ',' || $this->a === '=' ||
+ $this->a === ':' || $this->a === '[' || $this->a === '!' ||
+ $this->a === '&' || $this->a === '|' || $this->a === '?')) {
+
+ $this->output .= $this->a . $this->b;
+
+ for (;;) {
+ $this->a = $this->get();
+
+ if ($this->a === '/') {
+ break;
+ } elseif ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ } elseif (ord($this->a) <= self::ORD_LF) {
+ throw new JSMinException('Unterminated regular expression '.
+ 'literal.');
+ }
+
+ $this->output .= $this->a;
+ }
+
+ $this->b = $this->next();
+ }
+ }
+ }
+
+ protected function get() {
+ $c = $this->lookAhead;
+ $this->lookAhead = null;
+
+ if ($c === null) {
+ if ($this->inputIndex < $this->inputLength) {
+ $c = $this->input[$this->inputIndex];
+ $this->inputIndex += 1;
+ } else {
+ $c = null;
+ }
+ }
+
+ if ($c === "\r") {
+ return "\n";
+ }
+
+ if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
+ return $c;
+ }
+
+ return ' ';
+ }
+
+ protected function isAlphaNum($c) {
+ return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
+ }
+
+ protected function min() {
+ $this->a = "\n";
+ $this->action(3);
+
+ while ($this->a !== null) {
+ switch ($this->a) {
+ case ' ':
+ if ($this->isAlphaNum($this->b)) {
+ $this->action(1);
+ } else {
+ $this->action(2);
+ }
+ break;
+
+ case "\n":
+ switch ($this->b) {
+ case '{':
+ case '[':
+ case '(':
+ case '+':
+ case '-':
+ $this->action(1);
+ break;
+
+ case ' ':
+ $this->action(3);
+ break;
+
+ default:
+ if ($this->isAlphaNum($this->b)) {
+ $this->action(1);
+ }
+ else {
+ $this->action(2);
+ }
+ }
+ break;
+
+ default:
+ switch ($this->b) {
+ case ' ':
+ if ($this->isAlphaNum($this->a)) {
+ $this->action(1);
+ break;
+ }
+
+ $this->action(3);
+ break;
+
+ case "\n":
+ switch ($this->a) {
+ case '}':
+ case ']':
+ case ')':
+ case '+':
+ case '-':
+ case '"':
+ case "'":
+ $this->action(1);
+ break;
+
+ default:
+ if ($this->isAlphaNum($this->a)) {
+ $this->action(1);
+ }
+ else {
+ $this->action(3);
+ }
+ }
+ break;
+
+ default:
+ $this->action(1);
+ break;
+ }
+ }
+ }
+
+ return $this->output;
+ }
+
+ protected function next() {
+ $c = $this->get();
+
+ if ($c === '/') {
+ switch($this->peek()) {
+ case '/':
+ for (;;) {
+ $c = $this->get();
+
+ if (ord($c) <= self::ORD_LF) {
+ return $c;
+ }
+ }
+
+ case '*':
+ $this->get();
+
+ for (;;) {
+ switch($this->get()) {
+ case '*':
+ if ($this->peek() === '/') {
+ $this->get();
+ return ' ';
+ }
+ break;
+
+ case null:
+ throw new JSMinException('Unterminated comment.');
+ }
+ }
+
+ default:
+ return $c;
+ }
+ }
+
+ return $c;
+ }
+
+ protected function peek() {
+ $this->lookAhead = $this->get();
+ return $this->lookAhead;
+ }
+}
+
+/**
+ * @package JSMin
+ */
+class JSMinException extends Exception {}
diff --git a/buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php b/buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php new file mode 100644 index 00000000..26202942 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php @@ -0,0 +1,145 @@ +<?php
+/*
+ * $Id: 4a9a75fcd969cfc4e26a7f2c78836389e8be7864 $
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+require_once 'phing/Task.php';
+require_once 'phing/tasks/ext/jsmin/JsMin.php';
+
+/**
+ * Task to minify javascript files.
+ *
+ * Requires JSMin which can be found at http://code.google.com/p/jsmin-php/ but
+ * is bundled with Phing so no additional install of JsMin is required.
+ *
+ * @author Frank Kleine <mikey@stubbles.net>
+ * @version $Id: 4a9a75fcd969cfc4e26a7f2c78836389e8be7864 $
+ * @package phing.tasks.ext
+ * @since 2.3.0
+ */
+class JsMinTask extends Task
+{
+ /**
+ * the source files
+ *
+ * @var FileSet
+ */
+ protected $filesets = array();
+ /**
+ * Whether the build should fail, if
+ * errors occured
+ *
+ * @var boolean
+ */
+ protected $failonerror = false;
+
+ /**
+ * Define if the target should use or not a suffix -min
+ *
+ * @var boolean
+ */
+ protected $suffix = '-min';
+
+ /**
+ * directory to put minified javascript files into
+ *
+ * @var string
+ */
+ protected $targetDir;
+
+ /**
+ * Nested creator, adds a set of files (nested fileset attribute).
+ */
+ public function createFileSet()
+ {
+ $num = array_push($this->filesets, new FileSet());
+ return $this->filesets[$num - 1];
+ }
+
+ /**
+ * Whether the build should fail, if an error occured.
+ *
+ * @param boolean $value
+ */
+ public function setFailonerror($value)
+ {
+ $this->failonerror = $value;
+ }
+
+ /**
+ * Define if the task should or not use a suffix (-min is the default)
+ *
+ * @param string $value
+ */
+ public function setSuffix($value)
+ {
+ $this->suffix = $value;
+ }
+
+ /**
+ * sets the directory where minified javascript files should be put inot
+ *
+ * @param string $targetDir
+ */
+ public function setTargetDir($targetDir)
+ {
+ $this->targetDir = $targetDir;
+ }
+
+ /**
+ * The init method: Do init steps.
+ */
+ public function init()
+ {
+ return true;
+ }
+
+ /**
+ * The main entry point method.
+ */
+ public function main()
+ {
+ foreach ($this->filesets as $fs) {
+ try {
+ $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
+ $fullPath = realpath($fs->getDir($this->project));
+ foreach ($files as $file) {
+ $this->log('Minifying file ' . $file);
+ try {
+ $target = $this->targetDir . '/' . str_replace($fullPath, '', str_replace('.js', $this->suffix . '.js', $file));
+ if (file_exists(dirname($target)) === false) {
+ mkdir(dirname($target), 0700, true);
+ }
+
+ file_put_contents($target, JSMin::minify(file_get_contents($fullPath . '/' . $file)));
+ } catch (JSMinException $jsme) {
+ $this->log("Could not minify file $file: " . $jsme->getMessage(), Project::MSG_ERR);
+ }
+ }
+ } catch (BuildException $be) {
+ // directory doesn't exist or is not readable
+ if ($this->failonerror) {
+ throw $be;
+ } else {
+ $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
+ }
+ }
+ }
+ }
+}
diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php new file mode 100755 index 00000000..fbda3ecc --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php @@ -0,0 +1,184 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/system/ExecTask.php'; + +/** + * Abstract Liquibase task. Base class for all Liquibase Phing tasks. + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +abstract class AbstractLiquibaseTask extends Task +{ + protected $jar; + protected $changeLogFile; + protected $username; + protected $password; + protected $url; + protected $classpathref; + + + /** + * Sets the absolute path to liquibase jar. + * + * @param string the absolute path to the liquibase jar. + */ + public function setJar($jar) + { + $this->jar = $jar; + } + + + /** + * Sets the absolute path to the changelog file to use. + * + * @param string the absolute path to the changelog file + */ + public function setChangeLogFile($changelogFile) + { + $this->changeLogFile = $changelogFile; + } + + + /** + * Sets the username to connect to the database. + * + * @param string the username + */ + public function setUsername($username) + { + $this->username = $username; + } + + + /** + * Sets the password to connect to the database. + * + * @param string the password + */ + public function setPassword($password) + { + $this->password = $password; + } + + + /** + * Sets the url to connect to the database in jdbc style, e.g. + * <code> + * jdbc:postgresql://psqlhost/mydatabase + * </code> + * + * @param string jdbc connection string + */ + public function setUrl($url) + { + $this->url = $url; + } + + + /** + * Sets the Java classpathref. + * + * @param string A reference to the classpath that contains the database + * driver, liquibase.jar, and the changelog.xml file + */ + public function setclasspathref($classpathref) + { + $this->classpathref = $classpathref; + } + + + /** + * Ensure that correct parameters were passed in. + * + * @return void + */ + protected function checkParams() + { + if((null === $this->jar) or !file_exists($this->jar)) + { + throw new BuildException( + sprintf( + 'Specify the name of the LiquiBase.jar. "%s" does not exist!', + $this->jar + ) + ); + } + + if((null === $this->changeLogFile) or !file_exists($this->changeLogFile)) + { + throw new BuildException( + sprintf( + 'Specify the name of the Changelog file. "%s" does not exist!', + $this->changeLogFile + ) + ); + } + + if(null === $this->classpathref) + { + throw new BuildException('Please provide a classpath!'); + } + + if(null === $this->username) + { + throw new BuildException('Please provide a username for database acccess!'); + } + + if(null === $this->password) + { + throw new BuildException('Please provide a password for database acccess!'); + } + + if(null === $this->url) + { + throw new BuildException('Please provide a url for database acccess!'); + } + } + + + /** + * Executes the given command and returns the output. + * + * @param string the command to execute + * @param string additional parameters + * @return string the output of the executed command + */ + protected function execute($lbcommand, $lbparams = '') + { + $command = sprintf( + 'java -jar %s --changeLogFile=%s --url=%s --username=%s --password=%s --classpath=%s %s %s', + escapeshellarg($this->jar), + escapeshellarg($this->changeLogFile), + escapeshellarg($this->url), + escapeshellarg($this->username), + escapeshellarg($this->password), + escapeshellarg($this->classpathref), + escapeshellarg($lbcommand), + $lbparams + ); + + passthru($command); + + return; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php new file mode 100755 index 00000000..77fb97d2 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php @@ -0,0 +1,39 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php'; + +/** + * Task to create a changelog file. + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +class LiquibaseChangeLogTask extends AbstractLiquibaseTask +{ + /** + * @see Task::main() + */ + public function main() + { + $this->checkParams(); + $this->execute('generateChangeLog'); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php new file mode 100755 index 00000000..e79fae92 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php @@ -0,0 +1,86 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php'; + +/** + * Task to create a javadoc-like documentation based on current database and + * changelog. + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +class LiquibaseDbDocTask extends AbstractLiquibaseTask +{ + protected $outputDir; + + + /** + * Sets the output directory where the documentation gets generated to. + * + * @param string the output directory + */ + public function setOutputDir($outputDir) + { + $this->outputDir = $outputDir; + } + + + /** + * @see AbstractTask::checkParams() + */ + protected function checkParams() + { + parent::checkParams(); + + if((null === $this->outputDir) or !is_dir($this->outputDir)) + { + if(!mkdir($this->outputDir, 0777, true)) + { + throw new BuildException( + sprintf( + 'The directory "%s" does not exist and could not be created!', + $this->outputDir + ) + ); + } + } + + if(!is_writable($this->outputDir)) + { + throw new BuildException( + sprintf( + 'The directory "%s" is not writable!', + $this->outputDir + ) + ); + } + } + + + /** + * @see Task::main() + */ + public function main() + { + $this->checkParams(); + $this->execute('dbdoc', escapeshellarg($this->outputDir)); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php new file mode 100755 index 00000000..847f1401 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php @@ -0,0 +1,137 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php'; + +/** + * Task to create the diff between two databases. Will output the changes needed + * to convert the reference database to the database. + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +class LiquibaseDiffTask extends AbstractLiquibaseTask +{ + protected $referenceUsername; + protected $referencePassword; + protected $referenceUrl; + + + /** + * Sets the username to connect to the reference database. + * + * @param string the username + */ + public function setReferenceUsername($username) + { + $this->referenceUsername = $username; + } + + + /** + * Sets the password to connect to the refernce database. + * + * @param string the password + */ + public function setReferencePassword($password) + { + $this->referencePassword = $password; + } + + + /** + * Sets the url to connect to the reference database in jdbc style, e.g. + * <code> + * jdbc:postgresql://psqlhost/myrefdatabase + * </code> + * + * @param string jdbc connection string + */ + public function setReferenceUrl($url) + { + $this->referenceUrl = $url; + } + + + /** + * @see AbstractTask::checkParams() + */ + protected function checkParams() + { + parent::checkParams(); + + if(null === $this->referenceUsername) + { + throw new BuildException('Please provide a username for the reference database acccess!'); + } + + if(null === $this->referencePassword) + { + throw new BuildException('Please provide a password for the reference database acccess!'); + } + + if(null === $this->referenceUrl) + { + throw new BuildException('Please provide a url for the reference database acccess!'); + } + } + + + /** + * @see Task::main() + */ + public function main() + { + $this->checkParams(); + + $refparams = sprintf( + '--referenceUsername=%s --referencePassword=%s --referenceUrl=%s', + escapeshellarg($this->referenceUsername), + escapeshellarg($this->referencePassword), + escapeshellarg($this->referenceUrl) + ); + + // save main changelog file + $changelogFile = $this->changeLogFile; + + // set the name of the new generated changelog file + $this->setChangeLogFile(dirname($changelogFile).'/diffs/'.date('YmdHis').'.xml'); + if(!is_dir(dirname($changelogFile).'/diffs/')) + { + mkdir(dirname($changelogFile).'/diffs/', 0777, true); + } + $this->execute('diffChangeLog', $refparams); + + $xmlFile = new DOMDocument(); + $xmlFile->load($changelogFile); + + // create the new node + $rootNode = $xmlFile->getElementsByTagName('databaseChangeLog')->item(0); + $includeNode = $rootNode->appendChild($xmlFile->createElement('include')); + + // set the attributes for the new node + $includeNode->setAttribute('file', str_replace(dirname($changelogFile).'/', '', $this->changeLogFile)); + $includeNode->setAttribute('relativeToChangelogFile', 'true'); + file_put_contents($changelogFile, $xmlFile->saveXML()); + + $this->setChangeLogFile($changelogFile); + $this->execute('markNextChangeSetRan'); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php new file mode 100755 index 00000000..ec5584e6 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php @@ -0,0 +1,72 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php'; + +/** + * Rollbacks the database changes. + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +class LiquibaseRollbackTask extends AbstractLiquibaseTask +{ + protected $rollbackTag; + + + /** + * Sets the name of the tag to roll back to. + * + * @param string the name to roll back to + */ + public function setRollbackTag($rollbackTag) + { + $this->rollbackTag = $rollbackTag; + } + + + /** + * @see AbstractTask::checkParams() + */ + protected function checkParams() + { + parent::checkParams(); + + if(null === $this->rollbackTag) + { + throw new BuildException( + sprintf( + 'Please specify the tag to rollback to!', + $this->rollbackTag + ) + ); + } + } + + + /** + * @see Task::main() + */ + public function main() + { + $this->checkParams(); + $this->execute('rollback', escapeshellarg($this->rollbackTag)); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.php new file mode 100755 index 00000000..07b0e72e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.php @@ -0,0 +1,75 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php'; + +/** + * Task to tag the current database state. In case you tag the database multiple + * times without applying a new changelog before, the tags will overwrite each + * other! + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +class LiquibaseTagTask extends AbstractLiquibaseTask +{ + protected $tag; + + + /** + * Sets the name of tag which is used to mark the database state for + * possible future rollback. + * + * @param string the name to tag the database with + */ + public function setTag($tag) + { + $this->tag = $tag; + } + + + /** + * @see AbstractTask::checkParams() + */ + protected function checkParams() + { + parent::checkParams(); + + if(null === $this->tag) + { + throw new BuildException( + sprintf( + 'Please specify the tag!', + $this->tag + ) + ); + } + } + + + /** + * @see Task::main() + */ + public function main() + { + $this->checkParams(); + $this->execute('tag', escapeshellarg($this->tag)); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.php b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.php new file mode 100755 index 00000000..35162e4d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.php @@ -0,0 +1,39 @@ +<?php + +/** + * Copyright (c) 2007-2011 bitExpert AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php'; + +/** + * Task to update the database to latest version of the changelog file. + * + * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de> + * @version $Id$ + * @since 2.4.10 + * @package phing.tasks.ext.liquibase + */ +class LiquibaseUpdateTask extends AbstractLiquibaseTask +{ + /** + * @see Task::main() + */ + public function main() + { + $this->checkParams(); + $this->execute('update'); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php b/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php new file mode 100644 index 00000000..53286f46 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php @@ -0,0 +1,109 @@ +<?php +/** + * $Id: f3a492fa25b203d3263e3463c1ab522c61bd0a9c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; + +/** + * Analyzer element for the PhpDependTask + * + * @package phing.tasks.ext.pdepend + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: f3a492fa25b203d3263e3463c1ab522c61bd0a9c $ + * @since 2.4.1 + */ +class PhpDependAnalyzerElement +{ + /** + * The type of the analyzer + * + * @var string + */ + protected $_type = ''; + + /** + * The value(s) for the analyzer option + * + * @var array + */ + protected $_value = array(); + + /** + * Sets the analyzer type + * + * @param string $type Type of the analyzer + * + * @return void + */ + public function setType($type) + { + $this->_type = $type; + + switch ($this->_type) { + case 'coderank-mode': + break; + + default: + throw new BuildException( + "Analyzer '" . $this->_type . "' not implemented" + ); + } + } + + /** + * Get the analyzer type + * + * @return string + */ + public function getType() + { + return $this->_type; + } + + /** + * Sets the value for the analyzer + * + * @param string $value Value for the analyzer + * + * @return void + */ + public function setValue($value) + { + $this->_value = array(); + + $token = ' ,;'; + $values = strtok($value, $token); + + while ($values !== false) { + $this->_value[] = $values; + $values = strtok($token); + } + } + + /** + * Get the analyzer value + * + * @return string + */ + public function getValue() + { + return $this->_value; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php b/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php new file mode 100644 index 00000000..619f7377 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php @@ -0,0 +1,105 @@ +<?php +/** + * $Id: 6aa728f12c6a9b89fb93cfd39908918937a6d5f9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; + +/** + * Logger element for the PhpDependTask. + * + * @package phing.tasks.ext.pdepend + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 6aa728f12c6a9b89fb93cfd39908918937a6d5f9 $ + * @since 2.4.1 + */ +class PhpDependLoggerElement +{ + /** + * The type of the logger. + * + * @var string + */ + protected $_type = ''; + + /** + * Output file for logger. + * + * @var PhingFile + */ + protected $_outfile = null; + + /** + * Sets the logger type. + * + * @param string $type Type of the logger + * + * @return void + */ + public function setType($type) + { + $this->_type = $type; + + switch ($this->_type) { + case 'jdepend-chart': + case 'jdepend-xml': + case 'overview-pyramid': + case 'phpunit-xml': + case 'summary-xml': + break; + + default: + throw new BuildException( + "Logger '" . $this->_type . "' not implemented" + ); + } + } + + /** + * Get the logger type + * + * @return string + */ + public function getType() + { + return $this->_type; + } + + /** + * Sets the output file for the logger results. + * + * @param PhingFile $outfile The output file + * + * @return void + */ + public function setOutfile(PhingFile $outfile) + { + $this->_outfile = $outfile; + } + + /** + * Get the output file. + * + * @return PhingFile + */ + public function getOutfile() + { + return $this->_outfile; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php b/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php new file mode 100644 index 00000000..c42575b7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php @@ -0,0 +1,506 @@ +<?php +/** + * $Id: 572bbfe2e542b864211a85de9990f5cbfe31a4cd $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Runs the PHP_Depend software analyzer and metric tool. + * Performs static code analysis on a given source base. + * + * @package phing.tasks.ext.pdepend + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 572bbfe2e542b864211a85de9990f5cbfe31a4cd $ + * @since 2.4.1 + */ +class PhpDependTask extends Task +{ + /** + * A php source code filename or directory + * + * @var PhingFile + */ + protected $_file = null; + + /** + * All fileset objects assigned to this task + * + * @var array<FileSet> + */ + protected $_filesets = array(); + + /** + * List of allowed file extensions. Default file extensions are <b>php</b> + * and <p>php5</b>. + * + * @var array<string> + */ + protected $_allowedFileExtensions = array('php', 'php5'); + + /** + * List of exclude directories. Default exclude dirs are <b>.git</b>, + * <b>.svn</b> and <b>CVS</b>. + * + * @var array<string> + */ + protected $_excludeDirectories = array('.git', '.svn', 'CVS'); + + /** + * List of exclude packages + * + * @var array<string> + */ + protected $_excludePackages = array(); + + /** + * Should the parse ignore doc comment annotations? + * + * @var boolean + */ + protected $_withoutAnnotations = false; + + /** + * Should PHP_Depend treat <b>+global</b> as a regular project package? + * + * @var boolean + */ + protected $_supportBadDocumentation = false; + + /** + * Flag for enable/disable debugging + * + * @var boolean + */ + protected $_debug = false; + + /** + * PHP_Depend configuration file + * + * @var PhingFile + */ + protected $_configFile = null; + + /** + * Logger elements + * + * @var array<PhpDependLoggerElement> + */ + protected $_loggers = array(); + + /** + * Analyzer elements + * + * @var array<PhpDependAnalyzerElement> + */ + protected $_analyzers = array(); + + /** + * Holds the PHP_Depend runner instance + * + * @var PHP_Depend_TextUI_Runner + */ + protected $_runner = null; + + /** + * Flag that determines whether to halt on error + * + * @var boolean + */ + protected $_haltonerror = false; + + /** + * Load the necessary environment for running PHP_Depend + * + * @return void + * @throws BuildException + */ + public function init() + { + /** + * Determine PHP_Depend installation + */ + @include_once 'PHP/Depend/TextUI/Runner.php'; + + if (! class_exists('PHP_Depend_TextUI_Runner')) { + throw new BuildException( + 'PhpDependTask depends on PHP_Depend being installed ' + . 'and on include_path', + $this->getLocation() + ); + } + + /** + * Other dependencies that should only be loaded + * when class is actually used + */ + require_once 'phing/tasks/ext/pdepend/PhpDependLoggerElement.php'; + require_once 'phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php'; + require_once 'PHP/Depend/Autoload.php'; + } + + /** + * Set the input source file or directory + * + * @param PhingFile $file The input source file or directory + * + * @return void + */ + public function setFile(PhingFile $file) + { + $this->_file = $file; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute) + * + * @return FileSet The created fileset object + */ + public function createFileSet() + { + $num = array_push($this->_filesets, new FileSet()); + return $this->_filesets[$num-1]; + } + + /** + * Sets a list of filename extensions for valid php source code files + * + * @param string $fileExtensions List of valid file extensions + * + * @return void + */ + public function setAllowedFileExtensions($fileExtensions) + { + $this->_allowedFileExtensions = array(); + + $token = ' ,;'; + $ext = strtok($fileExtensions, $token); + + while ($ext !== false) { + $this->_allowedFileExtensions[] = $ext; + $ext = strtok($token); + } + } + + /** + * Sets a list of exclude directories + * + * @param string $excludeDirectories List of exclude directories + * + * @return void + */ + public function setExcludeDirectories($excludeDirectories) + { + $this->_excludeDirectories = array(); + + $token = ' ,;'; + $pattern = strtok($excludeDirectories, $token); + + while ($pattern !== false) { + $this->_excludeDirectories[] = $pattern; + $pattern = strtok($token); + } + } + + /** + * Sets a list of exclude packages + * + * @param string $excludePackages Exclude packages + * + * @return void + */ + public function setExcludePackages($excludePackages) + { + $this->_excludePackages = array(); + + $token = ' ,;'; + $pattern = strtok($excludePackages, $token); + + while ($pattern !== false) { + $this->_excludePackages[] = $pattern; + $pattern = strtok($token); + } + } + + /** + * Should the parser ignore doc comment annotations? + * + * @param boolean $withoutAnnotations + * + * @return void + */ + public function setWithoutAnnotations($withoutAnnotations) + { + $this->_withoutAnnotations = StringHelper::booleanValue( + $withoutAnnotations + ); + } + + /** + * Should PHP_Depend support projects with a bad documentation. If this + * option is set to <b>true</b>, PHP_Depend will treat the default package + * <b>+global</b> as a regular project package. + * + * @param boolean $supportBadDocumentation + * + * @return void + */ + public function setSupportBadDocumentation($supportBadDocumentation) + { + $this->_supportBadDocumentation = StringHelper::booleanValue( + $supportBadDocumentation + ); + } + + /** + * Set debugging On/Off + * + * @param boolean $debug + * + * @return void + */ + public function setDebug($debug) + { + $this->_debug = StringHelper::booleanValue($debug); + } + + /** + * Set halt on error + * + * @param boolean $haltonerror + * + * @return void + */ + public function setHaltonerror($haltonerror) + { + $this->_haltonerror = StringHelper::booleanValue($haltonerror); + } + + /** + * Set the configuration file + * + * @param PhingFile $configFile The configuration file + * + * @return void + */ + public function setConfigFile(PhingFile $configFile) + { + $this->_configFile = $configFile; + } + + /** + * Create object for nested logger element + * + * @return PhpDependLoggerElement + */ + public function createLogger() + { + $num = array_push($this->_loggers, new PhpDependLoggerElement()); + return $this->_loggers[$num-1]; + } + + /** + * Create object for nested analyzer element + * + * @return PhpDependAnalyzerElement + */ + public function createAnalyzer() + { + $num = array_push($this->_analyzers, new PhpDependAnalyzerElement()); + return $this->_analyzers[$num-1]; + } + + /** + * Executes PHP_Depend_TextUI_Runner against PhingFile or a FileSet + * + * @return void + * @throws BuildException + */ + public function main() + { + $autoload = new PHP_Depend_Autoload(); + $autoload->register(); + + if (!isset($this->_file) and count($this->_filesets) == 0) { + throw new BuildException( + "Missing either a nested fileset or attribute 'file' set" + ); + } + + if (count($this->_loggers) == 0) { + throw new BuildException("Missing nested 'logger' element"); + } + + $this->validateLoggers(); + $this->validateAnalyzers(); + + $filesToParse = array(); + + if ($this->_file instanceof PhingFile) { + $filesToParse[] = $this->_file->__toString(); + } else { + // append any files in filesets + foreach ($this->_filesets as $fs) { + $files = $fs->getDirectoryScanner($this->project) + ->getIncludedFiles(); + + foreach ($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $filesToParse[] = $f->getAbsolutePath(); + } + } + } + + $this->_runner = new PHP_Depend_TextUI_Runner(); + $this->_runner->addProcessListener(new PHP_Depend_TextUI_ResultPrinter()); + + $configurationFactory = new PHP_Depend_Util_Configuration_Factory(); + $configuration = $configurationFactory->createDefault(); + $this->_runner->setConfiguration($configuration); + + $this->_runner->setSourceArguments($filesToParse); + + foreach ($this->_loggers as $logger) { + // Register logger + $this->_runner->addLogger( + $logger->getType(), + $logger->getOutfile()->__toString() + ); + } + + foreach ($this->_analyzers as $analyzer) { + // Register additional analyzer + $this->_runner->addOption( + $analyzer->getType(), + $analyzer->getValue() + ); + } + + // Disable annotation parsing + if ($this->_withoutAnnotations) { + $this->_runner->setWithoutAnnotations(); + } + + // Enable bad documentation support + if ($this->_supportBadDocumentation) { + $this->_runner->setSupportBadDocumentation(); + } + + // Check for suffix + if (count($this->_allowedFileExtensions) > 0) { + $this->_runner->setFileExtensions($this->_allowedFileExtensions); + } + + // Check for ignore directories + if (count($this->_excludeDirectories) > 0) { + $this->_runner->setExcludeDirectories($this->_excludeDirectories); + } + + // Check for exclude packages + if (count($this->_excludePackages) > 0) { + $this->_runner->setExcludePackages($this->_excludePackages); + } + + // Check for configuration option + if ($this->_configFile instanceof PhingFile) { + if (file_exists($this->_configFile->__toString()) === false) { + throw new BuildException( + 'The configuration file "' + . $this->_configFile->__toString() . '" doesn\'t exist.' + ); + } + + // Load configuration file + $config = new PHP_Depend_Util_Configuration( + $this->_configFile->__toString(), + null, + true + ); + + // Store in config registry + PHP_Depend_Util_ConfigurationInstance::set($config); + } + + if ($this->_debug) { + require_once 'PHP/Depend/Util/Log.php'; + // Enable debug logging + PHP_Depend_Util_Log::setSeverity(PHP_Depend_Util_Log::DEBUG); + } + + $this->_runner->run(); + + if ($this->_runner->hasParseErrors() === true) { + $this->log('Following errors occurred:'); + + foreach ($this->_runner->getParseErrors() as $error) { + $this->log($error); + } + + if ($this->_haltonerror === true) { + throw new BuildException('Errors occurred during parse process'); + } + } + } + + /** + * Validates the available loggers + * + * @return void + * @throws BuildException + */ + protected function validateLoggers() + { + foreach ($this->_loggers as $logger) { + if ($logger->getType() === '') { + throw new BuildException( + "Logger missing required 'type' attribute" + ); + } + + if ($logger->getOutfile() === null) { + throw new BuildException( + "Logger requires 'outfile' attribute" + ); + } + } + } + + /** + * Validates the available analyzers + * + * @return void + * @throws BuildException + */ + protected function validateAnalyzers() + { + foreach ($this->_analyzers as $analyzer) { + if ($analyzer->getType() === '') { + throw new BuildException( + "Analyzer missing required 'type' attribute" + ); + } + + if (count($analyzer->getValue()) === 0) { + throw new BuildException( + "Analyzer missing required 'value' attribute" + ); + } + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php new file mode 100755 index 00000000..41f296f7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php @@ -0,0 +1,151 @@ +<?php + +/** + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + * + * @version SVN: $Id: dacfd46938db28930888f57a6924be642ec1b3d7 $ + * @package phing.tasks.ext.pdo + */ + +require_once 'phing/tasks/ext/pdo/PDOQuerySplitter.php'; + +/** + * Splits SQL source into queries using simple regular expressions + * + * Extracted from PDOSQLExecTask::runStatements() + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Alexey Borzov <avb@php.net> + * @package phing.tasks.ext.pdo + * @version $Id$ + */ +class DefaultPDOQuerySplitter extends PDOQuerySplitter +{ + /** + * Delimiter type, one of PDOSQLExecTask::DELIM_ROW or PDOSQLExecTask::DELIM_NORMAL + * @var string + */ + private $delimiterType; + + /** + * Leftover SQL from previous line + * @var string + */ + private $sqlBacklog = ''; + + /** + * Constructor, sets the parent task, reader with SQL source and delimiter type + * + * @param PDOSQLExecTask $parent + * @param Reader $reader + * @param string $delimiterType + */ + public function __construct(PDOSQLExecTask $parent, Reader $reader, $delimiterType = PDOSQLExecTask::DELIM_NORMAL) + { + parent::__construct($parent, $reader); + $this->delimiterType = $delimiterType; + } + + /** + * Returns next query from SQL source, null if no more queries left + * + * In case of "row" delimiter type this searches for strings containing only + * delimiters. In case of "normal" delimiter type, this uses simple regular + * expression logic to search for delimiters. + * + * @return string|null + */ + public function nextQuery() + { + $sql = ""; + $hasQuery = false; + + while (($line = $this->sqlReader->readLine()) !== null) { + $delimiter = $this->parent->getDelimiter(); + $project = $this->parent->getOwningTarget()->getProject(); + $line = ProjectConfigurator::replaceProperties( + $project, trim($line), $project->getProperties() + ); + + if (($line != $delimiter) && ( + StringHelper::startsWith("//", $line) || + StringHelper::startsWith("--", $line) || + StringHelper::startsWith("#", $line))) { + continue; + } + + if (strlen($line) > 4 + && strtoupper(substr($line,0, 4)) == "REM ") { + continue; + } + + // MySQL supports defining new delimiters + if (preg_match('/DELIMITER [\'"]?([^\'" $]+)[\'"]?/i', $line, $matches)) { + $this->parent->setDelimiter($matches[1]); + continue; + } + + if ($this->sqlBacklog !== "") { + $sql = $this->sqlBacklog; + $this->sqlBacklog = ""; + } + + $sql .= " " . $line . "\n"; + + // SQL defines "--" as a comment to EOL + // and in Oracle it may contain a hint + // so we cannot just remove it, instead we must end it + if (strpos($line, "--") !== false) { + $sql .= "\n"; + } + + // DELIM_ROW doesn't need this (as far as i can tell) + if ($this->delimiterType == PDOSQLExecTask::DELIM_NORMAL) { + + $reg = "#((?:\"(?:\\\\.|[^\"])*\"?)+|'(?:\\\\.|[^'])*'?|" . preg_quote($delimiter) . ")#"; + + $sqlParts = preg_split($reg, $sql, 0, PREG_SPLIT_DELIM_CAPTURE); + $this->sqlBacklog = ""; + foreach ($sqlParts as $sqlPart) { + // we always want to append, even if it's a delim (which will be stripped off later) + $this->sqlBacklog .= $sqlPart; + + // we found a single (not enclosed by ' or ") delimiter, so we can use all stuff before the delim as the actual query + if ($sqlPart === $delimiter) { + $sql = $this->sqlBacklog; + $this->sqlBacklog = ""; + $hasQuery = true; + } + } + } + + if ($hasQuery || ($this->delimiterType == PDOSQLExecTask::DELIM_ROW && $line == $delimiter)) { + // this assumes there is always a delimter on the end of the SQL statement. + $sql = StringHelper::substring($sql, 0, strlen($sql) - strlen($delimiter) + - ($this->delimiterType == PDOSQLExecTask::DELIM_ROW ? 2 : 1)); + return $sql; + } + } + + // Catch any statements not followed by ; + if ($sql !== "") { + return $sql; + } + + return null; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php new file mode 100755 index 00000000..60a9d436 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php @@ -0,0 +1,63 @@ +<?php + +/** + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + * + * @version $Id: e84ada3cdb7a04da60158c7a352fbb06f17f2ca7 $ + * @package phing.tasks.ext.pdo + */ + +/** + * Base class for classes that split SQL source into separate queries + * + * @author Alexey Borzov <avb@php.net> + * @package phing.tasks.ext.pdo + * @version $Id$ + */ +abstract class PDOQuerySplitter +{ + /** + * Task that uses the splitter + * @var PDOSQLExecTask + */ + protected $parent; + + /** + * Reader with SQL source + * @var BufferedReader + */ + protected $sqlReader; + + /** + * Constructor, sets the parent task and reader with SQL source + * + * @param PDOSQLExecTask $parent + * @param Reader $reader + */ + public function __construct(PDOSQLExecTask $parent, Reader $reader) + { + $this->parent = $parent; + $this->sqlReader = new BufferedReader($reader); + } + + /** + * Returns next query from SQL source, null if no more queries left + * + * @return string|null + */ + abstract public function nextQuery(); +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php new file mode 100644 index 00000000..82a90ca4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php @@ -0,0 +1,84 @@ +<?php +/** + * $Id: a3237522d22494bbfaf0521ebb8f091d448f3614 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; + +/** + * Abstract + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext.pdo + * @since 2.3.0 + */ +abstract class PDOResultFormatter +{ + /** + * Output writer. + * + * @var Writer + */ + protected $out; + + /** + * Sets the output writer. + * + * @param Writer $out + */ + public function setOutput(Writer $out) { + $this->out = $out; + } + + /** + * Gets the output writer. + * + * @return Writer + */ + public function getOutput() { + return $this->out; + } + + /** + * Gets the preferred output filename for this formatter. + * @return string + */ + abstract public function getPreferredOutfile(); + + /** + * Perform any initialization. + */ + public function initialize() { + + } + + /** + * Processes a specific row from PDO result set. + * + * @param array $row Row of PDO result set. + */ + abstract public function processRow($row); + + /** + * Perform any final tasks and Close the writer. + */ + public function close() { + $this->out->close(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php new file mode 100644 index 00000000..bc657604 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php @@ -0,0 +1,313 @@ +<?php +/** + * $Id: a3ca52c2b277a8cbc0d2802b75f2bea18701b636 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/tasks/ext/pdo/PlainPDOResultFormatter.php'; +require_once 'phing/tasks/ext/pdo/XMLPDOResultFormatter.php'; +require_once 'phing/util/LogWriter.php'; + +/** + * A class to represent the nested <formatter> element for PDO SQL results. + * + * This class is inspired by the similarly-named class in the PHPUnit tasks. + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext.pdo + * @since 2.3.0 + */ +class PDOSQLExecFormatterElement +{ + /** + * @var PDOResultFormatter + */ + private $formatter; + + /** + * The type of the formatter (used for built-in formatter classes). + * @var string + */ + private $type = ""; + + /** + * Whether to use file (or write output to phing log). + * @var boolean + */ + private $useFile = true; + + /** + * Output file for formatter. + * @var PhingFile + */ + private $outfile; + + /** + * Print header columns. + * @var boolean + */ + private $showheaders = true; + + /** + * Whether to format XML output. + * @var boolean + */ + private $formatoutput = true; + + /** + * Encoding for XML output. + * @var string + */ + private $encoding; + + /** + * Column delimiter. + * Defaults to ',' + * @var string + */ + private $coldelimiter = ","; + + /** + * Row delimiter. + * Defaults to PHP_EOL. + * @var string + */ + private $rowdelimiter = PHP_EOL; + + /** + * Append to an existing file or overwrite it? + * @var boolean + */ + private $append = false; + + /** + * Parameters for a custom formatter. + * @var array Parameter[] + */ + private $formatterParams = array(); + + /** + * @var PDOSQLExecTask + */ + private $parentTask; + + /** + * Construct a new PDOSQLExecFormatterElement with parent task. + * @param PDOSQLExecTask $parentTask + */ + public function __construct(PDOSQLExecTask $parentTask) + { + $this->parentTask = $parentTask; + } + + /** + * Supports nested <param> element (for custom formatter classes). + * @return Parameter + */ + public function createParam() { + $num = array_push($this->parameters, new Parameter()); + return $this->parameters[$num-1]; + } + + /** + * Gets a configured output writer. + * @return Writer + */ + private function getOutputWriter() + { + if ($this->useFile) { + $of = $this->getOutfile(); + if (!$of) { + $of = new PhingFile($this->formatter->getPreferredOutfile()); + } + return new FileWriter($of, $this->append); + } else { + return $this->getDefaultOutput(); + } + } + + /** + * Configures wrapped formatter class with any attributes on this element. + */ + public function prepare() { + + if (!$this->formatter) { + throw new BuildException("No formatter specified (use type or classname attribute)", $this->getLocation()); + } + + $out = $this->getOutputWriter(); + + $this->parentTask->log("Setting output writer to: " . get_class($out), Project::MSG_VERBOSE); + $this->formatter->setOutput($out); + + if ($this->formatter instanceof PlainPDOResultFormatter) { + // set any options that apply to the plain formatter + $this->formatter->setShowheaders($this->showheaders); + $this->formatter->setRowdelim($this->rowdelimiter); + $this->formatter->setColdelim($this->coldelimiter); + } elseif ($this->formatter instanceof XMLPDOResultFormatter) { + // set any options that apply to the xml formatter + $this->formatter->setEncoding($this->encoding); + $this->formatter->setFormatOutput($this->formatoutput); + } + + foreach($this->formatterParams as $param) { + $param = new Parameter(); + $method = 'set' . $param->getName(); + if (!method_exists($this->formatter, $param->getName())) { + throw new BuildException("Formatter " . get_class($this->formatter) . " does not have a $method method.", $this->getLocation()); + } + call_user_func(array($this->formatter, $method), $param->getValue()); + } + } + + /** + * Sets the formatter type. + * @param string $type + */ + function setType($type) { + $this->type = $type; + if ($this->type == "xml") { + $this->formatter = new XMLPDOResultFormatter(); + } elseif ($this->type == "plain") { + $this->formatter = new PlainPDOResultFormatter(); + } else { + throw new BuildException("Formatter '" . $this->type . "' not implemented"); + } + } + + /** + * Set classname for a custom formatter (must extend PDOResultFormatter). + * @param string $className + */ + function setClassName($className) { + $classNameNoDot = Phing::import($className); + $this->formatter = new $classNameNoDot(); + } + + /** + * Set whether to write formatter results to file. + * @param boolean $useFile + */ + function setUseFile($useFile) { + $this->useFile = (boolean) $useFile; + } + + /** + * Return whether to write formatter results to file. + * @return boolean + */ + function getUseFile() { + return $this->useFile; + } + + /** + * Sets the output file for the formatter results. + * @param PhingFile $outFile + */ + function setOutfile(PhingFile $outfile) { + $this->outfile = $outfile; + } + + /** + * Get the output file. + * @return PhingFile + */ + function getOutfile() { + return $this->outfile; + /* + } else { + return new PhingFile($this->formatter->getPreferredOutfile()); + }*/ + } + + /** + * whether output should be appended to or overwrite + * an existing file. Defaults to false. + * @param boolean $append + */ + public function setAppend($append) { + $this->append = (boolean) $append; + } + + /** + * Whether output should be appended to file. + * @return boolean + */ + public function getAppend() { + return $this->append; + } + + /** + * Print headers for result sets from the + * statements; optional, default true. + * @param boolean $showheaders + */ + public function setShowheaders($showheaders) { + $this->showheaders = (boolean) $showheaders; + } + + /** + * Sets the column delimiter. + * @param string $v + */ + public function setColdelim($v) { + $this->coldelimiter = $v; + } + + /** + * Sets the row delimiter. + * @param string $v + */ + public function setRowdelim($v) { + $this->rowdelimiter = $v; + } + + /** + * Set the DOM document encoding. + * @param string $v + */ + public function setEncoding($v) { + $this->encoding = $v; + } + + /** + * @param boolean $v + */ + public function setFormatOutput($v) { + $this->formatOutput = (boolean) $v; + } + + /** + * Gets a default output writer for this task. + * @return Writer + */ + private function getDefaultOutput() + { + return new LogWriter($this->parentTask); + } + + /** + * Gets the formatter that has been configured based on this element. + * @return PDOResultFormatter + */ + function getFormatter() { + return $this->formatter; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php new file mode 100755 index 00000000..3837d7ff --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php @@ -0,0 +1,606 @@ +<?php +/* + * $Id: 8b5a8e4f80b46f8a797b058dbb9a240a1185c12b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/pdo/PDOTask.php'; +include_once 'phing/system/io/StringReader.php'; +include_once 'phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php'; + +/** + * Executes a series of SQL statements on a database using PDO. + * + * <p>Statements can + * either be read in from a text file using the <i>src</i> attribute or from + * between the enclosing SQL tags.</p> + * + * <p>Multiple statements can be provided, separated by semicolons (or the + * defined <i>delimiter</i>). Individual lines within the statements can be + * commented using either --, // or REM at the start of the line.</p> + * + * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be + * turned on or off whilst executing the statements. If auto-commit is turned + * on each statement will be executed and committed. If it is turned off the + * statements will all be executed as one transaction.</p> + * + * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs + * during the execution of one of the statements. + * The possible values are: <b>continue</b> execution, only show the error; + * <b>stop</b> execution and commit transaction; + * and <b>abort</b> execution and transaction and fail task.</p> + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Jeff Martin <jeff@custommonkey.org> (Ant) + * @author Michael McCallum <gholam@xtra.co.nz> (Ant) + * @author Tim Stephenson <tim.stephenson@sybase.com> (Ant) + * @package phing.tasks.ext.pdo + * @version $Id: 8b5a8e4f80b46f8a797b058dbb9a240a1185c12b $ + */ +class PDOSQLExecTask extends PDOTask { + + /** + * Count of how many statements were executed successfully. + * @var int + */ + private $goodSql = 0; + + /** + * Count of total number of SQL statements. + * @var int + */ + private $totalSql = 0; + + const DELIM_ROW = "row"; + const DELIM_NORMAL = "normal"; + + /** + * Database connection + * @var PDO + */ + private $conn = null; + + /** + * Files to load + * @var array FileSet[] + */ + private $filesets = array(); + + /** + * Files to load + * @var array FileList[] + */ + private $filelists = array(); + + /** + * Formatter elements. + * @var array PDOSQLExecFormatterElement[] + */ + private $formatters = array(); + + /** + * SQL statement + * @var PDOStatement + */ + private $statement; + + /** + * SQL input file + * @var PhingFile + */ + private $srcFile; + + /** + * SQL input command + * @var string + */ + private $sqlCommand = ""; + + /** + * SQL transactions to perform + */ + private $transactions = array(); + + /** + * SQL Statement delimiter (for parsing files) + * @var string + */ + private $delimiter = ";"; + + /** + * The delimiter type indicating whether the delimiter will + * only be recognized on a line by itself + */ + private $delimiterType = "normal"; // can't use constant just defined + + /** + * Action to perform if an error is found + **/ + private $onError = "abort"; + + /** + * Encoding to use when reading SQL statements from a file + */ + private $encoding = null; + + /** + * Fetch mode for PDO select queries. + * @var int + */ + private $fetchMode; + + /** + * Set the name of the SQL file to be run. + * Required unless statements are enclosed in the build file + */ + public function setSrc(PhingFile $srcFile) { + $this->srcFile = $srcFile; + } + + /** + * Set an inline SQL command to execute. + * NB: Properties are not expanded in this text. + */ + public function addText($sql) { + $this->sqlCommand .= $sql; + } + + /** + * Adds a set of files (nested fileset attribute). + */ + public function addFileset(FileSet $set) { + $this->filesets[] = $set; + } + + /** + * Adds a set of files (nested filelist attribute). + */ + public function addFilelist(FileList $list) { + $this->filelists[] = $list; + } + + /** + * Creates a new PDOSQLExecFormatterElement for <formatter> element. + * @return PDOSQLExecFormatterElement + */ + public function createFormatter() + { + $fe = new PDOSQLExecFormatterElement($this); + $this->formatters[] = $fe; + return $fe; + } + + /** + * Add a SQL transaction to execute + */ + public function createTransaction() { + $t = new PDOSQLExecTransaction($this); + $this->transactions[] = $t; + return $t; + } + + /** + * Set the file encoding to use on the SQL files read in + * + * @param encoding the encoding to use on the files + */ + public function setEncoding($encoding) { + $this->encoding = $encoding; + } + + /** + * Set the statement delimiter. + * + * <p>For example, set this to "go" and delimitertype to "ROW" for + * Sybase ASE or MS SQL Server.</p> + * + * @param delimiter + */ + public function setDelimiter($delimiter) + { + $this->delimiter = $delimiter; + } + + /** + * Get the statement delimiter. + * + * @return string + */ + public function getDelimiter() + { + return $this->delimiter; + } + + /** + * Set the Delimiter type for this sql task. The delimiter type takes two + * values - normal and row. Normal means that any occurence of the delimiter + * terminate the SQL command whereas with row, only a line containing just + * the delimiter is recognized as the end of the command. + * + * @param string $delimiterType + */ + public function setDelimiterType($delimiterType) + { + $this->delimiterType = $delimiterType; + } + + /** + * Action to perform when statement fails: continue, stop, or abort + * optional; default "abort" + */ + public function setOnerror($action) { + $this->onError = $action; + } + + /** + * Sets the fetch mode to use for the PDO resultset. + * @param mixed $mode The PDO fetchmode integer or constant name. + */ + public function setFetchmode($mode) { + if (is_numeric($mode)) { + $this->fetchMode = (int) $mode; + } else { + if (defined($mode)) { + $this->fetchMode = constant($mode); + } else { + throw new BuildException("Invalid PDO fetch mode specified: " . $mode, $this->getLocation()); + } + } + } + + /** + * Gets a default output writer for this task. + * @return Writer + */ + private function getDefaultOutput() + { + return new LogWriter($this); + } + + /** + * Load the sql file and then execute it + * @throws BuildException + */ + public function main() { + + // Set a default fetchmode if none was specified + // (We're doing that here to prevent errors loading the class is PDO is not available.) + if ($this->fetchMode === null) { + $this->fetchMode = PDO::FETCH_ASSOC; + } + + // Initialize the formatters here. This ensures that any parameters passed to the formatter + // element get passed along to the actual formatter object + foreach($this->formatters as $fe) { + $fe->prepare(); + } + + $savedTransaction = array(); + for($i=0,$size=count($this->transactions); $i < $size; $i++) { + $savedTransaction[] = clone $this->transactions[$i]; + } + + $savedSqlCommand = $this->sqlCommand; + + $this->sqlCommand = trim($this->sqlCommand); + + try { + if ($this->srcFile === null && $this->sqlCommand === "" + && empty($this->filesets) && empty($this->filelists) + && count($this->transactions) === 0) { + throw new BuildException("Source file or fileset/filelist, " + . "transactions or sql statement " + . "must be set!", $this->location); + } + + if ($this->srcFile !== null && !$this->srcFile->exists()) { + throw new BuildException("Source file does not exist!", $this->location); + } + + // deal with the filesets + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $srcDir = $fs->getDir($this->project); + $srcFiles = $ds->getIncludedFiles(); + // Make a transaction for each file + foreach($srcFiles as $srcFile) { + $t = $this->createTransaction(); + $t->setSrc(new PhingFile($srcDir, $srcFile)); + } + } + + // process filelists + foreach($this->filelists as $fl) { + $srcDir = $fl->getDir($this->project); + $srcFiles = $fl->getFiles($this->project); + // Make a transaction for each file + foreach($srcFiles as $srcFile) { + $t = $this->createTransaction(); + $t->setSrc(new PhingFile($srcDir, $srcFile)); + } + } + + // Make a transaction group for the outer command + $t = $this->createTransaction(); + if ($this->srcFile) $t->setSrc($this->srcFile); + $t->addText($this->sqlCommand); + $this->conn = $this->getConnection(); + + try { + + $this->statement = null; + + // Initialize the formatters. + $this->initFormatters(); + + try { + + // Process all transactions + for ($i=0,$size=count($this->transactions); $i < $size; $i++) { + if (!$this->isAutocommit()) { + $this->log("Beginning transaction", Project::MSG_VERBOSE); + $this->conn->beginTransaction(); + } + $this->transactions[$i]->runTransaction(); + if (!$this->isAutocommit()) { + $this->log("Commiting transaction", Project::MSG_VERBOSE); + $this->conn->commit(); + } + } + } catch (Exception $e) { + $this->closeConnection(); + throw $e; + } + } catch (IOException $e) { + if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { + try { + $this->conn->rollback(); + } catch (PDOException $ex) {} + } + $this->closeConnection(); + throw new BuildException($e->getMessage(), $this->location); + } catch (PDOException $e){ + if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { + try { + $this->conn->rollback(); + } catch (PDOException $ex) {} + } + $this->closeConnection(); + throw new BuildException($e->getMessage(), $this->location); + } + + // Close the formatters. + $this->closeFormatters(); + + $this->log($this->goodSql . " of " . $this->totalSql . + " SQL statements executed successfully"); + + } catch (Exception $e) { + $this->transactions = $savedTransaction; + $this->sqlCommand = $savedSqlCommand; + $this->closeConnection(); + throw $e; + } + // finally { + $this->transactions = $savedTransaction; + $this->sqlCommand = $savedSqlCommand; + $this->closeConnection(); + } + + + /** + * read in lines and execute them + * @throws PDOException, IOException + */ + public function runStatements(Reader $reader) { + + if (self::DELIM_NORMAL == $this->delimiterType && 0 === strpos($this->getUrl(), 'pgsql:')) { + require_once 'phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php'; + $splitter = new PgsqlPDOQuerySplitter($this, $reader); + } else { + require_once 'phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php'; + $splitter = new DefaultPDOQuerySplitter($this, $reader, $this->delimiterType); + } + + try { + while (null !== ($query = $splitter->nextQuery())) { + $this->log("SQL: " . $query, Project::MSG_VERBOSE); + $this->execSQL($query); + } + + } catch (PDOException $e) { + throw $e; + } + } + + /** + * Whether the passed-in SQL statement is a SELECT statement. + * This does a pretty simple match, checking to see if statement starts with + * 'select' (but not 'select into'). + * + * @param string $sql + * @return boolean Whether specified SQL looks like a SELECT query. + */ + protected function isSelectSql($sql) + { + $sql = trim($sql); + return (stripos($sql, 'select') === 0 && stripos($sql, 'select into ') !== 0); + } + + /** + * Exec the sql statement. + * @throws PDOException + */ + protected function execSQL($sql) { + + // Check and ignore empty statements + if (trim($sql) == "") { + return; + } + + try { + $this->totalSql++; + + $this->statement = $this->conn->prepare($sql); + $this->statement->execute(); + $this->log($this->statement->rowCount() . " rows affected", Project::MSG_VERBOSE); + + // only call processResults() for statements that return actual data (such as 'select') + if ($this->statement->columnCount() > 0) + { + $this->processResults(); + } + + $this->statement->closeCursor(); + $this->statement = null; + + $this->goodSql++; + + } catch (PDOException $e) { + $this->log("Failed to execute: " . $sql, Project::MSG_ERR); + if ($this->onError != "continue") { + throw new BuildException("Failed to execute SQL", $e); + } + $this->log($e->getMessage(), Project::MSG_ERR); + } + } + + /** + * Returns configured PDOResultFormatter objects (which were created from PDOSQLExecFormatterElement objects). + * @return array PDOResultFormatter[] + */ + protected function getConfiguredFormatters() + { + $formatters = array(); + foreach ($this->formatters as $fe) { + $formatters[] = $fe->getFormatter(); + } + return $formatters; + } + + /** + * Initialize the formatters. + */ + protected function initFormatters() { + $formatters = $this->getConfiguredFormatters(); + foreach ($formatters as $formatter) { + $formatter->initialize(); + } + + } + + /** + * Run cleanup and close formatters. + */ + protected function closeFormatters() { + $formatters = $this->getConfiguredFormatters(); + foreach ($formatters as $formatter) { + $formatter->close(); + } + } + + /** + * Passes results from query to any formatters. + * @throws PDOException + */ + protected function processResults() { + + try { + + $this->log("Processing new result set.", Project::MSG_VERBOSE); + + $formatters = $this->getConfiguredFormatters(); + + while ($row = $this->statement->fetch($this->fetchMode)) { + foreach ($formatters as $formatter) { + $formatter->processRow($row); + } + } + + } catch (Exception $x) { + $this->log("Error processing reults: " . $x->getMessage(), Project::MSG_ERR); + foreach ($formatters as $formatter) { + $formatter->close(); + } + throw $x; + } + + } + + /** + * Closes current connection + */ + protected function closeConnection() + { + if ($this->conn) { + unset($this->conn); + } + } +} + +/** + * "Inner" class that contains the definition of a new transaction element. + * Transactions allow several files or blocks of statements + * to be executed using the same JDBC connection and commit + * operation in between. + * + * @package phing.tasks.ext.pdo + */ +class PDOSQLExecTransaction { + + private $tSrcFile = null; + private $tSqlCommand = ""; + private $parent; + + function __construct($parent) + { + // Parent is required so that we can log things ... + $this->parent = $parent; + } + + public function setSrc(PhingFile $src) + { + $this->tSrcFile = $src; + } + + public function addText($sql) + { + $this->tSqlCommand .= $sql; + } + + /** + * @throws IOException, PDOException + */ + public function runTransaction() + { + if (!empty($this->tSqlCommand)) { + $this->parent->log("Executing commands", Project::MSG_INFO); + $this->parent->runStatements(new StringReader($this->tSqlCommand)); + } + + if ($this->tSrcFile !== null) { + $this->parent->log("Executing file: " . $this->tSrcFile->getAbsolutePath(), + Project::MSG_INFO); + $reader = new FileReader($this->tSrcFile); + $this->parent->runStatements($reader); + $reader->close(); + } + } +} + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOTask.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOTask.php new file mode 100755 index 00000000..93feaa6d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PDOTask.php @@ -0,0 +1,215 @@ +<?php + +/* + * $Id: de478f3e51714db7d9163b6bbc3fa64de27549cb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/types/Reference.php'; + +/** + * Handles PDO configuration needed by SQL type tasks. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Nick Chalko <nick@chalko.com> (Ant) + * @author Jeff Martin <jeff@custommonkey.org> (Ant) + * @author Michael McCallum <gholam@xtra.co.nz> (Ant) + * @author Tim Stephenson <tim.stephenson@sybase.com> (Ant) + * @version $Id$ + * @package phing.tasks.system + */ +abstract class PDOTask extends Task { + + private $caching = true; + + /** + * Autocommit flag. Default value is false + */ + private $autocommit = false; + + /** + * DB url. + */ + private $url; + + /** + * User name. + */ + private $userId; + + /** + * Password + */ + private $password; + + /** + * RDBMS Product needed for this SQL. + **/ + private $rdbms; + + /** + * Initialize CreoleTask. + * This method includes any necessary Creole libraries and triggers + * appropriate error if they cannot be found. This is not done in header + * because we may want this class to be loaded w/o triggering an error. + */ + function init() { + if (!class_exists('PDO')) { + throw new Exception("PDOTask depends on PDO feature being included in PHP."); + } + } + + /** + * Caching loaders / driver. This is to avoid + * getting an OutOfMemoryError when calling this task + * multiple times in a row; default: true + * @param $enable + */ + public function setCaching($enable) { + $this->caching = $enable; + } + + /** + * Sets the database connection URL; required. + * @param url The url to set + */ + public function setUrl($url) { + $this->url = $url; + } + + /** + * Sets the password; required. + * @param password The password to set + */ + public function setPassword($password) { + $this->password = $password; + } + + /** + * Auto commit flag for database connection; + * optional, default false. + * @param autocommit The autocommit to set + */ + public function setAutocommit($autocommit) { + $this->autocommit = $autocommit; + } + + /** + * Sets the version string, execute task only if + * rdbms version match; optional. + * @param version The version to set + */ + public function setVersion($version) { + $this->version = $version; + } + + protected function getLoaderMap() { + return self::$loaderMap; + } + + + /** + * Creates a new Connection as using the driver, url, userid and password specified. + * The calling method is responsible for closing the connection. + * @return Connection the newly created connection. + * @throws BuildException if the UserId/Password/Url is not set or there is no suitable driver or the driver fails to load. + */ + protected function getConnection() { + + if ($this->url === null) { + throw new BuildException("Url attribute must be set!", $this->location); + } + + try { + + $this->log("Connecting to " . $this->getUrl(), Project::MSG_VERBOSE); + + $user = null; + $pass = null; + + if ($this->userId) { + $user = $this->getUserId(); + } + + if ($this->password) { + $pass = $this->getPassword(); + } + + $conn = new PDO($this->getUrl(), $user, $pass); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + try { + $conn->setAttribute(PDO::ATTR_AUTOCOMMIT, $this->autocommit); + } catch (PDOException $pe) { + $this->log("Unable to enable auto-commit for this database: " . $pe->getMessage(), Project::MSG_VERBOSE); + } + + return $conn; + + } catch (SQLException $e) { + throw new BuildException($e->getMessage(), $this->location); + } + + } + + public function isCaching($value) { + $this->caching = $value; + } + + /** + * Gets the autocommit. + * @return Returns a boolean + */ + public function isAutocommit() { + return $this->autocommit; + } + + /** + * Gets the url. + * @return Returns a String + */ + public function getUrl() { + return $this->url; + } + + /** + * Gets the userId. + * @return Returns a String + */ + public function getUserId() { + return $this->userId; + } + + /** + * Set the user name for the connection; required. + * @param userId The userId to set + */ + public function setUserid($userId) { + $this->userId = $userId; + } + + /** + * Gets the password. + * @return Returns a String + */ + public function getPassword() { + return $this->password; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php new file mode 100755 index 00000000..b99ac624 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php @@ -0,0 +1,291 @@ +<?php + +/** + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + * + * @version SVN: $Id: 0e3570c0e594f4396d833d77e841294855b297d9 $ + * @package phing.tasks.ext.pdo + */ + +require_once 'phing/tasks/ext/pdo/PDOQuerySplitter.php'; + +/** + * Splits PostgreSQL's dialect of SQL into separate queries + * + * Unlike DefaultPDOQuerySplitter this uses a lexer instead of regular + * expressions. This allows handling complex constructs like C-style comments + * (including nested ones) and dollar-quoted strings. + * + * @author Alexey Borzov <avb@php.net> + * @package phing.tasks.ext.pdo + * @version $Id: 0e3570c0e594f4396d833d77e841294855b297d9 $ + * @link http://www.phing.info/trac/ticket/499 + * @link http://www.postgresql.org/docs/current/interactive/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING + */ +class PgsqlPDOQuerySplitter extends PDOQuerySplitter +{ + /**#@+ + * Lexer states + */ + const STATE_NORMAL = 0; + const STATE_SINGLE_QUOTED = 1; + const STATE_DOUBLE_QUOTED = 2; + const STATE_DOLLAR_QUOTED = 3; + const STATE_COMMENT_LINEEND = 4; + const STATE_COMMENT_MULTILINE = 5; + const STATE_BACKSLASH = 6; + /**#@-*/ + + /** + * Nesting depth of current multiline comment + * @var int + */ + protected $commentDepth = 0; + + /** + * Current dollar-quoting "tag" + * @var string + */ + protected $quotingTag = ''; + + /** + * Current lexer state, one of STATE_* constants + * @var int + */ + protected $state = self::STATE_NORMAL; + + /** + * Whether a backslash was just encountered in quoted string + * @var bool + */ + protected $escape = false; + + /** + * Current source line being examined + * @var string + */ + protected $line = ''; + + /** + * Position in current source line + * @var int + */ + protected $inputIndex; + + /** + * Gets next symbol from the input, false if at end + * + * @return string|bool + */ + public function getc() + { + if (!strlen($this->line) || $this->inputIndex >= strlen($this->line)) { + if (null === ($line = $this->sqlReader->readLine())) { + return false; + } + $project = $this->parent->getOwningTarget()->getProject(); + $this->line = ProjectConfigurator::replaceProperties( + $project, $line, $project->getProperties() + ) . "\n"; + $this->inputIndex = 0; + } + return $this->line[$this->inputIndex++]; + } + + /** + * Bactracks one symbol on the input + * + * NB: we don't need ungetc() at the start of the line, so this case is + * not handled. + */ + public function ungetc() + { + $this->inputIndex--; + } + + /** + * Checks whether symbols after $ are a valid dollar-quoting tag + * + * @return string|bool Dollar-quoting "tag" if it is present, false otherwise + */ + protected function checkDollarQuote() + { + $ch = $this->getc(); + if ('$' == $ch) { + // empty tag + return ''; + + } elseif (!ctype_alpha($ch) && '_' != $ch) { + // not a delimiter + $this->ungetc(); + return false; + + } else { + $tag = $ch; + while (false !== ($ch = $this->getc())) { + if ('$' == $ch) { + return $tag; + + } elseif (ctype_alnum($ch) || '_' == $ch) { + $tag .= $ch; + + } else { + for ($i = 0; $i < strlen($tag); $i++) { + $this->ungetc(); + } + return false; + } + } + } + } + + public function nextQuery() + { + $sql = ''; + $delimiter = $this->parent->getDelimiter(); + $openParens = 0; + + while (false !== ($ch = $this->getc())) { + switch ($this->state) { + case self::STATE_NORMAL: + switch ($ch) { + case '-': + if ('-' == $this->getc()) { + $this->state = self::STATE_COMMENT_LINEEND; + } else { + $this->ungetc(); + } + break; + case '"': + $this->state = self::STATE_DOUBLE_QUOTED; + break; + case "'": + $this->state = self::STATE_SINGLE_QUOTED; + break; + case '/': + if ('*' == $this->getc()) { + $this->state = self::STATE_COMMENT_MULTILINE; + $this->commentDepth = 1; + } else { + $this->ungetc(); + } + break; + case '$': + if (false !== ($tag = $this->checkDollarQuote())) { + $this->state = self::STATE_DOLLAR_QUOTED; + $this->quotingTag = $tag; + $sql .= '$' . $tag . '$'; + continue 3; + } + break; + case '(': + $openParens++; + break; + case ')': + $openParens--; + break; + // technically we can use e.g. psql's \g command as delimiter + case $delimiter[0]: + // special case to allow "create rule" statements + // http://www.postgresql.org/docs/current/interactive/sql-createrule.html + if (';' == $delimiter && 0 < $openParens) { + break; + } + $hasQuery = true; + for ($i = 1; $i < strlen($delimiter); $i++) { + if ($delimiter[$i] != $this->getc()) { + $hasQuery = false; + } + } + if ($hasQuery) { + return $sql; + } else { + for ($j = 1; $j < $i; $j++) { + $this->ungetc(); + } + } + } + break; + + case self::STATE_COMMENT_LINEEND: + if ("\n" == $ch) { + $this->state = self::STATE_NORMAL; + } + break; + + case self::STATE_COMMENT_MULTILINE: + switch ($ch) { + case '/': + if ('*' != $this->getc()) { + $this->ungetc(); + } else { + $this->commentDepth++; + } + break; + + case '*': + if ('/' != $this->getc()) { + $this->ungetc(); + } else { + $this->commentDepth--; + if (0 == $this->commentDepth) { + $this->state = self::STATE_NORMAL; + continue 3; + } + } + } + + case self::STATE_SINGLE_QUOTED: + case self::STATE_DOUBLE_QUOTED: + if ($this->escape) { + $this->escape = false; + break; + } + $quote = $this->state == self::STATE_SINGLE_QUOTED ? "'" : '"'; + switch ($ch) { + case '\\': + $this->escape = true; + break; + case $quote: + if ($quote == $this->getc()) { + $sql .= $quote; + } else { + $this->ungetc(); + $this->state = self::STATE_NORMAL; + } + } + + case self::STATE_DOLLAR_QUOTED: + if ('$' == $ch && false !== ($tag = $this->checkDollarQuote())) { + if ($tag == $this->quotingTag) { + $this->state = self::STATE_NORMAL; + } + $sql .= '$' . $tag . '$'; + continue 2; + } + } + + if ($this->state != self::STATE_COMMENT_LINEEND && $this->state != self::STATE_COMMENT_MULTILINE) { + $sql .= $ch; + } + } + if ('' !== $sql) { + return $sql; + } + return null; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php new file mode 100644 index 00000000..3aad8ec8 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php @@ -0,0 +1,130 @@ +<?php +/** + * $Id: 0b899576769e68651a8847db8db8941aa4d784a2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/tasks/ext/pdo/PDOResultFormatter.php'; + +/** + * Plain text formatter for PDO results. + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext.pdo + * @since 2.3.0 + */ +class PlainPDOResultFormatter extends PDOResultFormatter +{ + /** + * Have column headers been printed? + * @var boolean + */ + private $colsprinted = false; + + /** + * Whether to show headers. + * @var boolean + */ + private $showheaders = true; + + /** + * Column delimiter. + * Defaults to ',' + * @var string + */ + private $coldelimiter = ","; + + /** + * Row delimiter. + * Defaults to PHP_EOL. + * @var string + */ + private $rowdelimiter = PHP_EOL; + + /** + * Set the showheaders attribute. + * @param boolean $v + */ + public function setShowheaders($v) { + $this->showheaders = StringHelper::booleanValue($v); + } + + /** + * Sets the column delimiter. + * @param string $v + */ + public function setColdelim($v) { + $this->coldelimiter = $v; + } + + /** + * Sets the row delimiter. + * @param string $v + */ + public function setRowdelim($v) { + $this->rowdelimiter = $v; + } + + /** + * Processes a specific row from PDO result set. + * + * @param array $row Row of PDO result set. + */ + public function processRow($row) { + + if (!$this->colsprinted && $this->showheaders) { + $first = true; + foreach($row as $fieldName => $ignore) { + if ($first) $first = false; else $line .= ","; + $line .= $fieldName; + } + + $this->out->write($line); + $this->out->write(PHP_EOL); + + $line = ""; + $colsprinted = true; + } // if show headers + + $first = true; + foreach($row as $columnValue) { + + if ($columnValue != null) { + $columnValue = trim($columnValue); + } + + if ($first) { + $first = false; + } else { + $line .= $this->coldelimiter; + } + $line .= $columnValue; + } + + $this->out->write($line); + $this->out->write($this->rowdelimiter); + + } + + public function getPreferredOutfile() + { + return new PhingFile('results.txt'); + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php new file mode 100644 index 00000000..435ae6e7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php @@ -0,0 +1,141 @@ +<?php +/** + * $Id: e5c24c7ac3cd665f7877d66f64218be584429581 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/tasks/ext/pdo/PDOResultFormatter.php'; + +/** + * XML formatter for PDO results. + * + * This class reprsents the output of a query using a simple XML schema. + * + * <results> + * <row> + * <col name="id">value</col> + * <col name="name">value2</col> + * </row> + * <row> + * <col name="id">value</col> + * <col name="name">value2</col> + * </row> + * </results> + * + * The actual names of the colums will depend on the fetchmode that was used + * with PDO. + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext.pdo + * @since 2.3.0 + */ +class XMLPDOResultFormatter extends PDOResultFormatter { + + /** + * The XML document being created. + * @var DOMDocument + */ + private $doc; + + /** + * @var DOMElement + */ + private $rootNode; + + /** + * XML document encoding + * + * @var string + */ + private $encoding; + + /** + * @var boolean + */ + private $formatOutput = true; + + /** + * Set the DOM document encoding. + * @param string $v + */ + public function setEncoding($v) { + $this->encoding = $v; + } + + /** + * @param boolean $v + */ + public function setFormatOutput($v) { + $this->formatOutput = (boolean) $v; + } + + public function initialize() { + $this->doc = new DOMDocument("1.0", $this->encoding); + $this->rootNode = $this->doc->createElement('results'); + $this->doc->appendChild($this->rootNode); + $this->doc->formatOutput = $this->formatOutput; + } + + /** + * Processes a specific row from PDO result set. + * + * @param array $row Row of PDO result set. + */ + public function processRow($row) { + + $rowNode = $this->doc->createElement('row'); + $this->rootNode->appendChild($rowNode); + + foreach($row as $columnName => $columnValue) { + + $colNode = $this->doc->createElement('column'); + $colNode->setAttribute('name', $columnName); + + if ($columnValue != null) { + $columnValue = trim($columnValue); + $colNode->nodeValue = $columnValue; + } + $rowNode->appendChild($colNode); + } + + } + + /** + * Gets a preferred filename for an output file. + * + * If no filename is specified, this is where the results will be placed + * (unless usefile=false). + * + * @return string + */ + public function getPreferredOutfile() + { + return new PhingFile('results.xml'); + } + + /** + * Write XML to file and free the DOM objects. + */ + public function close() { + $this->out->write($this->doc->saveXML()); + $this->rootNode = null; + $this->doc = null; + parent::close(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/pearpackage/Fileset.php b/buildscripts/phing/classes/phing/tasks/ext/pearpackage/Fileset.php new file mode 100755 index 00000000..23a0e4c6 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/pearpackage/Fileset.php @@ -0,0 +1,228 @@ +<?php +/* + * $Id: 7016dea93483cc99ad17a638e30f5ff57c37c78b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/PhingFile.php'; + +/** + * Builds list of files for PEAR_PackageFileManager using a Phing FileSet. + * + * Some code here is taken from PEAR_PackageFileManager_File -- getting results from flat + * array into the assoc array expected from getFileList(). + * + * @author Greg Beaver + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.tasks.ext.pearpackage + * @version $Id$ + */ +class PEAR_PackageFileManager_Fileset { + + /** + * Curent Phing Project. + * @var Project + */ + private $project; + + /** + * FileSets to use. + * @var array FileSet[] + */ + private $filesets = array(); + + /** + * Set up the FileSet filelist generator + * + * 'project' and 'filesets' are the only options that this class uses. + * + * @param PEAR_PackageFileManager + * @param array + */ + function __construct($options) + { + if (!is_array($options)) { + $options = $options->getOptions(); + } + + $this->project = $options['phing_project']; + $this->filesets = $options['phing_filesets']; + } + + /** + * Generate the <filelist></filelist> section + * of the package file. + * + * This function performs the backend generation of the array + * containing all files in this package + * @return array structure of all files to include + */ + function getFileList() { + + $allfiles = array(); + + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + + $files = $ds->getIncludedFiles(); + + // We need to store these files keyed by the basedir from DirectoryScanner + // so that we can resolve the fullpath of the file later. + if (isset($allfiles[$ds->getBasedir()])) + { + $allfiles[$ds->getBasedir()] = array_merge($allfiles[$ds->getBasedir()], $files); + } + else + { + $allfiles[$ds->getBasedir()] = $files; + } + } + + $struc = array(); + + foreach($allfiles as $basedir => $files) { + + foreach($files as $file) { + + // paths are relative to $basedir above + $path = strtr(dirname($file), DIRECTORY_SEPARATOR, '/'); + + if (!$path || $path == '.') { + $path = '/'; // for array index + } + + $parts = explode('.', basename($file)); + $ext = array_pop($parts); + if (strlen($ext) == strlen($file)) { + $ext = ''; + } + + $f = new PhingFile($basedir, $file); + + $struc[$path][] = array('file' => basename($file), + 'ext' => $ext, + 'path' => (($path == '/') ? basename($file) : $path . '/' . basename($file)), + 'fullpath' => $f->getAbsolutePath()); + } + } + + uksort($struc,'strnatcasecmp'); + foreach($struc as $key => $ind) { + usort($ind, array($this, 'sortfiles')); + $struc[$key] = $ind; + } + + $tempstruc = $struc; + $struc = array('/' => $tempstruc['/']); + $bv = 0; + foreach($tempstruc as $key => $ind) { + $save = $key; + if ($key != '/') { + $struc['/'] = $this->setupDirs($struc['/'], explode('/', $key), $tempstruc[$key]); + } + } + uksort($struc['/'], array($this, 'mystrucsort')); + + return $struc; + } + + /** + * Recursively move contents of $struc into associative array + * + * The contents of $struc have many indexes like 'dir/subdir/subdir2'. + * This function converts them to + * array('dir' => array('subdir' => array('subdir2'))) + * @param array struc is array('dir' => array of files in dir, + * 'dir/subdir' => array of files in dir/subdir,...) + * @param array array form of 'dir/subdir/subdir2' array('dir','subdir','subdir2') + * @return array same as struc but with array('dir' => + * array(file1,file2,'subdir' => array(file1,...))) + */ + private function setupDirs($struc, $dir, $contents) { + + if (!count($dir)) { + foreach($contents as $dir => $files) { + if (is_string($dir)) { + if (strpos($dir, '/')) { + $test = true; + $a = $contents[$dir]; + unset($contents[$dir]); + $b = explode('/', $dir); + $c = array_shift($b); + if (isset($contents[$c])) { + $contents[$c] = $this->setDir($contents[$c], $this->setupDirs(array(), $b, $a)); + } else { + $contents[$c] = $this->setupDirs(array(), $b, $a); + } + } + } + } + return $contents; + } + $me = array_shift($dir); + if (!isset($struc[$me])) { + $struc[$me] = array(); + } + $struc[$me] = $this->setupDirs($struc[$me], $dir, $contents); + return $struc; + } + + /** + * Recursively add all the subdirectories of $contents to $dir without erasing anything in + * $dir + * @param array + * @param array + * @return array processed $dir + */ + function setDir($dir, $contents) + { + while(list($one,$two) = each($contents)) { + if (isset($dir[$one])) { + $dir[$one] = $this->setDir($dir[$one], $contents[$one]); + } else { + $dir[$one] = $two; + } + } + return $dir; + } + + /** + * Sorting functions for the file list + * @param string + * @param string + * @access private + */ + function sortfiles($a, $b) + { + return strnatcasecmp($a['file'],$b['file']); + } + + function mystrucsort($a, $b) + { + if (is_numeric($a) && is_string($b)) return 1; + if (is_numeric($b) && is_string($a)) return -1; + if (is_numeric($a) && is_numeric($b)) + { + if ($a > $b) return 1; + if ($a < $b) return -1; + if ($a == $b) return 0; + } + return strnatcasecmp($a,$b); + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadata.php b/buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadata.php new file mode 100644 index 00000000..1f7a263c --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadata.php @@ -0,0 +1,55 @@ +<?php +/* + * $Id: fae81ee47ae75fc98d7848a22da93bfd4afb7a1b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phar/PharMetadataElement.php'; + +/** + * @package phing.tasks.ext.phar + * @author Alexey Shockov <alexey@shockov.com> + * @since 2.4.0 + */ +class PharMetadata +{ + /** + * @var array + */ + protected $elements = array(); + /** + * @return PharMetadataElement + */ + public function createElement() + { + return ($this->elements[] = new PharMetadataElement()); + } + /** + * @return array + */ + public function toArray() + { + $metadata = array(); + + foreach ($this->elements as $element) { + $metadata[$element->getName()] = $element->toArray(); + } + + return $metadata; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php b/buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php new file mode 100644 index 00000000..c40f68f1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php @@ -0,0 +1,80 @@ +<?php +/* + * $Id: 4c70deae6a6f273d55f504077a3941ad926ad325 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phar/PharMetadata.php'; + +/** + * @package phing.tasks.ext.phar + * @author Alexey Shockov <alexey@shockov.com> + * @since 2.4.0 + */ +class PharMetadataElement + extends PharMetadata +{ + /** + * @var string + */ + private $name; + /** + * @var string + */ + private $value; + /** + * @param string $value + */ + public function setValue($value) + { + $this->value = $value; + } + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + /** + * @return string + */ + public function getName() + { + return $this->name; + } + /** + * Return array of + * + * @return string|array + */ + public function getValue() + { + /* + * Elements first! + */ + return (empty($this->elements) ? $this->value : $this->elements); + } + /** + * @return string|array + */ + public function toArray() + { + return (empty($this->elements) ? $this->value : parent::toArray()); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phar/PharPackageTask.php b/buildscripts/phing/classes/phing/tasks/ext/phar/PharPackageTask.php new file mode 100644 index 00000000..76a4215a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phar/PharPackageTask.php @@ -0,0 +1,362 @@ +<?php +/* + * $Id: 0396ab9c461e7d7655f12c9ed3a613fe6e69f973 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; +require_once 'phing/types/IterableFileSet.php'; +require_once 'phing/tasks/ext/phar/PharMetadata.php'; + +/** + * Package task for {@link http://ru.php.net/manual/en/book.phar.php Phar technology}. + * + * @package phing.tasks.ext + * @author Alexey Shockov <alexey@shockov.com> + * @since 2.4.0 + */ +class PharPackageTask + extends MatchingTask +{ + /** + * @var PhingFile + */ + private $destinationFile; + /** + * @var int + */ + private $compression = Phar::NONE; + /** + * Base directory, from where local package paths will be calculated. + * + * @var PhingFile + */ + private $baseDirectory; + /** + * @var PhingFile + */ + private $cliStubFile; + /** + * @var PhingFile + */ + private $webStubFile; + /** + * @var string + */ + private $stubPath; + /** + * Private key the Phar will be signed with. + * + * @var PhingFile + */ + private $key; + /** + * Password for the private key. + * + * @var string + */ + private $keyPassword = ''; + /** + * @var int + */ + private $signatureAlgorithm = Phar::SHA1; + /** + * @var array + */ + private $filesets = array(); + /** + * @var PharMetadata + */ + private $metadata = null; + /** + * @var string + */ + private $alias; + /** + * @return PharMetadata + */ + public function createMetadata() + { + return ($this->metadata = new PharMetadata()); + } + /** + * @return FileSet + */ + public function createFileSet() + { + $this->fileset = new IterableFileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + /** + * @param string $algorithm + */ + public function setSignature($algorithm) + { + /* + * If we don't support passed algprithm, leave old one. + */ + switch ($algorithm) { + case 'md5': + $this->signatureAlgorithm = Phar::MD5; + break; + case 'sha1': + $this->signatureAlgorithm = Phar::SHA1; + break; + case 'sha256': + $this->signatureAlgorithm = Phar::SHA256; + break; + case 'sha512': + $this->signatureAlgorithm = Phar::SHA512; + break; + case 'openssl': + $this->signatureAlgorithm = Phar::OPENSSL; + break; + default: + break; + } + } + /** + * @param string $compression + */ + public function setCompression($compression) + { + /* + * If we don't support passed compression, leave old one. + */ + switch ($compression) { + case 'gzip': + $this->compression = Phar::GZ; + break; + case 'bzip2': + $this->compression = Phar::BZ2; + break; + default: + break; + } + } + /** + * @param PhingFile $destinationFile + */ + public function setDestFile(PhingFile $destinationFile) + { + $this->destinationFile = $destinationFile; + } + /** + * @param PhingFile $baseDirectory + */ + public function setBaseDir(PhingFile $baseDirectory) + { + $this->baseDirectory = $baseDirectory; + } + /** + * @param PhingFile $stubFile + */ + public function setCliStub(PhingFile $stubFile) + { + $this->cliStubFile = $stubFile; + } + /** + * @param PhingFile $stubFile + */ + public function setWebStub(PhingFile $stubFile) + { + $this->webStubFile = $stubFile; + } + /** + * @param string $stubPath + */ + public function setStub($stubPath) + { + $this->stubPath = $stubPath; + } + /** + * @param $alias + */ + public function setAlias($alias) + { + $this->alias = $alias; + } + /** + * Sets the private key to use to sign the Phar with. + * + * @param PhingFile $key Private key to sign the Phar with. + */ + public function setKey(PhingFile $key) + { + $this->key = $key; + } + /** + * Password for the private key. + * + * @param string $keyPassword + */ + public function setKeyPassword($keyPassword) + { + $this->keyPassword = $keyPassword; + } + /** + * @throws BuildException + */ + public function main() + { + $this->checkPreconditions(); + + try { + $this->log( + 'Building package: '.$this->destinationFile->__toString(), + Project::MSG_INFO + ); + + /* + * Delete old package, if exists. + */ + if ($this->destinationFile->exists()) { + /* + * TODO Check operation for errors... + */ + $this->destinationFile->delete(); + } + + $phar = $this->buildPhar(); + $phar->startBuffering(); + + $baseDirectory = realpath($this->baseDirectory->getPath()); + + foreach ($this->filesets as $fileset) { + $this->log( + 'Adding specified files in ' . $fileset->getDir($this->project) . ' to package', + Project::MSG_VERBOSE + ); + + $phar->buildFromIterator($fileset, $baseDirectory); + } + + $phar->stopBuffering(); + + /* + * File compression, if needed. + */ + if (Phar::NONE != $this->compression) { + $phar->compressFiles($this->compression); + } + } catch (Exception $e) { + throw new BuildException( + 'Problem creating package: '.$e->getMessage(), + $e, + $this->getLocation() + ); + } + } + /** + * @throws BuildException + */ + private function checkPreconditions() + { + if (is_null($this->destinationFile)) { + throw new BuildException("destfile attribute must be set!", $this->getLocation()); + } + + if ($this->destinationFile->exists() && $this->destinationFile->isDirectory()) { + throw new BuildException("destfile is a directory!", $this->getLocation()); + } + + if (!$this->destinationFile->canWrite()) { + throw new BuildException("Can not write to the specified destfile!", $this->getLocation()); + } + if (!is_null($this->baseDirectory)) { + if (!$this->baseDirectory->exists()) { + throw new BuildException("basedir '" . (string) $this->baseDirectory . "' does not exist!", $this->getLocation()); + } + } + if ($this->signatureAlgorithm == Phar::OPENSSL) { + + if (!extension_loaded('openssl')) { + throw new BuildException("PHP OpenSSL extension is required for OpenSSL signing of Phars!", $this->getLocation()); + } + + if (is_null($this->key)) { + throw new BuildException("key attribute must be set for OpenSSL signing!", $this->getLocation()); + } + + if (!$this->key->exists()) { + throw new BuildException("key '" . (string) $this->key . "' does not exist!", $this->getLocation()); + } + + if (!$this->key->canRead()) { + throw new BuildException("key '" . (string) $this->key . "' cannot be read!", $this->getLocation()); + } + } + } + /** + * Build and configure Phar object. + * + * @return Phar + */ + private function buildPhar() + { + $phar = new Phar($this->destinationFile); + + if ($this->signatureAlgorithm == Phar::OPENSSL) { + + // Load up the contents of the key + $keyContents = file_get_contents($this->key); + + // Setup an OpenSSL resource using the private key and tell the Phar + // to sign it using that key. + $private = openssl_pkey_get_private($keyContents, $this->keyPassword); + $phar->setSignatureAlgorithm(Phar::OPENSSL, $private); + + // Get the details so we can get the public key and write that out + // alongside the phar. + $details = openssl_pkey_get_details($private); + file_put_contents($this->destinationFile . '.pubkey', $details['key']); + + } else { + $phar->setSignatureAlgorithm($this->signatureAlgorithm); + } + + if (isset($this->stubPath)) { + $phar->setStub(file_get_contents($this->stubPath)); + } else { + if (!empty($this->cliStubFile)) { + $cliStubFile = $this->cliStubFile->getPathWithoutBase($this->baseDirectory); + } else { + $cliStubFile = null; + } + + if (!empty($this->webStubFile)) { + $webStubFile = $this->webStubFile->getPathWithoutBase($this->baseDirectory); + } else { + $webStubFile = null; + } + + $phar->setDefaultStub($cliStubFile, $webStubFile); + } + + if ($metadata = $this->metadata->toArray()) { + $phar->setMetadata($metadata); + } + + if(!empty($this->alias)){ + $phar->setAlias($this->alias); + } + + return $phar; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php b/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php new file mode 100644 index 00000000..80da93fa --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php @@ -0,0 +1,248 @@ +<?php +/** + * $Id: 5029881915d6a9e95320ba6e7f463f38af66cd93 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/phk/PhkPackageWebAccess.php'; + +/** + * See {@link http://phk.tekwire.net/} for more information about PHK. + * + * @author Alexey Shockov <alexey@shockov.com> + * @package phing.tasks.ext.phk + */ +class PhkPackageTask extends Task +{ + /** + * @var string + */ + private $outputFile; + /** + * @var string + */ + private $inputDirectory; + /** + * @var string + */ + private $phkCreatorPath; + /** + * @var PhkPackageWebAccess + */ + private $webAccess; + /** + * @var array + */ + private $modifiers = array(); + /** + * @var array + */ + private $options = array(); + /** + * @return PhkPackageWebAccess + */ + public function createWebAccess() + { + return ($this->webAccess = new PhkPackageWebAccess()); + } + /** + * @param string $crcCheck + */ + public function setCrcCheck($crcCheck) + { + $this->options['crc_check'] = ('true' == $crcCheck ? true : false); + } + /** + * @param string $webRunScript + */ + public function setWebRunScript($webRunScript) + { + $this->options['web_run_script'] = $webRunScript; + } + /** + * @param string $cliRunScript + */ + public function setCliRunScript($cliRunScript) + { + $this->options['cli_run_script'] = $cliRunScript; + } + /** + * @param string $libRunScript + */ + public function setLibRunScript($libRunScript) + { + $this->options['lib_run_script'] = $libRunScript; + } + /** + * @param string $name + */ + public function setName($name) + { + $this->options['name'] = $name; + } + /** + * @param string $webMainRedirect + */ + public function setWebMainRedirect($webMainRedirect) + { + $this->options['web_main_redirect'] = ('true' == $webMainRedirect ? true : false); + } + /** + * @param string $pluginClass + */ + public function setPluginClass($pluginClass) + { + $this->options['plugin_class'] = $pluginClass; + } + /** + * @param string $version + */ + public function setVersion($version) + { + $this->options['version'] = $version; + } + /** + * @param string $summary + */ + public function setSummary($summary) + { + $this->options['summary'] = $summary; + } + /** + * @param string $inputDirectory + */ + public function setInputDirectory($inputDirectory) + { + $this->inputDirectory = $inputDirectory; + } + /** + * @param string $outputFile + */ + public function setOutputFile($outputFile) + { + $this->outputFile = $outputFile; + } + /** + * May be none, gzip or bzip2. + * + * @param string $compress + */ + public function setCompress($compress) + { + $this->modifiers['compress'] = $compress; + } + /** + * True or false. + * + * @param srting $strip + */ + public function setStrip($strip) + { + $this->modifiers['strip'] = $strip; + } + /** + * Path to PHK_Creator.phk file. + * + * @param srting $path + */ + public function setPhkCreatorPath($path) + { + $this->phkCreatorPath = $path; + } + /** + * + */ + public function init() + { + + } + /** + * Main method... + */ + public function main() + { + /* + * Check for empty first - speed ;) + */ + if (!is_file($this->phkCreatorPath)) { + throw new BuildException('You must specify the "phkcreatorpath" attribute for PHK task.'); + } + if (empty($this->inputDirectory)) { + throw new BuildException('You must specify the "inputdirectory" attribute for PHK task.'); + } + if (empty($this->outputFile)) { + throw new BuildException('You must specify the "outputfile" attribute for PHK task.'); + } + + require_once $this->phkCreatorPath; + + $mountPoint = PHK_Mgr::mount($this->outputFile, PHK::F_CREATOR); + $phkManager = PHK_Mgr::instance($mountPoint); + + /* + * Add files. + */ + $phkManager->ftree()->merge_file_tree('/', $this->inputDirectory, $this->modifiers); + + /* + * Add web_access to options, if present. + */ + if (!is_null($this->webAccess)) { + $webAccessPaths = $this->webAccess->getPaths(); + if (!empty($webAccessPaths)) { + $this->options['web_access'] = $webAccessPaths; + } + } + + $phkManager->set_options($this->options); + + /* + * Intercept output (in PHP we can't intercept stream). + */ + ob_start(); + /* + * Create file... + */ + $phkManager->dump(); + /* + * Print with Phing log... + */ + $output = trim(ob_get_clean()); + $output = explode("\n", $output); + foreach ($output as $line) { + /* + * Delete all '--- *' lines. Bluh! + */ + /* + * TODO Change preg_math to more faster alternative. + */ + if (preg_match('/^---/', $line)) { + continue; + } + + $this->log($line); + } + + /* + * Set rights for generated file... Don't use umask() - see + * notes in official documentation for this function. + */ + chmod($this->outputFile, 0644); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php b/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php new file mode 100644 index 00000000..9634d899 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php @@ -0,0 +1,57 @@ +<?php +/** + * $Id: b6ea3e7df3c43498e8c6e105bedfd0b6e99e055a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phk/PhkPackageWebAccessPath.php'; + +/** + * @author Alexey Shockov <alexey@shockov.com> + * @package phing.tasks.ext.phk + */ +class PhkPackageWebAccess +{ + /** + * @var array + */ + private $paths = array(); + /** + * @return PhkPackageWebAccessPath + */ + public function createPath() + { + return ($this->paths[] = new PhkPackageWebAccessPath()); + } + /** + * @return array + */ + public function getPaths() + { + /* + * Get real paths... + */ + $paths = array(); + + foreach ($this->paths as $path) { + $paths[] = $path->getPath(); + } + + return $paths; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php b/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php new file mode 100644 index 00000000..730a8929 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php @@ -0,0 +1,46 @@ +<?php +/** + * $Id: 755aeaf34726af2eb30c989914faad8d7aebd126 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * @author Alexey Shockov <alexey@shockov.com> + * @package phing.tasks.ext.phk + */ +class PhkPackageWebAccessPath +{ + /** + * @var string + */ + private $path; + /** + * @param string $path + */ + public function addText($path) + { + $this->path = trim($path); + } + /** + * @return string + */ + public function getPath() + { + return $this->path; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php new file mode 100644 index 00000000..e650ca72 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php @@ -0,0 +1,177 @@ +<?php +/** + * $Id: dece05e79e883b4d5e95f2215e36ce4f2f72ed2e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; + +/** + * A wrapper for the implementations of PHPCPDResultFormatter. + * + * @package phing.tasks.ext.phpcpd + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: dece05e79e883b4d5e95f2215e36ce4f2f72ed2e $ + */ +class PHPCPDFormatterElement +{ + /** + * The report result formatter. + * + * @var PHPCPDResultFormatter + */ + protected $_formatter = null; + + /** + * The type of the formatter. + * + * @var string + */ + protected $_type = ''; + + /** + * Whether to use file (or write output to phing log). + * + * @var boolean + */ + protected $_useFile = true; + + /** + * Output file for formatter. + * + * @var PhingFile + */ + protected $_outfile = null; + + /** + * The parent task + * + * @var PHPCPDTask + */ + private $_parentTask; + + /** + * Construct a new PHPCPDFormatterElement with parent task. + * @param PHPCPDTask $parentTask + */ + public function __construct(PHPCPDTask $parentTask) + { + $this->_parentTask = $parentTask; + } + + /** + * Sets the formatter type. + * + * @param string $type Type of the formatter + * + * @return void + */ + public function setType($type) + { + $this->_type = $type; + + switch ($this->_type) { + case 'pmd': + if ($this->_useFile === false) { + throw new BuildException( + "Formatter '" . $this->_type + . "' can only print the result to an file" + ); + } + + include_once 'phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php'; + $this->_formatter = new PMDPHPCPDResultFormatter(); + break; + + case 'default': + include_once 'phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php'; + $this->_formatter = new DefaultPHPCPDResultFormatter(); + break; + + default: + throw new BuildException( + "Formatter '" . $this->_type . "' not implemented" + ); + } + } + + /** + * Get the formatter type + * + * @return string + */ + public function getType() + { + return $this->_type; + } + + /** + * Set whether to write formatter results to file or not. + * + * @param boolean $useFile True or false. + * + * @return void + */ + public function setUseFile($useFile) + { + $this->_useFile = StringHelper::booleanValue($useFile); + } + + /** + * Return whether to write formatter results to file or not. + * + * @return boolean + */ + public function getUseFile() + { + return $this->_useFile; + } + + /** + * Sets the output file for the formatter results. + * + * @param PhingFile $outfile The output file + * + * @return void + */ + public function setOutfile(PhingFile $outfile) + { + $this->_outfile = $outfile; + } + + /** + * Get the output file. + * + * @return PhingFile + */ + public function getOutfile() + { + return $this->_outfile; + } + + /** + * Returns the report formatter. + * + * @throws BuildException When the specified renderer does not exist. + * @return PHPCPDResultFormatter + */ + public function getFormatter() + { + return $this->_formatter; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php new file mode 100644 index 00000000..31f76154 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php @@ -0,0 +1,312 @@ +<?php +/** + * $Id: 8fbff39b2ca68e97afd59d5dc6d5a37c3678624e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Runs PHP Copy & Paste Detector. Checking PHP files for duplicated code. + * Refactored original PhpCpdTask provided by + * Timo Haberkern <timo.haberkern@fantastic-bits.de> + * + * @package phing.tasks.ext.phpcpd + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 8fbff39b2ca68e97afd59d5dc6d5a37c3678624e $ + */ +class PHPCPDTask extends Task +{ + /** + * A php source code filename or directory + * + * @var PhingFile + */ + protected $_file = null; + + /** + * All fileset objects assigned to this task + * + * @var array<FileSet> + */ + protected $_filesets = array(); + + /** + * Minimum number of identical lines. + * + * @var integer + */ + protected $_minLines = 5; + + /** + * Minimum number of identical tokens. + * + * @var integer + */ + protected $_minTokens = 70; + + /** + * List of valid file extensions for analyzed files. + * + * @var array + */ + protected $_allowedFileExtensions = array('php'); + + /** + * List of exclude directory patterns. + * + * @var array + */ + protected $_ignorePatterns = array('.git', '.svn', 'CVS', '.bzr', '.hg'); + + /** + * The format for the report + * + * @var string + */ + protected $_format = 'default'; + + /** + * Formatter elements. + * + * @var array<PHPCPDFormatterElement> + */ + protected $_formatters = array(); + + /** + * Load the necessary environment for running PHPCPD. + * + * @throws BuildException - if the phpcpd classes can't be loaded. + */ + public function init() + { + /** + * Determine PHPCPD installation + */ + @include_once 'PHPCPD/Autoload.php'; + + if (! class_exists('PHPCPD_TextUI_Command')) { + throw new BuildException( + 'PHPCPDTask depends on PHPCPD being installed ' + . 'and on include_path.', + $this->getLocation() + ); + } + + // Other dependencies that should only be loaded + // when class is actually used + require_once 'phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php'; + } + + /** + * Set the input source file or directory. + * + * @param PhingFile $file The input source file or directory. + * + * @return void + */ + public function setFile(PhingFile $file) + { + $this->_file = $file; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + * + * @param FileSet $fs List of files to scan + * + * @return void + */ + public function addFileSet(FileSet $fs) + { + $this->_filesets[] = $fs; + } + + /** + * Sets the minimum number of identical lines (default: 5). + * + * @param integer $minLines Minimum number of identical lines + * + * @return void + */ + public function setMinLines($minLines) + { + $this->_minLines = $minLines; + } + + /** + * Sets the minimum number of identical tokens (default: 70). + * + * @param integer $minTokens Minimum number of identical tokens + */ + public function setMinTokens($minTokens) + { + $this->_minTokens = $minTokens; + } + + /** + * Sets a list of filename extensions for valid php source code files. + * + * @param string $fileExtensions List of valid file extensions. + * + * @return void + */ + public function setAllowedFileExtensions($fileExtensions) + { + $this->_allowedFileExtensions = array(); + + $token = ' ,;'; + $ext = strtok($fileExtensions, $token); + + while ($ext !== false) { + $this->_allowedFileExtensions[] = $ext; + $ext = strtok($token); + } + } + + /** + * Sets a list of ignore patterns that is used to exclude directories from + * the source analysis. + * + * @param string $ignorePatterns List of ignore patterns. + * + * @return void + */ + public function setIgnorePatterns($ignorePatterns) + { + $this->_ignorePatterns = array(); + + $token = ' ,;'; + $pattern = strtok($ignorePatterns, $token); + + while ($pattern !== false) { + $this->_ignorePatterns[] = $pattern; + $pattern = strtok($token); + } + } + + /** + * Sets the output format + * + * @param string $format Format of the report + */ + public function setFormat($format) + { + $this->_format = $format; + } + + /** + * Create object for nested formatter element. + * + * @return PHPCPDFormatterElement + */ + public function createFormatter() + { + $num = array_push( + $this->_formatters, + new PHPCPDFormatterElement($this) + ); + return $this->_formatters[$num-1]; + } + + /** + * Executes PHPCPD against PhingFile or a FileSet + * + * @return void + */ + public function main() + { + if (!isset($this->_file) and count($this->_filesets) == 0) { + throw new BuildException( + "Missing either a nested fileset or attribute 'file' set" + ); + } + + if (count($this->_formatters) == 0) { + // turn legacy format attribute into formatter + $fmt = new PHPCPDFormatterElement($this); + $fmt->setType($this->_format); + $fmt->setUseFile(false); + $this->_formatters[] = $fmt; + } + + $this->validateFormatters(); + + $filesToParse = array(); + + if ($this->_file instanceof PhingFile) { + $filesToParse[] = $this->_file->getPath(); + } else { + // append any files in filesets + foreach ($this->_filesets as $fs) { + $files = $fs->getDirectoryScanner($this->project) + ->getIncludedFiles(); + + foreach ($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $filesToParse[] = $f->getAbsolutePath(); + } + } + } + + $this->log('Processing files...'); + + $detector = new PHPCPD_Detector(new PHPCPD_Detector_Strategy_Default()); + $clones = $detector->copyPasteDetection( + $filesToParse, + $this->_minLines, + $this->_minTokens + ); + + $this->log('Finished copy/paste detection'); + + foreach ($this->_formatters as $fe) { + $formatter = $fe->getFormatter(); + $formatter->processClones( + $clones, + $this->project, + $fe->getUseFile(), + $fe->getOutfile() + ); + } + } + + /** + * Validates the available formatters + * + * @throws BuildException + * @return void + */ + protected function validateFormatters() + { + foreach ($this->_formatters as $fe) { + if ($fe->getType() == '') { + throw new BuildException( + "Formatter missing required 'type' attribute." + ); + } + + if ($fe->getUsefile() && $fe->getOutfile() === null) { + throw new BuildException( + "Formatter requires 'outfile' attribute " + . "when 'useFile' is true." + ); + } + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php new file mode 100644 index 00000000..b84da547 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php @@ -0,0 +1,58 @@ +<?php +/** + * $Id: a4825de14746285d97af9d435215ec8400406fa7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PHPCPD/TextUI/ResultPrinter.php'; +require_once 'phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php'; + +/** + * Prints plain text output of phpcpd run + * + * @package phing.tasks.ext.phpcpd.formatter + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: a4825de14746285d97af9d435215ec8400406fa7 $ + */ +class DefaultPHPCPDResultFormatter extends PHPCPDResultFormatter +{ + + /** + * Processes a list of clones. + * + * @param PHPCPD_CloneMap $clones + * @param Project $project + * @param boolean $useFile + * @param PhingFile|null $outfile + */ + public function processClones(PHPCPD_CloneMap $clones, Project $project, $useFile = false, $outFile = null) + { + $logger = new PHPCPD_TextUI_ResultPrinter(); + // default format goes to logs, no buffering + ob_start(); + $logger->printResult($clones, $project->getBaseDir(), true); + $output = ob_get_contents(); + ob_end_clean(); + + if (!$useFile || empty($outFile)) { + echo $output; + } else { + file_put_contents($outFile->getPath(), $output); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php new file mode 100644 index 00000000..0cdab194 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php @@ -0,0 +1,40 @@ +<?php +/** + * $Id: 478d7a823945aa4706befb0059b2fab28743fb07 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * This abstract class describes classes that format the results of a PHPCPD run. + * + * @package phing.tasks.ext.phpcpd.formatter + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 478d7a823945aa4706befb0059b2fab28743fb07 $ + */ +abstract class PHPCPDResultFormatter +{ + /** + * Processes a list of clones. + * + * @param PHPCPD_CloneMap $clones + * @param Project $project + * @param boolean $useFile + * @param PhingFile|null $outfile + */ + abstract public function processClones(PHPCPD_CloneMap $clones, Project $project, $useFile = false, $outFile = null); +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php new file mode 100644 index 00000000..33c24407 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php @@ -0,0 +1,51 @@ +<?php +/** + * $Id: a336822b8c54702ba2b0590e5a812c779e5c221b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PHPCPD/Log/XML/PMD.php'; +require_once 'phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php'; + +/** + * Prints PMD-XML output of phpcpd run + * + * @package phing.tasks.ext.phpcpd.formatter + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: a336822b8c54702ba2b0590e5a812c779e5c221b $ + */ +class PMDPHPCPDResultFormatter extends PHPCPDResultFormatter +{ + /** + * Processes a list of clones. + * + * @param PHPCPD_CloneMap $clones + * @param Project $project + * @param boolean $useFile + * @param PhingFile|null $outfile + */ + public function processClones(PHPCPD_CloneMap $clones, Project $project, $useFile = false, $outFile = null) + { + if (!$useFile || empty($outFile)) { + throw new BuildException("Output filename required for this formatter"); + } + + $logger = new PHPCPD_Log_XML_PMD($outFile); + $logger->processClones($clones); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php new file mode 100644 index 00000000..462b1d99 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php @@ -0,0 +1,98 @@ +<?php +/** + * $Id: 73c919ab2044bf6582f52bd7ccb0184019d52f53 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PhpDocumentor/phpDocumentor/Errors.inc'; + +/** + * Phing subclass of the ErrorTracker class provided with PhpDocumentor to work around limitations in PhpDocumentor API. + * + * This class is necessary because PhpDocumentor does directly output errors and + * warnings occured during testing for undocumented elements to stdout. + * This class is injected globally to force PhpDocumentor to use phing's logging + * mechanism. + * + * Obviously this is far from ideal, but there's also no solution given the inflexibility of the + * PhpDocumentor design. + * + * @author Timo A. Hummel <privat@timohummel.com> @author felicitus + * @version $Id: 73c919ab2044bf6582f52bd7ccb0184019d52f53 $ + * @package phing.tasks.ext.phpdoc + */ +class PhingPhpDocumentorErrorTracker extends ErrorTracker { + + /* + * @var object Reference to the task we're called with + */ + private $task; + + /** + * Outputs a warning. This is an almost 1:1 copy from PhpDocumentor, + * we're just processing the warning text and send it to phing's logger. + * + * @param $num integer Number of parameters + * @return nothing + */ + function addWarning ($num) { + $a = array('', '', '', ''); + if (func_num_args()>1) { + for ($i=1;$i<func_num_args();$i++) { + $a[$i - 1] = func_get_arg($i); + } + } + + $message = sprintf($GLOBALS['phpDocumentor_warning_descrip'][$num], $a[0], $a[1], $a[2], $a[3]); + $this->task->log($message, Project::MSG_WARN); + + } + + /** + * Outputs an error. This is an almost 1:1 copy from PhpDocumentor, + * we're just processing the error text and send it to phing's logger. + * + * @param $num integer Number of parameters + * @return nothing + */ + + function addError ($num) { + $a = array('', '', '', ''); + if (func_num_args()>1) { + for ($i=1;$i<func_num_args();$i++) { + $a[$i - 1] = func_get_arg($i); + } + } + + $message = sprintf($GLOBALS['phpDocumentor_error_descrip'][$num], $a[0], $a[1], $a[2], $a[3]); + $this->task->log($message, Project::MSG_ERR); + + } + + /** + * Sets the task we're working with. This is necessary since we need to be + * able to call the method "log". + * + * @param object $task The task we're working with + * @return nothing + */ + public function setTask ($task) { + $this->task = $task; + } + +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php new file mode 100644 index 00000000..62662b0b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php @@ -0,0 +1,230 @@ +<?php +/** + * $Id: cde99d501839daf8c9dd9df61ee6cce7caad6b3e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PhpDocumentor/phpDocumentor/Setup.inc.php'; + +/** + * Phing subclass of the phpDocumentor_setup class provided with PhpDocumentor to work around limitations in PhpDocumentor API. + * + * This class is necessary because phpDocumentor_setup does not expose a complete API for setting configuration options. Because + * this class must directly modify some "private" GLOBAL(!) configuration variables, it is liable to break if the PhpDocumentor + * internal implementation changes. Obviously this is far from ideal, but there's also no solution given the inflexibility of the + * PhpDocumentor design. + * + * @author Hans Lellelid <hans@xmpl.org>@author hans + * @version $Id: cde99d501839daf8c9dd9df61ee6cce7caad6b3e $ + * @package phing.tasks.ext.phpdoc + */ +class PhingPhpDocumentorSetup extends phpDocumentor_setup { + + /** + * Constructs a new PhingPhpDocumentorSetup. + * + * @param string $configDir Directory in which to look for configuration files. + * @param object $task The task we're working with, so we can pass it on to the ErrorTracker + */ + public function __construct($configdir = null, $task) { + global $_phpDocumentor_cvsphpfile_exts, $_phpDocumentor_setting, $_phpDocumentor_phpfile_exts; + + $this->setup = new Io(); + $this->render = new phpDocumentor_IntermediateParser("Default Title"); + + $GLOBALS['_phpDocumentor_install_dir'] = $configdir; + $this->parseIni(); + + // These redundant-looking lines seem to actually make a difference. + // See: http://phing.info/trac/ticket/150 + $_phpDocumentor_phpfile_exts = $GLOBALS['_phpDocumentor_phpfile_exts']; + $_phpDocumentor_cvsphpfile_exts = $GLOBALS['_phpDocumentor_cvsphpfile_exts']; + + if (tokenizer_ext) { + $this->parse = new phpDocumentorTParser(); + } else { + $this->parse = new Parser(); + } + + $this->setMemoryLimit(); + + include_once 'phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php'; + + // Inject our own error tracker to PhpDocumentor + $GLOBALS['phpDocumentor_errors'] = new PhingPhpDocumentorErrorTracker; + $GLOBALS['phpDocumentor_errors']->setTask($task); + + } + + /** + * Set whether to generate sourcecode for each file parsed. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param bool $b + */ + public function setGenerateSourcecode($b) { + global $_phpDocumentor_setting; + $_phpDocumentor_setting['sourcecode'] = (boolean) $b; + } + + /** + * Set an array of README/INSTALL/CHANGELOG file paths. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param array $files Absolute paths to files. + */ + public function setRicFiles($files) { + global $_phpDocumentor_RIC_files; + $_phpDocumentor_RIC_files = $files; + } + + /** + * Set comma-separated list of tags to ignore. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param string $tags + */ + public function setIgnoreTags($tags) { + global $_phpDocumentor_setting; + $ignoretags = explode(',', $tags); + $ignoretags = array_map('trim', $ignoretags); + $tags = array(); + foreach($ignoretags as $tag) { + if (!in_array($tag,array('@global', '@access', '@package', '@ignore', '@name', '@param', '@return', '@staticvar', '@var'))) + $tags[] = $tag; + } + $_phpDocumentor_setting['ignoretags'] = $tags; + } + + /** + * Set whether to parse dirs as PEAR repos. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param bool $b + */ + public function setPear($b) { + global $_phpDocumentor_setting; + $_phpDocumentor_setting['pear'] = (boolean) $b; + } + + /** + * Set fullpath to directory to look in for examples. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param string $dir + */ + public function setExamplesDir($dir) { + global $_phpDocumentor_setting; + $_phpDocumentor_setting['examplesdir'] = $dir; + } + + /** + * Sets the default package name. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param string $name + */ + public function setDefaultPackageName($name) { + $GLOBALS['phpDocumentor_DefaultPackageName'] = trim($name); + } + + /** + * Sets the default category name. + * + * This method exists as a hack because there is no API exposed for this in PhpDocumentor. + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + * @param string $name + */ + public function setDefaultCategoryName($name) { + $GLOBALS['phpDocumentor_DefaultCategoryName'] = trim($name); + } + + /** + * Enables quiet mode. + * + * This method exists as a hack because the API exposed for this method in PhpDocumentor + * doesn't work correctly. + * + * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this + * is subject to break if PhpDocumentor internals changes. + * + */ + public function setQuietMode() { + global $_phpDocumentor_setting; + $_phpDocumentor_setting['quiet'] = true; + parent::setQuietMode(); + } + + /** + * Control whether or not warnings will be shown for undocumented elements. + * Useful for identifying classes and methods that haven't yet been + * documented. + * + * @param bool $bEnable + */ + public function setUndocumentedelements($bEnable) { + $this->render->setUndocumentedElementWarningsMode($bEnable); + } + + /** + * custom tags, will be recognized and put in tags[] instead of + * unknowntags[] + * + * This method exists as a hack because the API exposed for this method in + * PhpDocumentor doesn't work correctly. + * + * Note that because we are setting a "private" GLOBAL(!!) config var with + * this value, this is subject to break if PhpDocumentor internals changes. + * + * @param string $sCustomtags + */ + public function setCustomtags($sCustomtags) { + global $_phpDocumentor_setting; + $_phpDocumentor_setting['customtags'] = $sCustomtags; + } + + /** + * Files to ignore + * + * @param string $sIgnore + */ + public function setIgnore($sIgnore) { + global $_phpDocumentor_setting; + $_phpDocumentor_setting['ignore'] = $sIgnore; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.php b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.php new file mode 100755 index 00000000..70fcc9e2 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.php @@ -0,0 +1,224 @@ +<?php +/* + * $Id: eaa494390770adc752097a412d63fb863482fd5d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/FileOutputStream.php'; + +/** + * PhpDocumentor2 Task (http://www.phpdoc.org) + * Based on the DocBlox Task + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: eaa494390770adc752097a412d63fb863482fd5d $ + * @since 2.4.10 + * @package phing.tasks.ext.phpdoc + */ +class PhpDocumentor2Task extends Task +{ + /** + * List of filesets + * @var FileSet[] + */ + private $filesets = array(); + + /** + * Destination/target directory + * @var PhingFile + */ + private $destDir = null; + + /** + * name of the template to use + * @var string + */ + private $template = "responsive"; + + /** + * Title of the project + * @var string + */ + private $title = ""; + + /** + * Force phpDocumentor to be quiet + * @var boolean + */ + private $quiet = true; + + /** + * Nested creator, adds a set of files (nested fileset attribute). + * + * @return FileSet + */ + public function createFileSet() + { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Sets destination/target directory + * @param PhingFile $destDir + */ + public function setDestDir(PhingFile $destDir) + { + $this->destDir = $destDir; + } + + /** + * Convenience setter (@see setDestDir) + * @param PhingFile $output + */ + public function setOutput(PhingFile $output) + { + $this->destDir = $output; + } + + /** + * Sets the template to use + * @param strings $template + */ + public function setTemplate($template) + { + $this->template = (string) $template; + } + + /** + * Sets the title of the project + * @param strings $title + */ + public function setTitle($title) + { + $this->title = (string) $title; + } + + /** + * Forces phpDocumentor to be quiet + * @param boolean $quiet + */ + public function setQuiet($quiet) + { + $this->quiet = (boolean) $quiet; + } + + /** + * Finds and initializes the phpDocumentor installation + */ + private function initializePhpDocumentor() + { + $phpDocumentorPath = null; + + foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) { + $testPhpDocumentorPath = $path . DIRECTORY_SEPARATOR . 'phpDocumentor' . DIRECTORY_SEPARATOR . 'src'; + + if (file_exists($testPhpDocumentorPath)) { + $phpDocumentorPath = $testPhpDocumentorPath; + } + } + + if (empty($phpDocumentorPath)) { + throw new BuildException("Please make sure PhpDocumentor 2 is installed and on the include_path.", $this->getLocation()); + } + + set_include_path($phpDocumentorPath . PATH_SEPARATOR . get_include_path()); + + require_once $phpDocumentorPath . '/phpDocumentor/Bootstrap.php'; + + $bootstrap = phpDocumentor_Bootstrap::createInstance(); + + $autoloader = $bootstrap->registerAutoloader(); + + if ($this->quiet) { + phpDocumentor_Core_Abstract::config()->logging->level = 'quiet'; + } else { + phpDocumentor_Core_Abstract::config()->logging->level = 'debug'; + } + + $bootstrap->registerPlugins($autoloader); + } + + /** + * Build a list of files (from the fileset elements) + * and call the phpDocumentor parser + * + * @return string + */ + private function parseFiles() + { + $parser = new phpDocumentor_Parser(); + + //Only initialize the dispatcher when not already done + if (is_null(phpDocumentor_Parser_Abstract::$event_dispatcher)) { + phpDocumentor_Parser_Abstract::$event_dispatcher = new sfEventDispatcher(); + } + $parser->setTitle($this->title); + + $paths = array(); + + // filesets + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $dir = $fs->getDir($this->project); + $srcFiles = $ds->getIncludedFiles(); + + foreach ($srcFiles as $file) { + $paths[] = $dir . FileSystem::getFileSystem()->getSeparator() . $file; + } + } + + $this->log("Will parse " . count($paths) . " file(s)", Project::MSG_VERBOSE); + + $files = new phpDocumentor_Parser_Files(); + $files->addFiles($paths); + + $parser->setPath($files->getProjectRoot()); + + return $parser->parseFiles($files); + } + + /** + * Task entry point + * @see Task::main() + */ + public function main() + { + if (empty($this->destDir)) { + throw new BuildException("You must supply the 'destdir' attribute", $this->getLocation()); + } + + if (empty($this->filesets)) { + throw new BuildException("You have not specified any files to include (<fileset>)", $this->getLocation()); + } + + $this->initializePhpDocumentor(); + + $xml = $this->parseFiles(); + + $this->log("Transforming...", Project::MSG_VERBOSE); + + $transformer = new phpDocumentor_Transformer(); + $transformer->setTemplatesPath(phpDocumentor_Core_Abstract::config()->paths->templates); + $transformer->setTemplates($this->template); + $transformer->setSource($xml); + $transformer->setTarget($this->destDir->getAbsolutePath()); + $transformer->execute(); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php new file mode 100755 index 00000000..fb2a0b2b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php @@ -0,0 +1,265 @@ +<?php + +/** + * $Id: 6a6c740651bb91c9854fcdf0cb1d7e768b84f805 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phpdoc/PhpDocumentorTask.php'; + +/** + * Task to run phpDocumentor with an external process + * + * This classes uses the commandline phpdoc script to build documentation. + * Use this task instead of the PhpDocumentorTask when you've a clash with the + * Smarty libraries. + * + * @author Michiel Rook <mrook@php.net> + * @author Markus Fischer <markus@fischer.name> + * @version $Id: 6a6c740651bb91c9854fcdf0cb1d7e768b84f805 $ + * @package phing.tasks.ext.phpdoc + */ +class PhpDocumentorExternalTask extends PhpDocumentorTask +{ + /** + * The path to the executable for phpDocumentor + */ + protected $programPath = 'phpdoc'; + + protected $sourcepath = NULL; + + /** + * @var bool ignore symlinks to other files or directories + */ + protected $ignoresymlinks = false; + + /** + * Sets the path to the phpDocumentor executable + */ + public function setProgramPath($programPath) + { + $this->programPath = $programPath; + } + + /** + * Returns the path to the phpDocumentor executable + */ + public function getProgramPath() + { + return $this->programPath; + } + + /** + * Set the source path. A directory or a comma separate list of directories. + */ + public function setSourcepath($sourcepath) + { + $this->sourcepath = $sourcepath; + } + + /** + * Ignore symlinks to other files or directories. + * + * @param bool $bSet + */ + public function setIgnoresymlinks($bSet) { + $this->ignoresymlinks = $bSet; + } + + /** + * Main entrypoint of the task + */ + public function main() + { + $this->validate(); + $arguments = join(' ', $this->constructArguments()); + + $this->log("Running phpDocumentor..."); + + exec($this->programPath . " " . $arguments, $output, $return); + + if ($return != 0) + { + throw new BuildException("Could not execute phpDocumentor: " . implode(' ', $output)); + } + + foreach($output as $line) + { + if(strpos($line, 'ERROR') !== false) + { + $this->log($line, Project::MSG_ERR); + continue; + } + + $this->log($line, Project::MSG_VERBOSE); + } + } + + /** + * Constructs an argument string for phpDocumentor + * @return array + */ + protected function constructArguments() + { + $aArgs = array(); + if ($this->title) + { + $aArgs[] = '--title "' . $this->title . '"'; + } + + if ($this->destdir) + { + $aArgs[] = '--target "' . $this->destdir->getAbsolutePath() . '"'; + } + + if ($this->sourcepath) + { + $aArgs[] = '--directory "' . $this->sourcepath . '"'; + } + + if ($this->output) + { + $aArgs[] = '--output ' . $this->output; + } + + if ($this->linksource) + { + $aArgs[] = '--sourcecode on'; + } + + if ($this->parseprivate) + { + $aArgs[] = '--parseprivate on'; + } + + if ($this->ignore) + { + $aArgs[] = '--ignore ' . $this->ignore; + } + + // append any files in filesets + $filesToParse = array(); + foreach($this->filesets as $fs) { + $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles(); + foreach($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $filesToParse[] = $f->getAbsolutePath(); + } + } + if (count($filesToParse) > 0) { + $aArgs[] = '--filename "' . join(',', $filesToParse) . '"'; + } + + // append any files in filesets + $ricFiles = array(); + foreach($this->projDocFilesets as $fs) { + $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles(); + foreach($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $ricFiles[] = $f->getAbsolutePath(); + } + } + if (count($ricFiles) > 0) { + $aArgs[] = '--readmeinstallchangelog "' . + join(',', $ricFiles) . '"'; + } + + if ($this->javadocDesc) { + $aArgs[] = '--javadocdesc on'; + } + + if ($this->quiet) { + $aArgs[] = '--quiet on'; + } + + if ($this->packages) { + $aArgs[] = '--packageoutput "' . $this->packages . '"'; + } + + if ($this->ignoreTags) { + $aArgs[] = '--ignore-tags "' . $this->ignoreTags . '"'; + } + + if ($this->defaultCategoryName) { + $aArgs[] = '--defaultcategoryname "' . $this->defaultCategoryName . + '"'; + } + + if ($this->examplesDir) { + $aArgs[] = '--examplesdir "' . $this->examplesDir->getAbsolutePath() + . '"'; + } + + if ($this->templateBase) { + $aArgs[] = '--templatebase "' . $this->templateBase->getAbsolutePath() + . '"'; + } + + if ($this->pear) { + $aArgs[] = '--pear on'; + } + + if ($this->undocumentedelements) { + $aArgs[] = '--undocumentedelements on'; + } + + if ($this->customtags) { + $aArgs[] = '--customtags "' . $this->customtags . '"'; + } + + if ($this->ignoresymlinks) { + $aArgs[] = '--ignoresymlinks on'; + } + + return $aArgs; + } + + /** + * Override PhpDocumentorTask::init() because they're specific to the phpdoc + * API which we don't use. + */ + public function init() { + } + + /** + * Validates that necessary minimum options have been set. Based on + * PhpDocumentorTask::validate(). + */ + protected function validate() { + if (!$this->destdir) { + throw new BuildException("You must specify a destdir for phpdoc.", + $this->getLocation()); + } + if (!$this->output) { + throw new BuildException("You must specify an output format for " . + "phpdoc (e.g. HTML:frames:default).", $this->getLocation()); + } + if (empty($this->filesets) && !$this->sourcepath) { + throw new BuildException("You have not specified any files to " . + "include (<fileset> or sourcepath attribute) for phpdoc.", + $this->getLocation()); + } + if ($this->configdir) { + $this->log('Ignoring unsupported configdir-Attribute', + Project::MSG_VERBOSE); + } + } +}; + + + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php new file mode 100755 index 00000000..e4ae363a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php @@ -0,0 +1,480 @@ +<?php + +/** + * $Id: 23a04ddae9cad46198e15081a0fb44354135b1c8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Task to run PhpDocumentor. + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Michiel Rook <mrook@php.net> + * @version $Id: 23a04ddae9cad46198e15081a0fb44354135b1c8 $ + * @package phing.tasks.ext.phpdoc + */ +class PhpDocumentorTask extends Task +{ + + /** + * @var string Title for browser window / package index. + */ + protected $title; + + /** + * @var PhingFile The target directory for output files. + */ + protected $destdir; + + /** + * @var array FileSet[] Filesets for files to parse. + */ + protected $filesets = array(); + + /** + * @var array FileSet[] Project documentation (README/INSTALL/CHANGELOG) files. + */ + protected $projDocFilesets = array(); + + /** + * @var string Package output format. + */ + protected $output; + + /** + * @var boolean Whether to generate sourcecode for each file parsed. + */ + protected $linksource = false; + + /** + * @var boolean Whether to parse private members. + */ + protected $parsePrivate = false; + + /** + * @var boolean Whether to use javadoc descriptions (more primitive). + */ + protected $javadocDesc = false; + + /** + * @var PhingFile Base directory for locating template files. + */ + protected $templateBase; + + /** + * @var boolean Wheter to suppress output. + */ + protected $quiet = false; + + /** + * @var string Comma-separated list of packages to output. + */ + protected $packages; + + /** + * @var string Comma-separated list of tags to ignore. + */ + protected $ignoreTags; + + /** + * @var string Default package name. + */ + protected $defaultPackageName; + + /** + * @var string Default category name. + */ + protected $defaultCategoryName; + + /** + * @var PhingFile Directory in which to look for examples. + */ + protected $examplesDir; + + /** + * @var PhingFile Directory in which to look for configuration files. + */ + protected $configDir; + + /** + * @var boolean Whether to parse as a PEAR repository. + */ + protected $pear = false; + + /** + * @var boolean Control whether or not warnings will be shown for + * undocumented elements. Useful for identifying classes and + * methods that haven't yet been documented. + */ + protected $undocumentedelements = false; + + /** + * @var string custom tags, will be recognized and put in tags[] instead of + * unknowntags[]. + */ + protected $customtags = ''; + + /** + * @var string files to ignore + */ + protected $ignore = ''; + + /** + * Set the title for the generated documentation + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * Set the destination directory for the generated documentation + */ + public function setDestdir(PhingFile $destdir) { + $this->destdir = $destdir; + } + + /** + * Alias for {@link setDestdir()}. + * @see setDestdir() + */ + public function setTarget(PhingFile $destdir) { + $this->setDestdir($destdir); + } + + /** + * Set the output format (e.g. HTML:Smarty:PHP). + * @param string $output + */ + public function setOutput($output) { + $this->output = $output; + } + + /** + * Set whether to generate sourcecode for each file parsed + * @param boolean + */ + public function setSourcecode($b) { + $this->setLinksource($b); + } + + /** + * Set whether to generate sourcecode for each file parsed + * @param boolean + */ + public function setLinksource($b) { + $this->linksource = $b; + } + + /** + * Set whether to suppress output. + * @param boolean $b + */ + public function setQuiet($b) { + $this->quiet = $b; + } + + /** + * Should private members/classes be documented + * @param boolean + */ + public function setParseprivate($parseprivate) { + $this->parsePrivate = $parseprivate; + } + + /** + * Whether to use javadoc descriptions (more primitive). + * @param boolean + */ + public function setJavadocdesc($javadoc) { + $this->javadocDesc = $javadoc; + } + + /** + * Set (comma-separated) list of packages to output. + * + * @param string $packages + */ + public function setPackageoutput($packages) { + $this->packages = $packages; + } + + /** + * Set (comma-separated) list of tags to ignore. + * + * @param string $tags + */ + public function setIgnoretags($tags) { + $this->ignoreTags = $tags; + } + + /** + * Set a directory to search for examples in. + * @param PhingFile $d + */ + public function setExamplesdir(PhingFile $d) { + $this->examplesDir = $d; + } + + /** + * Set a directory to search for configuration files in. + * @param PhingFile $d + */ + public function setConfigdir(PhingFile $d) { + $this->configDir = $d; + } + + /** + * Sets the default package name. + * @param string $name + */ + public function setDefaultpackagename($name) { + $this->defaultPackageName = $name; + } + + /** + * Sets the default category name. + * @param string $name + */ + public function setDefaultcategoryname($name) { + $this->defaultCategoryName = $name; + } + + /** + * Set whether to parse as PEAR repository. + * @param boolean $b + */ + public function setPear($b) { + $this->pear = $b; + } + + /** + * Creates a FileSet. + * @return FileSet + */ + public function createFileset() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Creates a readme/install/changelog fileset. + * @return FileSet + */ + public function createProjdocfileset() { + $num = array_push($this->projDocFilesets, new FileSet()); + return $this->projDocFilesets[$num-1]; + } + + /** + * Control whether or not warnings will be shown for undocumented elements. + * Useful for identifying classes and methods that haven't yet been + * documented. + * @param boolean $b + */ + public function setUndocumentedelements($b) { + $this->undocumentedelements = $b; + } + + /** + * custom tags, will be recognized and put in tags[] instead of + * unknowntags[]. + * + * @param string $sCustomtags + */ + public function setCustomtags($sCustomtags) { + $this->customtags = $sCustomtags; + } + + /** + * Set base location of all templates for this parse. + * + * @param PhingFile $destdir + */ + public function setTemplateBase(PhingFile $oTemplateBase) { + $this->templateBase = $oTemplateBase; + } + + /** + * Set files to ignore + * @param string $sIgnore + */ + public function setIgnore($sIgnore) { + $this->ignore = $sIgnore; + } + + /** + * Searches include_path for PhpDocumentor install and adjusts include_path appropriately. + * @throws BuildException - if unable to find PhpDocumentor on include_path + */ + protected function findPhpDocumentorInstall() + { + $found = null; + foreach(explode(PATH_SEPARATOR, get_include_path()) as $path) { + $testpath = $path . DIRECTORY_SEPARATOR . 'PhpDocumentor'; + if (file_exists($testpath)) { + $found = $testpath; + break; + } + } + if (!$found) { + throw new BuildException("PhpDocumentor task depends on PhpDocumentor being installed and on include_path.", $this->getLocation()); + } + // otherwise, adjust the include_path to path to include the PhpDocumentor directory ... + set_include_path(get_include_path() . PATH_SEPARATOR . $found); + include_once ("phpDocumentor/Setup.inc.php"); + if (!class_exists('phpDocumentor_setup')) { + throw new BuildException("Error including PhpDocumentor setup class file."); + } + } + + /** + * Main entrypoint of the task + * Loads the necessary environment for running PhpDoc, then runs PhpDoc + * + * @throws BuildException - if the phpdoc classes can't be loaded. + */ + function main() + { + $this->findPhpDocumentorInstall(); + include_once 'phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php'; + + $this->validate(); + $configdir = $this->configDir ? $this->configDir->getAbsolutePath() : null; + $phpdoc = new PhingPhpDocumentorSetup($configdir, $this); + $this->setPhpDocumentorOptions($phpdoc); + //$phpdoc->readCommandLineSettings(); + $phpdoc->setupConverters($this->output); + $phpdoc->createDocs(); + } + + /** + * Validates that necessary minimum options have been set. + * @throws BuildException if validation doesn't pass + */ + protected function validate() + { + if (!$this->destdir) { + throw new BuildException("You must specify a destdir for phpdoc.", $this->getLocation()); + } + if (!$this->output) { + throw new BuildException("You must specify an output format for phpdoc (e.g. HTML:frames:default).", $this->getLocation()); + } + if (empty($this->filesets)) { + throw new BuildException("You have not specified any files to include (<fileset>) for phpdoc.", $this->getLocation()); + } + } + + /** + * Sets the options on the passed-in phpdoc setup object. + * @param PhingPhpDocumentorSetup $phpdoc + */ + protected function setPhpDocumentorOptions(PhingPhpDocumentorSetup $phpdoc) + { + + // Title MUST be set first ... (because it re-initializes the internal state of the PhpDocu renderer) + if ($this->title) { + $phpdoc->setTitle($this->title); + } + + if ($this->parsePrivate) { + $phpdoc->setParsePrivate(); + } + + if ($this->javadocDesc) { + $phpdoc->setJavadocDesc(); + } + + if ($this->quiet) { + $phpdoc->setQuietMode(); + } + + if ($this->destdir) { + $phpdoc->setTargetDir($this->destdir->getAbsolutePath()); + } + + if ($this->packages) { + $phpdoc->setPackageOutput($this->packages); + } + + if ($this->templateBase) { + $phpdoc->setTemplateBase($this->templateBase->getAbsolutePath()); + } + + if ($this->linksource) { + $phpdoc->setGenerateSourcecode($this->linksource); + } + + if ($this->examplesDir) { + $phpdoc->setExamplesDir($this->examplesDir->getAbsolutePath()); + } + + if ($this->ignoreTags) { + $phpdoc->setIgnoreTags($this->ignoreTags); + } + + if ($this->defaultPackageName) { + $phpdoc->setDefaultPackageName($this->defaultPackageName); + } + + if ($this->defaultCategoryName) { + $phpdoc->setDefaultCategoryName($this->defaultCategoryName); + } + + if ($this->pear) { + $phpdoc->setPear($this->pear); + } + + if ($this->ignore) { + $phpdoc->setIgnore($this->ignore); + } + + // append any files in filesets + $filesToParse = array(); + foreach($this->filesets as $fs) { + $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles(); + foreach($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $filesToParse[] = $f->getAbsolutePath(); + } + } + //print_r(implode(",", $filesToParse)); + $phpdoc->setFilesToParse(implode(",", $filesToParse)); + + + // append any files in filesets + $ricFiles = array(); + foreach($this->projDocFilesets as $fs) { + $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles(); + foreach($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $ricFiles[] = $f->getName(); + } + } + $phpdoc->setRicFiles($ricFiles); + + if ($this->undocumentedelements) { + $phpdoc->setUndocumentedelements($this->undocumentedelements); + } + + if ($this->customtags) { + $phpdoc->setCustomtags($this->customtags); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php b/buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php new file mode 100644 index 00000000..c327e331 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php @@ -0,0 +1,181 @@ +<?php +/** + * $Id: 69fc758899446b96312ac12f26b461969eb41b6e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; + +/** + * A wrapper for the implementations of PHPMDResultFormatter. + * + * @package phing.tasks.ext.phpmd + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 69fc758899446b96312ac12f26b461969eb41b6e $ + * @since 2.4.1 + */ +class PHPMDFormatterElement +{ + /** + * @var PHPMDResultFormatter + */ + protected $formatter = null; + + /** + * The type of the formatter. + * + * @var string + */ + protected $type = ""; + + /** + * Whether to use file (or write output to phing log). + * + * @var boolean + */ + protected $useFile = true; + + /** + * Output file for formatter. + * + * @var PhingFile + */ + protected $outfile = null; + + /** + * Sets the formatter type. + * + * @param string $type Type of the formatter + * + * @return void + */ + public function setType($type) + { + $this->type = $type; + + switch ($this->type) { + case 'xml': + include_once 'PHP/PMD/Renderer/XMLRenderer.php'; + break; + + case 'html': + include_once 'PHP/PMD/Renderer/HTMLRenderer.php'; + break; + + case 'text': + include_once 'PHP/PMD/Renderer/TextRenderer.php'; + break; + + default: + throw new BuildException("Formatter '" . $this->type . "' not implemented"); + } + } + + /** + * Get the formatter type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set whether to write formatter results to file or not. + * + * @param boolean $useFile True or false. + * + * @return void + */ + public function setUseFile($useFile) + { + $this->useFile = (boolean) $useFile; + } + + /** + * Return whether to write formatter results to file or not. + * + * @return boolean + */ + public function getUseFile() + { + return $this->useFile; + } + + /** + * Sets the output file for the formatter results. + * + * @param PhingFile $outfile The output file + * + * @return void + */ + public function setOutfile(PhingFile $outfile) + { + $this->outfile = $outfile; + } + + /** + * Get the output file. + * + * @return PhingFile + */ + public function getOutfile() + { + return $this->outfile; + } + + /** + * Creates a report renderer instance based on the formatter type. + * + * @return PHP_PMD_AbstractRenderer + * @throws BuildException When the specified renderer does not exist. + */ + public function getRenderer() + { + switch ($this->type) { + case 'xml': + $renderer = new PHP_PMD_Renderer_XMLRenderer(); + break; + + case 'html': + $renderer = new PHP_PMD_Renderer_HTMLRenderer(); + break; + + case 'text': + $renderer = new PHP_PMD_Renderer_TextRenderer(); + break; + + default: + throw new BuildException("PHP_MD renderer '" . $this->type . "' not implemented"); + } + + // Create a report stream + if ($this->getUseFile() === false || $this->getOutfile() === null) { + $stream = STDOUT; + } else { + $stream = fopen($this->getOutfile()->getAbsoluteFile(), 'wb'); + } + + require_once 'PHP/PMD/Writer/Stream.php'; + + $renderer->setWriter(new PHP_PMD_Writer_Stream($stream)); + + return $renderer; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php b/buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php new file mode 100644 index 00000000..1c4d34bf --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php @@ -0,0 +1,284 @@ +<?php +/** + * $Id: 35668c2c6fbf1ca87fab21c26fd55ef630458fc7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/phpmd/PHPMDFormatterElement.php'; + +/** + * Runs PHP Mess Detector. Checking PHP files for several potential problems + * based on rulesets. + * + * @package phing.tasks.ext.phpmd + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 35668c2c6fbf1ca87fab21c26fd55ef630458fc7 $ + * @since 2.4.1 + */ +class PHPMDTask extends Task +{ + /** + * A php source code filename or directory + * + * @var PhingFile + */ + protected $file = null; + + /** + * All fileset objects assigned to this task + * + * @var array<FileSet> + */ + protected $filesets = array(); + + /** + * The rule-set filenames or identifier. + * + * @var string + */ + protected $rulesets = 'codesize,unusedcode'; + + /** + * The minimum priority for rules to load. + * + * @var integer + */ + protected $minimumPriority = 0; + + /** + * List of valid file extensions for analyzed files. + * + * @var array + */ + protected $allowedFileExtensions = array('php'); + + /** + * List of exclude directory patterns. + * + * @var array + */ + protected $ignorePatterns = array('.git', '.svn', 'CVS', '.bzr', '.hg'); + + /** + * The format for the report + * + * @var string + */ + protected $format = 'text'; + + /** + * Formatter elements. + * + * @var array<PHPMDFormatterElement> + */ + protected $formatters = array(); + + /** + * Set the input source file or directory. + * + * @param PhingFile $file The input source file or directory. + * + * @return void + */ + public function setFile(PhingFile $file) + { + $this->file = $file; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + * + * @return FileSet The created fileset object + */ + public function createFileSet() + { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Sets the minimum rule priority. + * + * @param integer $minimumPriority Minimum rule priority. + * + * @return void + */ + public function setMinimumPriority($minimumPriority) + { + $this->minimumPriority = $minimumPriority; + } + + /** + * Sets the rule-sets. + * + * @param string $ruleSetFileNames Comma-separated string of rule-set filenames + * or identifier. + * + * @return void + */ + public function setRulesets($ruleSetFileNames) + { + $this->rulesets = $ruleSetFileNames; + } + + /** + * Sets a list of filename extensions for valid php source code files. + * + * @param string $fileExtensions List of valid file extensions without leading dot. + * + * @return void + */ + public function setAllowedFileExtensions($fileExtensions) + { + $this->allowedFileExtensions = array(); + + $token = ' ,;'; + $ext = strtok($fileExtensions, $token); + + while ($ext !== false) { + $this->allowedFileExtensions[] = $ext; + $ext = strtok($token); + } + } + + /** + * Sets a list of ignore patterns that is used to exclude directories from + * the source analysis. + * + * @param string $ignorePatterns List of ignore patterns. + * + * @return void + */ + public function setIgnorePatterns($ignorePatterns) + { + $this->ignorePatterns = array(); + + $token = ' ,;'; + $pattern = strtok($ignorePatterns, $token); + + while ($pattern !== false) { + $this->ignorePatterns[] = $pattern; + $pattern = strtok($token); + } + } + + /** + * Create object for nested formatter element. + * + * @return PHPMDFormatterElement + */ + public function createFormatter() + { + $num = array_push($this->formatters, new PHPMDFormatterElement()); + return $this->formatters[$num-1]; + } + + /** + * Executes PHPMD against PhingFile or a FileSet + * + * @throws BuildException - if the phpmd classes can't be loaded. + * @return void + */ + public function main() + { + /** + * Find PHPMD + */ + @include_once 'PHP/PMD.php'; + + if (! class_exists('PHP_PMD')) { + throw new BuildException( + 'PHPMDTask depends on PHPMD being installed and on include_path.', + $this->getLocation() + ); + } + + require_once 'PHP/PMD/AbstractRule.php'; + + if (!$this->minimumPriority) { + $this->minimumPriority = PHP_PMD_AbstractRule::LOWEST_PRIORITY; + } + + if (!isset($this->file) and count($this->filesets) == 0) { + throw new BuildException("Missing either a nested fileset or attribute 'file' set"); + } + + if (count($this->formatters) == 0) { + // turn legacy format attribute into formatter + $fmt = new PHPMDFormatterElement(); + $fmt->setType($this->format); + $fmt->setUseFile(false); + $this->formatters[] = $fmt; + } + + $reportRenderers = array(); + + foreach ($this->formatters as $fe) { + if ($fe->getType() == '') { + throw new BuildException("Formatter missing required 'type' attribute."); + } + if ($fe->getUsefile() && $fe->getOutfile() === null) { + throw new BuildException("Formatter requires 'outfile' attribute when 'useFile' is true."); + } + + $reportRenderers[] = $fe->getRenderer(); + } + + // Create a rule set factory + $ruleSetFactory = new PHP_PMD_RuleSetFactory(); + $ruleSetFactory->setMinimumPriority($this->minimumPriority); + + $phpmd = new PHP_PMD(); + + $phpmd->setFileExtensions($this->allowedFileExtensions); + $phpmd->setIgnorePattern($this->ignorePatterns); + + $filesToParse = array(); + + if ($this->file instanceof PhingFile) { + $filesToParse[] = $this->file->getPath(); + } else { + // append any files in filesets + foreach ($this->filesets as $fs) { + $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles(); + foreach ($files as $filename) { + $f = new PhingFile($fs->getDir($this->project), $filename); + $filesToParse[] = $f->getAbsolutePath(); + } + } + } + + if (count($filesToParse) > 0) { + $inputPath = implode(',', $filesToParse); + + $this->log('Processing files...'); + + $phpmd->processFiles( + $inputPath, + $this->rulesets, + $reportRenderers, + $ruleSetFactory + ); + + $this->log('Finished processing files'); + } else { + $this->log('No files to process'); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/BatchTest.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/BatchTest.php new file mode 100755 index 00000000..6a6cec24 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/BatchTest.php @@ -0,0 +1,230 @@ +<?php +/** + * $Id: 4067d915614ff7a864c31f19549bcf6a96c0f92d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phpunit/PHPUnitUtil.php'; +require_once 'phing/types/FileSet.php'; + +/** + * Scans a list of files given by the fileset attribute, extracts valid test cases + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 4067d915614ff7a864c31f19549bcf6a96c0f92d $ + * @package phing.tasks.ext.phpunit + * @since 2.1.0 + */ +class BatchTest +{ + /** the list of filesets containing the testcase filename rules */ + private $filesets = array(); + + /** the reference to the project */ + private $project = NULL; + + /** the classpath to use with Phing::__import() calls */ + private $classpath = NULL; + + /** names of classes to exclude */ + private $excludeClasses = array(); + + /** name of the batchtest/suite */ + protected $name = "Phing Batchtest"; + + /** + * Create a new batchtest instance + * + * @param Project the project it depends on. + */ + public function __construct(Project $project) + { + $this->project = $project; + } + + /** + * Sets the name of the batchtest/suite + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Sets the classes to exclude + */ + public function setExclude($exclude) + { + $this->excludeClasses = explode(" ", $exclude); + } + + /** + * Sets the classpath + */ + public function setClasspath(Path $classpath) + { + if ($this->classpath === null) + { + $this->classpath = $classpath; + } + else + { + $this->classpath->append($classpath); + } + } + + /** + * Creates a new Path object + */ + public function createClasspath() + { + $this->classpath = new Path(); + return $this->classpath; + } + + /** + * Returns the classpath + */ + public function getClasspath() + { + return $this->classpath; + } + + /** + * Add a new fileset containing the XML results to aggregate + * + * @param FileSet the new fileset containing XML results. + */ + public function addFileSet(FileSet $fileset) + { + $this->filesets[] = $fileset; + } + + /** + * Iterate over all filesets and return the filename of all files. + * + * @return array an array of filenames + */ + private function getFilenames() + { + $filenames = array(); + + foreach ($this->filesets as $fileset) + { + $ds = $fileset->getDirectoryScanner($this->project); + $ds->scan(); + + $files = $ds->getIncludedFiles(); + + foreach ($files as $file) + { + $filenames[] = $ds->getBaseDir() . "/" . $file; + } + } + + return $filenames; + } + + /** + * Checks wheter $input is a PHPUnit Test + */ + private function isTestCase($input) + { + return is_subclass_of($input, 'PHPUnit_Framework_TestCase') || is_subclass_of($input, 'PHPUnit_Framework_TestSuite'); + } + + /** + * Filters an array of classes, removes all classes that are not test cases or test suites, + * or classes that are declared abstract + */ + private function filterTests($input) + { + $reflect = new ReflectionClass($input); + + return $this->isTestCase($input) && (!$reflect->isAbstract()); + } + + /** + * Returns an array of test cases and test suites that are declared + * by the files included by the filesets + * + * @return array an array of tests. + */ + protected function elements() + { + $filenames = $this->getFilenames(); + + $declaredClasses = array(); + + foreach ($filenames as $filename) + { + $definedClasses = PHPUnitUtil::getDefinedClasses($filename, $this->classpath); + + foreach($definedClasses as $definedClass) { + $this->project->log("(PHPUnit) Adding $definedClass (from $filename) to tests.", Project::MSG_DEBUG); + } + + $declaredClasses = array_merge($declaredClasses, $definedClasses); + } + + $elements = array_filter($declaredClasses, array($this, "filterTests")); + + return $elements; + } + + /** + * Returns a testsuite containing all the tests in this batch + * + * @deprecated + * @return PHPUnit_Framework_TestSuite + */ + public function getTestSuite() + { + $suite = new PHPUnit_Framework_TestSuite($this->name); + + foreach ($this->elements() as $test) + { + $testClass = new $test(); + if (!($testClass instanceof PHPUnit_Framework_TestSuite)) + { + $testClass = new ReflectionClass($test); + } + + $suite->addTestSuite($testClass); + } + + return $suite; + } + + /** + * Add the tests in this batchtest to a test suite + * @param PHPUnit_Framework_TestSuite $suite + */ + public function addToTestSuite(PHPUnit_Framework_TestSuite $suite) + { + foreach ($this->elements() as $element) { + $testClass = new $element(); + if (!($testClass instanceof PHPUnit_Framework_TestSuite)) + { + $testClass = new ReflectionClass($element); + } + $suite->addTestSuite($testClass); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/FormatterElement.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/FormatterElement.php new file mode 100755 index 00000000..c220b79d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/FormatterElement.php @@ -0,0 +1,178 @@ +<?php +/** + * $Id: 296214ebac3a12e51bffed3dcc2c0bb93fb0754e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; + +/** + * A wrapper for the implementations of PHPUnit2ResultFormatter. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 296214ebac3a12e51bffed3dcc2c0bb93fb0754e $ + * @package phing.tasks.ext.phpunit + * @since 2.1.0 + */ +class FormatterElement +{ + protected $formatter = NULL; + + protected $type = ""; + + protected $useFile = true; + + protected $toDir = "."; + + protected $outfile = ""; + + protected $parent = NULL; + + /** + * Sets parent task + * @param Task $parent Calling Task + */ + public function setParent($parent) + { + $this->parent = $parent; + } + + /** + * Loads a specific formatter type + * @param string $type + */ + public function setType($type) + { + $this->type = $type; + + if ($this->type == "summary") + { + require_once 'phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php'; + $this->formatter = new SummaryPHPUnitResultFormatter($this->parent); + } + else + if ($this->type == "clover") + { + require_once 'phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php'; + $this->formatter = new CloverPHPUnitResultFormatter($this->parent); + } + else + if ($this->type == "xml") + { + require_once 'phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php'; + $this->formatter = new XMLPHPUnitResultFormatter($this->parent); + } + else + if ($this->type == "plain") + { + require_once 'phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php'; + $this->formatter = new PlainPHPUnitResultFormatter($this->parent); + } + else + { + throw new BuildException("Formatter '" . $this->type . "' not implemented"); + } + } + + /** + * Loads a specific formatter class + */ + public function setClassName($className) + { + $classNameNoDot = Phing::import($className); + + $this->formatter = new $classNameNoDot(); + } + + /** + * Sets whether to store formatting results in a file + */ + public function setUseFile($useFile) + { + $this->useFile = $useFile; + } + + /** + * Returns whether to store formatting results in a file + */ + public function getUseFile() + { + return $this->useFile; + } + + /** + * Sets output directory + * @param string $toDir + */ + public function setToDir($toDir) + { + $this->toDir = $toDir; + } + + /** + * Returns output directory + * @return string + */ + public function getToDir() + { + return $this->toDir; + } + + /** + * Sets output filename + * @param string $outfile + */ + public function setOutfile($outfile) + { + $this->outfile = $outfile; + } + + /** + * Returns output filename + * @return string + */ + public function getOutfile() + { + if ($this->outfile) + { + return $this->outfile; + } + else + { + return $this->formatter->getPreferredOutfile() . $this->getExtension(); + } + } + + /** + * Returns extension + * @return string + */ + public function getExtension() + { + return $this->formatter->getExtension(); + } + + /** + * Returns formatter object + * @return PHPUnitResultFormatter + */ + public function getFormatter() + { + return $this->formatter; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php new file mode 100755 index 00000000..87338da7 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php @@ -0,0 +1,248 @@ +<?php +/** + * $Id: b88d6fa4ca4717177b562a0475c81d92c161d9b4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/FileWriter.php'; +require_once 'phing/util/ExtendedFileStream.php'; + +/** + * Transform a PHPUnit xml report using XSLT. + * This transformation generates an html report in either framed or non-framed + * style. The non-framed style is convenient to have a concise report via mail, + * the framed report is much more convenient if you want to browse into + * different packages or testcases since it is a Javadoc like report. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: b88d6fa4ca4717177b562a0475c81d92c161d9b4 $ + * @package phing.tasks.ext.phpunit + * @since 2.1.0 + */ +class PHPUnitReportTask extends Task +{ + private $format = "noframes"; + private $styleDir = ""; + private $toDir = ""; + + /** + * Whether to use the sorttable JavaScript library, defaults to false + * See {@link http://www.kryogenix.org/code/browser/sorttable/)} + * + * @var boolean + */ + private $useSortTable = false; + + /** the directory where the results XML can be found */ + private $inFile = "testsuites.xml"; + + /** + * Set the filename of the XML results file to use. + */ + public function setInFile(PhingFile $inFile) + { + $this->inFile = $inFile; + } + + /** + * Set the format of the generated report. Must be noframes or frames. + */ + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Set the directory where the stylesheets are located. + */ + public function setStyleDir($styleDir) + { + $this->styleDir = $styleDir; + } + + /** + * Set the directory where the files resulting from the + * transformation should be written to. + */ + public function setToDir(PhingFile $toDir) + { + $this->toDir = $toDir; + } + + /** + * Sets whether to use the sorttable JavaScript library, defaults to false + * See {@link http://www.kryogenix.org/code/browser/sorttable/)} + * + * @param boolean $useSortTable + */ + public function setUseSortTable($useSortTable) + { + $this->useSortTable = (boolean) $useSortTable; + } + + /** + * Returns the path to the XSL stylesheet + */ + protected function getStyleSheet() + { + $xslname = "phpunit-" . $this->format . ".xsl"; + + if ($this->styleDir) + { + $file = new PhingFile($this->styleDir, $xslname); + } + else + { + $path = Phing::getResourcePath("phing/etc/$xslname"); + + if ($path === NULL) + { + $path = Phing::getResourcePath("etc/$xslname"); + + if ($path === NULL) + { + throw new BuildException("Could not find $xslname in resource path"); + } + } + + $file = new PhingFile($path); + } + + if (!$file->exists()) + { + throw new BuildException("Could not find file " . $file->getPath()); + } + + return $file; + } + + /** + * Transforms the DOM document + */ + protected function transform(DOMDocument $document) + { + if (!$this->toDir->exists()) + { + throw new BuildException("Directory '" . $this->toDir . "' does not exist"); + } + + $xslfile = $this->getStyleSheet(); + + $xsl = new DOMDocument(); + $xsl->load($xslfile->getAbsolutePath()); + + $proc = new XSLTProcessor(); + if (defined('XSL_SECPREF_WRITE_FILE')) + { + if (version_compare(PHP_VERSION,'5.4',"<")) + { + ini_set("xsl.security_prefs", XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY); + } + else + { + $proc->setSecurityPrefs(XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY); + } + } + + $proc->importStyleSheet($xsl); + $proc->setParameter('', 'output.sorttable', $this->useSortTable); + + if ($this->format == "noframes") + { + $writer = new FileWriter(new PhingFile($this->toDir, "phpunit-noframes.html")); + $writer->write($proc->transformToXML($document)); + $writer->close(); + } + else + { + ExtendedFileStream::registerStream(); + + $toDir = (string) $this->toDir; + + // urlencode() the path if we're on Windows + if (FileSystem::getFileSystem()->getSeparator() == '\\') { + $toDir = urlencode($toDir); + } + + // no output for the framed report + // it's all done by extension... + $proc->setParameter('', 'output.dir', $toDir); + $proc->transformToXML($document); + + ExtendedFileStream::unregisterStream(); + } + } + + /** + * Fixes DOM document tree: + * - adds package="default" to 'testsuite' elements without + * package attribute + * - removes outer 'testsuite' container(s) + */ + protected function fixDocument(DOMDocument $document) + { + $rootElement = $document->firstChild; + + $xp = new DOMXPath($document); + + $nodes = $xp->query("/testsuites/testsuite"); + + foreach ($nodes as $node) { + $children = $xp->query("./testsuite", $node); + + if ($children->length) { + foreach ($children as $child) { + if (!$child->hasAttribute('package')) + { + $child->setAttribute('package', 'default'); + } + $rootElement->appendChild($child); + } + + $rootElement->removeChild($node); + } + } + } + + /** + * Initialize the task + */ + public function init() + { + if (!class_exists('XSLTProcessor')) { + throw new BuildException("PHPUnitReportTask requires the XSL extension"); + } + } + + /** + * The main entry point + * + * @throws BuildException + */ + public function main() + { + $testSuitesDoc = new DOMDocument(); + $testSuitesDoc->load((string) $this->inFile); + + $this->fixDocument($testSuitesDoc); + + $this->transform($testSuitesDoc); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.php new file mode 100755 index 00000000..5b842b95 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.php @@ -0,0 +1,378 @@ +<?php +/** + * $Id: 4554fdd642b6ef7774cbb89c537ccba90f3ca972 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; +require_once 'phing/util/LogWriter.php'; + +/** + * Runs PHPUnit tests. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 4554fdd642b6ef7774cbb89c537ccba90f3ca972 $ + * @package phing.tasks.ext.phpunit + * @see BatchTest + * @since 2.1.0 + */ +class PHPUnitTask extends Task +{ + private $batchtests = array(); + private $formatters = array(); + private $bootstrap = ""; + private $haltonerror = false; + private $haltonfailure = false; + private $haltonincomplete = false; + private $haltonskipped = false; + private $errorproperty; + private $failureproperty; + private $incompleteproperty; + private $skippedproperty; + private $printsummary = false; + private $testfailed = false; + private $testfailuremessage = ""; + private $codecoverage = null; + private $groups = array(); + private $excludeGroups = array(); + private $processIsolation = false; + private $usecustomerrorhandler = true; + + /** + * Initialize Task. + * This method includes any necessary PHPUnit libraries and triggers + * appropriate error if they cannot be found. This is not done in header + * because we may want this class to be loaded w/o triggering an error. + */ + public function init() { + /** + * Determine PHPUnit version number + */ + @include_once 'PHPUnit/Runner/Version.php'; + + if (!class_exists('PHPUnit_Runner_Version')) { + throw new BuildException("PHPUnitTask requires PHPUnit to be installed", $this->getLocation()); + } + + $version = PHPUnit_Runner_Version::id(); + + if (version_compare($version, '3.6.0') < 0) + { + throw new BuildException("PHPUnitTask requires PHPUnit version >= 3.6.0", $this->getLocation()); + } + + /** + * Other dependencies that should only be loaded when class is actually used. + */ + require_once 'phing/tasks/ext/phpunit/PHPUnitTestRunner.php'; + require_once 'phing/tasks/ext/phpunit/BatchTest.php'; + require_once 'phing/tasks/ext/phpunit/FormatterElement.php'; + + /** + * point PHPUnit_MAIN_METHOD define to non-existing method + */ + if (!defined('PHPUnit_MAIN_METHOD')) + { + define('PHPUnit_MAIN_METHOD', 'PHPUnitTask::undefined'); + } + } + + /** + * Sets the name of a bootstrap file that is run before + * executing the tests + * + * @param string $bootstrap the name of the bootstrap file + */ + public function setBootstrap($bootstrap) + { + $this->bootstrap = $bootstrap; + } + + public function setErrorproperty($value) + { + $this->errorproperty = $value; + } + + public function setFailureproperty($value) + { + $this->failureproperty = $value; + } + + public function setIncompleteproperty($value) + { + $this->incompleteproperty = $value; + } + + public function setSkippedproperty($value) + { + $this->skippedproperty = $value; + } + + public function setHaltonerror($value) + { + $this->haltonerror = $value; + } + + public function setHaltonfailure($value) + { + $this->haltonfailure = $value; + } + + public function getHaltonfailure() + { + return $this->haltonfailure; + } + + public function setHaltonincomplete($value) + { + $this->haltonincomplete = $value; + } + + public function getHaltonincomplete() + { + return $this->haltonincomplete; + } + + public function setHaltonskipped($value) + { + $this->haltonskipped = $value; + } + + public function getHaltonskipped() + { + return $this->haltonskipped; + } + + public function setPrintsummary($printsummary) + { + $this->printsummary = $printsummary; + } + + public function setCodecoverage($codecoverage) + { + $this->codecoverage = $codecoverage; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = $processIsolation; + } + + public function setUseCustomErrorHandler($usecustomerrorhandler) + { + $this->usecustomerrorhandler = $usecustomerrorhandler; + } + + public function setGroups($groups) + { + $token = ' ,;'; + $this->groups = array(); + $tok = strtok($groups, $token); + while ($tok !== false) { + $this->groups[] = $tok; + $tok = strtok($token); + } + } + + public function setExcludeGroups($excludeGroups) + { + $token = ' ,;'; + $this->excludeGroups = array(); + $tok = strtok($excludeGroups, $token); + while ($tok !== false) { + $this->excludeGroups[] = $tok; + $tok = strtok($token); + } + } + + /** + * Add a new formatter to all tests of this task. + * + * @param FormatterElement formatter element + */ + public function addFormatter(FormatterElement $fe) + { + $fe->setParent($this); + $this->formatters[] = $fe; + } + + /** + * The main entry point + * + * @throws BuildException + */ + public function main() + { + if ($this->codecoverage && !extension_loaded('xdebug')) + { + throw new Exception("PHPUnitTask depends on Xdebug being installed to gather code coverage information."); + } + + if ($this->printsummary) + { + $fe = new FormatterElement(); + $fe->setParent($this); + $fe->setType("summary"); + $fe->setUseFile(false); + $this->formatters[] = $fe; + } + + $autoloadSave = spl_autoload_functions(); + + if ($this->bootstrap) + { + require_once $this->bootstrap; + } + + $suite = new PHPUnit_Framework_TestSuite('AllTests'); + + foreach ($this->batchtests as $batchtest) + { + $batchtest->addToTestSuite($suite); + } + + $this->execute($suite); + + if ($this->testfailed) + { + throw new BuildException($this->testfailuremessage); + } + + $autoloadNew = spl_autoload_functions(); + foreach ($autoloadNew as $autoload) { + spl_autoload_unregister($autoload); + } + + foreach ($autoloadSave as $autoload) { + spl_autoload_register($autoload); + } + } + + /** + * @throws BuildException + */ + protected function execute($suite) + { + $runner = new PHPUnitTestRunner($this->project, $this->groups, $this->excludeGroups, $this->processIsolation); + + if ($this->codecoverage) { + /** + * Add some defaults to the PHPUnit filter + */ + $pwd = dirname(__FILE__); + $path = realpath($pwd . '/../../../'); + + $filter = new PHP_CodeCoverage_Filter(); + $filter->addDirectoryToBlacklist($path); + $runner->setCodecoverage(new PHP_CodeCoverage(null, $filter)); + } + + $runner->setUseCustomErrorHandler($this->usecustomerrorhandler); + + foreach ($this->formatters as $fe) + { + $formatter = $fe->getFormatter(); + + if ($fe->getUseFile()) + { + $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile()); + + $writer = new FileWriter($destFile->getAbsolutePath()); + + $formatter->setOutput($writer); + } + else + { + $formatter->setOutput($this->getDefaultOutput()); + } + + $runner->addFormatter($formatter); + + $formatter->startTestRun(); + } + + $runner->run($suite); + + foreach ($this->formatters as $fe) + { + $formatter = $fe->getFormatter(); + $formatter->endTestRun(); + } + + $retcode = $runner->getRetCode(); + + if ($retcode == PHPUnitTestRunner::ERRORS) { + if ($this->errorproperty) { + $this->project->setNewProperty($this->errorproperty, true); + } + if ($this->haltonerror) { + $this->testfailed = true; + $this->testfailuremessage = $runner->getLastErrorMessage(); + } + } elseif ($retcode == PHPUnitTestRunner::FAILURES) { + if ($this->failureproperty) { + $this->project->setNewProperty($this->failureproperty, true); + } + + if ($this->haltonfailure) { + $this->testfailed = true; + $this->testfailuremessage = $runner->getLastFailureMessage(); + } + } elseif ($retcode == PHPUnitTestRunner::INCOMPLETES) { + if ($this->incompleteproperty) { + $this->project->setNewProperty($this->incompleteproperty, true); + } + + if ($this->haltonincomplete) { + $this->testfailed = true; + $this->testfailuremessage = $runner->getLastIncompleteMessage(); + } + } elseif ($retcode == PHPUnitTestRunner::SKIPPED) { + if ($this->skippedproperty) { + $this->project->setNewProperty($this->skippedproperty, true); + } + + if ($this->haltonskipped) { + $this->testfailed = true; + $this->testfailuremessage = $runner->getLastSkippedMessage(); + } + } + } + + protected function getDefaultOutput() + { + return new LogWriter($this); + } + + /** + * Adds a set of tests based on pattern matching. + * + * @return BatchTest a new instance of a batch test. + */ + public function createBatchTest() + { + $batchtest = new BatchTest($this->getProject()); + + $this->batchtests[] = $batchtest; + + return $batchtest; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.php new file mode 100755 index 00000000..6c7e5e68 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.php @@ -0,0 +1,312 @@ +<?php +/** + * $Id: 5926dfc1177ec0c52ec275a8e542979c8deb6e6f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PHPUnit/Autoload.php'; +require_once 'phing/tasks/ext/coverage/CoverageMerger.php'; +require_once 'phing/system/util/Timer.php'; + +/** + * Simple Testrunner for PHPUnit that runs all tests of a testsuite. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 5926dfc1177ec0c52ec275a8e542979c8deb6e6f $ + * @package phing.tasks.ext.phpunit + * @since 2.1.0 + */ +class PHPUnitTestRunner extends PHPUnit_Runner_BaseTestRunner implements PHPUnit_Framework_TestListener +{ + const SUCCESS = 0; + const FAILURES = 1; + const ERRORS = 2; + const INCOMPLETES = 3; + const SKIPPED = 4; + + private $retCode = 0; + private $lastErrorMessage = ''; + private $lastFailureMessage = ''; + private $lastIncompleteMessage = ''; + private $lastSkippedMessage = ''; + private $formatters = array(); + + private $codecoverage = null; + + private $project = NULL; + + private $groups = array(); + private $excludeGroups = array(); + + private $processIsolation = false; + + private $useCustomErrorHandler = true; + + public function __construct(Project $project, $groups = array(), $excludeGroups = array(), $processIsolation = false) + { + $this->project = $project; + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + $this->processIsolation = $processIsolation; + $this->retCode = self::SUCCESS; + } + + public function setCodecoverage($codecoverage) + { + $this->codecoverage = $codecoverage; + } + + public function setUseCustomErrorHandler($useCustomErrorHandler) + { + $this->useCustomErrorHandler = $useCustomErrorHandler; + } + + public function addFormatter($formatter) + { + $this->formatters[] = $formatter; + } + + public function handleError($level, $message, $file, $line) + { + return PHPUnit_Util_ErrorHandler::handleError($level, $message, $file, $line); + } + + /** + * Run a test + */ + public function run(PHPUnit_Framework_TestSuite $suite) + { + $res = new PHPUnit_Framework_TestResult(); + + if ($this->codecoverage) + { + $whitelist = CoverageMerger::getWhiteList($this->project); + + $this->codecoverage->filter()->addFilesToWhiteList($whitelist); + + $res->setCodeCoverage($this->codecoverage); + } + + $res->addListener($this); + + foreach ($this->formatters as $formatter) + { + $res->addListener($formatter); + } + + /* Set PHPUnit error handler */ + if ($this->useCustomErrorHandler) + { + $oldErrorHandler = set_error_handler(array($this, 'handleError'), E_ALL | E_STRICT); + } + + $suite->run($res, false, $this->groups, $this->excludeGroups, $this->processIsolation); + + foreach ($this->formatters as $formatter) + { + $formatter->processResult($res); + } + + /* Restore Phing error handler */ + if ($this->useCustomErrorHandler) + { + restore_error_handler(); + } + + if ($this->codecoverage) + { + CoverageMerger::merge($this->project, $this->codecoverage->getData()); + } + + if ($res->errorCount() != 0) + { + $this->retCode = self::ERRORS; + } + else if ($res->failureCount() != 0) + { + $this->retCode = self::FAILURES; + } + else if ($res->notImplementedCount() != 0) + { + $this->retCode = self::INCOMPLETES; + } + else if ($res->skippedCount() != 0) + { + $this->retCode = self::SKIPPED; + } + } + + public function getRetCode() + { + return $this->retCode; + } + + public function getLastErrorMessage() + { + return $this->lastErrorMessage; + } + + public function getLastFailureMessage() + { + return $this->lastFailureMessage; + } + + public function getLastIncompleteMessage() + { + return $this->lastIncompleteMessage; + } + + public function getLastSkippedMessage() + { + return $this->lastSkippedMessage; + } + + protected function composeMessage($message, PHPUnit_Framework_Test $test, Exception $e) + { + return "Test $message (" . $test->getName() . " in class " . get_class($test) . "): " . $e->getMessage(); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->lastErrorMessage = $this->composeMessage("ERROR", $test, $e); + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->lastFailureMessage = $this->composeMessage("FAILURE", $test, $e); + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->lastIncompleteMessage = $this->composeMessage("INCOMPLETE", $test, $e); + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->lastSkippedMessage = $this->composeMessage("SKIPPED", $test, $e); + } + + /** + * A test started. + * + * @param string $testName + */ + public function testStarted($testName) + { + } + + /** + * A test ended. + * + * @param string $testName + */ + public function testEnded($testName) + { + } + + /** + * A test failed. + * + * @param integer $status + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + */ + public function testFailed($status, PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e) + { + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + * + * @param string $message + */ + protected function runFailed($message) + { + throw new BuildException($message); + } + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php new file mode 100755 index 00000000..25a85f4c --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php @@ -0,0 +1,141 @@ +<?php +/** + * $Id: c569f96e625ed8f9c6ae5add2b2f4a0a6c3e5a54 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Various utility functions + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: c569f96e625ed8f9c6ae5add2b2f4a0a6c3e5a54 $ + * @package phing.tasks.ext.phpunit + * @since 2.1.0 + */ +class PHPUnitUtil +{ + protected static $definedClasses = array(); + + /** + * Returns the package of a class as defined in the docblock of the class using @package + * + * @param string the name of the class + * @return string the name of the package + */ + static function getPackageName($classname) + { + $reflect = new ReflectionClass($classname); + + if (preg_match('/@package[\s]+([\.\w]+)/', $reflect->getDocComment(), $matches)) + { + return $matches[1]; + } + else + { + return "default"; + } + } + + /** + * Returns the subpackage of a class as defined in the docblock of the class + * using @subpackage + * + * @param string $classname the name of the class + * + * @author Benjamin Schultz <bschultz@proqrent.de> + * @return string|null the name of the subpackage + */ + public static function getSubpackageName($classname) + { + $reflect = new ReflectionClass($classname); + + if (preg_match('/@subpackage[\s]+([\.\w]+)/', $reflect->getDocComment(), $matches)) { + return $matches[1]; + } else { + return null; + } + } + + /** + * Derives the classname from a filename. + * Assumes that there is only one class defined in that particular file, and that + * the naming follows the dot-path (Java) notation scheme. + * + * @param string the filename + * @return string the name fo the class + */ + public static function getClassFromFileName($filename) + { + $filename = basename($filename); + + $rpos = strrpos($filename, '.'); + + if ($rpos != -1) + { + $filename = substr($filename, 0, $rpos); + } + + return $filename; + } + + /** + * @param string the filename + * @param Path optional classpath + * @return array list of classes defined in the file + */ + public static function getDefinedClasses($filename, $classpath = NULL) + { + $filename = realpath($filename); + + if (!file_exists($filename)) + { + throw new Exception("File '" . $filename . "' does not exist"); + } + + if (isset(self::$definedClasses[$filename])) + { + return self::$definedClasses[$filename]; + } + + Phing::__import($filename, $classpath); + + $declaredClasses = get_declared_classes(); + + foreach ($declaredClasses as $classname) + { + $reflect = new ReflectionClass($classname); + + self::$definedClasses[$reflect->getFilename()][] = $classname; + + if (is_array(self::$definedClasses[$reflect->getFilename()])) + { + self::$definedClasses[$reflect->getFilename()] = array_unique(self::$definedClasses[$reflect->getFilename()]); + } + } + + if (isset(self::$definedClasses[$filename])) + { + return self::$definedClasses[$filename]; + } + else + { + return array(); + } + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php new file mode 100755 index 00000000..4d03078d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php @@ -0,0 +1,87 @@ +<?php +/** + * $Id: 97f504caad678a6c7d231fe298c27d1281008e48 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php'; + +/** + * Prints Clover XML output of the test + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 97f504caad678a6c7d231fe298c27d1281008e48 $ + * @package phing.tasks.ext.formatter + * @since 2.4.0 + */ +class CloverPHPUnitResultFormatter extends PHPUnitResultFormatter +{ + /** + * @var PHPUnit_Framework_TestResult + */ + private $result = NULL; + + /** + * PHPUnit version + * @var string + */ + private $version = NULL; + + public function __construct(PHPUnitTask $parentTask) + { + parent::__construct($parentTask); + + $this->version = PHPUnit_Runner_Version::id(); + } + + public function getExtension() + { + return ".xml"; + } + + public function getPreferredOutfile() + { + return "clover-coverage"; + } + + public function processResult(PHPUnit_Framework_TestResult $result) + { + $this->result = $result; + } + + public function endTestRun() + { + require_once 'PHP/CodeCoverage/Report/Clover.php'; + + $coverage = $this->result->getCodeCoverage(); + + if (!empty($coverage)) { + $clover = new PHP_CodeCoverage_Report_Clover(); + + $contents = $clover->process($coverage); + + if ($this->out) + { + $this->out->write($contents); + $this->out->close(); + } + } + + parent::endTestRun(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php new file mode 100755 index 00000000..1b09dbc1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php @@ -0,0 +1,203 @@ +<?php +/** + * $Id: cbf356a8395e116cd6ecddb7f5a822d4b6edf01c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PHPUnit/Framework/TestListener.php'; + +require_once 'phing/system/io/Writer.php'; + +/** + * This abstract class describes classes that format the results of a PHPUnit testrun. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: cbf356a8395e116cd6ecddb7f5a822d4b6edf01c $ + * @package phing.tasks.ext.phpunit.formatter + * @since 2.1.0 + */ +abstract class PHPUnitResultFormatter implements PHPUnit_Framework_TestListener +{ + protected $out = NULL; + + protected $project = NULL; + + private $timers = false; + + private $runCounts = false; + + private $failureCounts = false; + + private $errorCounts = false; + + private $incompleteCounts = false; + + private $skipCounts = false; + + /** + * Constructor + * @param PHPUnitTask $parentTask Calling Task + */ + public function __construct(PHPUnitTask $parentTask) + { + $this->project = $parentTask->getProject(); + } + + /** + * Sets the writer the formatter is supposed to write its results to. + */ + public function setOutput(Writer $out) + { + $this->out = $out; + } + + /** + * Returns the extension used for this formatter + * + * @return string the extension + */ + public function getExtension() + { + return ""; + } + + public function getPreferredOutfile() + { + return ""; + } + + public function processResult(PHPUnit_Framework_TestResult $result) + { + } + + public function startTestRun() + { + $this->timers = array($this->getMicrotime()); + $this->runCounts = array(0); + $this->failureCounts = array(0); + $this->errorCounts = array(0); + $this->incompleteCounts = array(0); + $this->skipCounts = array(0); + } + + public function endTestRun() + { + } + + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->timers[] = $this->getMicrotime(); + $this->runCounts[] = 0; + $this->failureCounts[] = 0; + $this->errorCounts[] = 0; + $this->incompleteCounts[] = 0; + $this->skipCounts[] = 0; + } + + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $lastRunCount = array_pop($this->runCounts); + $this->runCounts[count($this->runCounts) - 1] += $lastRunCount; + + $lastFailureCount = array_pop($this->failureCounts); + $this->failureCounts[count($this->failureCounts) - 1] += $lastFailureCount; + + $lastErrorCount = array_pop($this->errorCounts); + $this->errorCounts[count($this->errorCounts) - 1] += $lastErrorCount; + + $lastIncompleteCount = array_pop($this->incompleteCounts); + $this->incompleteCounts[count($this->incompleteCounts) - 1] += $lastIncompleteCount; + + $lastSkipCount = array_pop($this->skipCounts); + $this->skipCounts[count($this->skipCounts) - 1] += $lastSkipCount; + + array_pop($this->timers); + } + + public function startTest(PHPUnit_Framework_Test $test) + { + $this->runCounts[count($this->runCounts) - 1]++; + } + + public function endTest(PHPUnit_Framework_Test $test, $time) + { + } + + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->errorCounts[count($this->errorCounts) - 1]++; + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->failureCounts[count($this->failureCounts) - 1]++; + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->incompleteCounts[count($this->incompleteCounts) - 1]++; + } + + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->skipCounts[count($this->skipCounts) - 1]++; + } + + public function getRunCount() + { + return end($this->runCounts); + } + + public function getFailureCount() + { + return end($this->failureCounts); + } + + public function getErrorCount() + { + return end($this->errorCounts); + } + + public function getIncompleteCount() + { + return end($this->incompleteCounts); + } + + public function getSkippedCount() + { + return end($this->skipCounts); + } + + public function getElapsedTime() + { + if (end($this->timers)) + { + return $this->getMicrotime() - end($this->timers); + } + else + { + return 0; + } + } + + private function getMicrotime() { + list($usec, $sec) = explode(' ', microtime()); + return (float)$usec + (float)$sec; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php new file mode 100755 index 00000000..e67cfd2b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php @@ -0,0 +1,128 @@ +<?php +/** + * $Id: 529f9b6ab9ced7b78871e3612cd8afce58261a6f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php'; + +/** + * Prints plain text output of the test to a specified Writer. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 529f9b6ab9ced7b78871e3612cd8afce58261a6f $ + * @package phing.tasks.ext.phpunit.formatter + * @since 2.1.0 + */ +class PlainPHPUnitResultFormatter extends PHPUnitResultFormatter +{ + private $inner = ""; + + public function getExtension() + { + return ".txt"; + } + + public function getPreferredOutfile() + { + return "testresults"; + } + + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + parent::startTestSuite($suite); + + $this->inner = ""; + } + + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + if ($suite->getName() == 'AllTests') + { + return false; + } + + $sb = "Testsuite: " . $suite->getName() . "\n"; + $sb.= "Tests run: " . $this->getRunCount(); + $sb.= ", Failures: " . $this->getFailureCount(); + $sb.= ", Errors: " . $this->getErrorCount(); + $sb.= ", Incomplete: " . $this->getIncompleteCount(); + $sb.= ", Skipped: " . $this->getSkippedCount(); + $sb.= ", Time elapsed: " . sprintf('%0.5f', $this->getElapsedTime()) . " s\n"; + + if ($this->out != NULL) + { + $this->out->write($sb); + $this->out->write($this->inner); + } + + parent::endTestSuite($suite); + } + + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + parent::addError($test, $e, $time); + + $this->formatError("ERROR", $test, $e); + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + parent::addFailure($test, $e, $time); + $this->formatError("FAILED", $test, $e); + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + parent::addIncompleteTest($test, $e, $time); + + $this->formatError("INCOMPLETE", $test); + } + + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + parent::addSkippedTest($test, $e, $time); + $this->formatError("SKIPPED", $test); + } + + private function formatError($type, PHPUnit_Framework_Test $test, Exception $e = null) + { + if ($test != null) + { + $this->endTest($test, time()); + } + + $this->inner.= $test->getName() . " " . $type . "\n"; + + if ($e !== null) { + $this->inner.= $e->getMessage() . "\n"; + // $this->inner.= PHPUnit_Util_Filter::getFilteredStackTrace($e, true) . "\n"; + } + } + + public function endTestRun() + { + parent::endTestRun(); + + if ($this->out != NULL) + { + $this->out->close(); + } + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php new file mode 100755 index 00000000..0007c235 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php @@ -0,0 +1,62 @@ +<?php +/** + * $Id: bd9c51fa75c9b856105fc810200028d855a3782d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php'; + +/** + * Prints short summary output of the test to Phing's logging system. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: bd9c51fa75c9b856105fc810200028d855a3782d $ + * @package phing.tasks.ext.formatter + * @since 2.1.0 + */ +class SummaryPHPUnitResultFormatter extends PHPUnitResultFormatter +{ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + parent::endTestSuite($suite); + } + + public function endTestRun() + { + parent::endTestRun(); + + $sb = "Total tests run: " . $this->getRunCount(); + $sb.= ", Failures: " . $this->getFailureCount(); + $sb.= ", Errors: " . $this->getErrorCount(); + $sb.= ", Incomplete: " . $this->getIncompleteCount(); + $sb.= ", Skipped: " . $this->getSkippedCount(); + $sb.= ", Time elapsed: " . sprintf('%0.5f', $this->getElapsedTime()) . " s\n"; + + if ($this->out != NULL) + { + $this->out->write($sb); + $this->out->close(); + } + } + + public function getExtension() + { + return NULL; + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php new file mode 100755 index 00000000..7fef7454 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php @@ -0,0 +1,120 @@ +<?php +/** + * $Id: 3d9c47a29ded9b67b3a3e10c55602b0ab2a9ea38 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'PHPUnit/Util/Log/JUnit.php'; + +require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php'; + +/** + * Prints XML output of the test to a specified Writer + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 3d9c47a29ded9b67b3a3e10c55602b0ab2a9ea38 $ + * @package phing.tasks.ext.formatter + * @since 2.1.0 + */ +class XMLPHPUnitResultFormatter extends PHPUnitResultFormatter +{ + /** + * @var PHPUnit_Util_Log_JUnit + */ + private $logger = NULL; + + public function __construct(PHPUnitTask $parentTask) + { + parent::__construct($parentTask); + + $logIncompleteSkipped = $parentTask->getHaltonincomplete() || $parentTask->getHaltonskipped(); + + $this->logger = new PHPUnit_Util_Log_JUnit(null, $logIncompleteSkipped); + $this->logger->setWriteDocument(false); + } + + public function getExtension() + { + return ".xml"; + } + + public function getPreferredOutfile() + { + return "testsuites"; + } + + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + parent::startTestSuite($suite); + + $this->logger->startTestSuite($suite); + } + + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + parent::endTestSuite($suite); + + $this->logger->endTestSuite($suite); + } + + public function startTest(PHPUnit_Framework_Test $test) + { + parent::startTest($test); + + $this->logger->startTest($test); + } + + public function endTest(PHPUnit_Framework_Test $test, $time) + { + parent::endTest($test, $time); + + $this->logger->endTest($test, $time); + } + + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + parent::addError($test, $e, $time); + + $this->logger->addError($test, $e, $time); + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + parent::addFailure($test, $e, $time); + + $this->logger->addFailure($test, $e, $time); + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + parent::addIncompleteTest($test, $e, $time); + + $this->logger->addIncompleteTest($test, $e, $time); + } + + public function endTestRun() + { + parent::endTestRun(); + + if ($this->out) + { + $this->out->write($this->logger->getXML()); + $this->out->close(); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/rSTTask.php b/buildscripts/phing/classes/phing/tasks/ext/rSTTask.php new file mode 100644 index 00000000..77170f18 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/rSTTask.php @@ -0,0 +1,476 @@ +<?php + +/** + * reStructuredText rendering task for Phing, the PHP build tool. + * + * PHP version 5 + * + * @category Tasks + * @package phing.tasks.ext + * @author Christian Weiske <cweiske@cweiske.de> + * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html + * @link http://www.phing.info/ + * @version SVN: $Id: ad2ac21008b4635c4f557e3a65c9306a350ca1f2 $ + */ + +require_once 'phing/Task.php'; +require_once 'phing/util/FileUtils.php'; + +/** + * reStructuredText rendering task for Phing, the PHP build tool. + * + * PHP version 5 + * + * @category Tasks + * @package phing.tasks.ext + * @author Christian Weiske <cweiske@cweiske.de> + * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html + * @link http://www.phing.info/ + */ +class rSTTask extends Task +{ + /** + * @var string Taskname for logger + */ + protected $taskName = 'rST'; + + /** + * Result format, defaults to "html". + * @see $supportedFormats for all possible options + * + * @var string + */ + protected $format = 'html'; + + /** + * Array of supported output formats + * + * @var array + * @see $format + * @see $targetExt + */ + protected static $supportedFormats = array( + 'html', 'latex', 'man', 'odt', 's5', 'xml' + ); + + /** + * Maps formats to file extensions + * + * @var array + */ + protected static $targetExt = array( + 'html' => 'html', + 'latex' => 'tex', + 'man' => '3', + 'odt' => 'odt', + 's5' => 'html', + 'xml' => 'xml', + ); + + /** + * Input file in rST format. + * Required + * + * @var string + */ + protected $file = null; + + /** + * Additional rst2* tool parameters. + * + * @var string + */ + protected $toolParam = null; + + /** + * Full path to the tool, i.e. /usr/local/bin/rst2html + * + * @var string + */ + protected $toolPath = null; + + /** + * Output file or directory. May be omitted. + * When it ends with a slash, it is considered to be a directory + * + * @var string + */ + protected $destination = null; + + protected $filesets = array(); // all fileset objects assigned to this task + protected $mapperElement = null; + + /** + * all filterchains objects assigned to this task + * + * @var array + */ + protected $filterChains = array(); + + /** + * mode to create directories with + * + * @var integer + */ + protected $mode = 0755; + + /** + * Only render files whole source files are newer than the + * target files + * + * @var boolean + */ + protected $uptodate = false; + + + /** + * Init method: requires the PEAR System class + */ + public function init() + { + require_once 'System.php'; + } + + /** + * The main entry point method. + * + * @return void + */ + public function main() + { + $tool = $this->getToolPath($this->format); + if (count($this->filterChains)) { + $this->fileUtils = new FileUtils(); + } + + if ($this->file != '') { + $file = $this->file; + $targetFile = $this->getTargetFile($file, $this->destination); + $this->render($tool, $file, $targetFile); + return; + } + + if (!count($this->filesets)) { + throw new BuildException( + '"file" attribute or "fileset" subtag required' + ); + } + + // process filesets + $mapper = null; + if ($this->mapperElement !== null) { + $mapper = $this->mapperElement->getImplementation(); + } + + $project = $this->getProject(); + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + + foreach ($srcFiles as $src) { + $file = new PhingFile($fromDir, $src); + if ($mapper !== null) { + $results = $mapper->main($file); + if ($results === null) { + throw new BuildException( + sprintf( + 'No filename mapper found for "%s"', + $file + ) + ); + } + $targetFile = reset($results); + } else { + $targetFile = $this->getTargetFile($file, $this->destination); + } + $this->render($tool, $file, $targetFile); + } + } + } + + + + /** + * Renders a single file and applies filters on it + * + * @param string $tool conversion tool to use + * @param string $source rST source file + * @param string $targetFile target file name + * + * @return void + */ + protected function render($tool, $source, $targetFile) + { + if (count($this->filterChains) == 0) { + return $this->renderFile($tool, $source, $targetFile); + } + + $tmpTarget = tempnam(sys_get_temp_dir(), 'rST-'); + $this->renderFile($tool, $source, $tmpTarget); + + $this->fileUtils->copyFile( + new PhingFile($tmpTarget), + new PhingFile($targetFile), + true, false, $this->filterChains, + $this->getProject(), $this->mode + ); + unlink($tmpTarget); + } + + + + /** + * Renders a single file with the rST tool. + * + * @param string $tool conversion tool to use + * @param string $source rST source file + * @param string $targetFile target file name + * + * @return void + * + * @throws BuildException When the conversion fails + */ + protected function renderFile($tool, $source, $targetFile) + { + if ($this->uptodate && file_exists($targetFile) + && filemtime($source) <= filemtime($targetFile) + ) { + //target is up to date + return; + } + //work around a bug in php by replacing /./ with / + $targetDir = str_replace('/./', '/', dirname($targetFile)); + if (!is_dir($targetDir)) { + $this->log("Creating directory '$targetDir'", Project::MSG_VERBOSE); + mkdir($targetDir, $this->mode, true); + } + + $cmd = $tool + . ' --exit-status=2' + . ' ' . $this->toolParam + . ' ' . escapeshellarg($source) + . ' ' . escapeshellarg($targetFile) + . ' 2>&1'; + + $this->log('command: ' . $cmd, Project::MSG_VERBOSE); + exec($cmd, $arOutput, $retval); + if ($retval != 0) { + $this->log(implode("\n", $arOutput), Project::MSG_INFO); + throw new BuildException('Rendering rST failed'); + } + $this->log(implode("\n", $arOutput), Project::MSG_DEBUG); + } + + + + /** + * Finds the rst2* binary path + * + * @param string $format Output format + * + * @return string Full path to rst2$format + * + * @throws BuildException When the tool cannot be found + */ + protected function getToolPath($format) + { + if ($this->toolPath !== null) { + return $this->toolPath; + } + + $tool = 'rst2' . $format; + $path = System::which($tool); + if (!$path) { + throw new BuildException( + sprintf('"%s" not found. Install python-docutils.', $tool) + ); + } + + return $path; + } + + + + /** + * Determines and returns the target file name from the + * input file and the configured destination name. + * + * @param string $file Input file + * @param string $destination Destination file or directory name, + * may be null + * + * @return string Target file name + * + * @uses $format + * @uses $targetExt + */ + public function getTargetFile($file, $destination = null) + { + if ($destination != '' + && substr($destination, -1) !== '/' + && substr($destination, -1) !== '\\' + ) { + return $destination; + } + + if (strtolower(substr($file, -4)) == '.rst') { + $file = substr($file, 0, -4); + } + + return $destination . $file . '.' . self::$targetExt[$this->format]; + } + + + + /** + * The setter for the attribute "file" + * + * @param string $file Path of file to render + * + * @return void + */ + public function setFile($file) + { + $this->file = $file; + } + + + + /** + * The setter for the attribute "format" + * + * @param string $format Output format + * + * @return void + * + * @throws BuildException When the format is not supported + */ + public function setFormat($format) + { + if (!in_array($format, self::$supportedFormats)) { + throw new BuildException( + sprintf( + 'Invalid output format "%s", allowed are: %s', + $format, + implode(', ', self::$supportedFormats) + ) + ); + } + $this->format = $format; + } + + + + /** + * The setter for the attribute "destination" + * + * @param string $destination Output file or directory. When it ends + * with a slash, it is taken as directory. + * + * @return void + */ + public function setDestination($destination) + { + $this->destination = $destination; + } + + /** + * The setter for the attribute "toolparam" + * + * @param string $param Additional rst2* tool parameters + * + * @return void + */ + public function setToolparam($param) + { + $this->toolParam = $param; + } + + /** + * The setter for the attribute "toolpath" + * + * @param string $param Full path to tool path, i.e. /usr/local/bin/rst2html + * + * @return void + * + * @throws BuildException When the tool does not exist or is not executable + */ + public function setToolpath($path) + { + if (!file_exists($path)) { + $fullpath = System::which($path); + if ($fullpath === false) { + throw new BuildException( + 'Tool does not exist. Path: ' . $path + ); + } + $path = $fullpath; + } + if (!is_executable($path)) { + throw new BuildException( + 'Tool not executable. Path: ' . $path + ); + } + $this->toolPath = $path; + } + + /** + * The setter for the attribute "uptodate" + * + * @param string $uptodate True/false + * + * @return void + */ + public function setUptodate($uptodate) + { + $this->uptodate = (boolean)$uptodate; + } + + + + /** + * Add a set of files to be rendered. + * + * @param FileSet $fileset Set of rst files to render + * + * @return void + */ + public function addFileset(FileSet $fileset) + { + $this->filesets[] = $fileset; + } + + + + /** + * Nested creator, creates one Mapper for this task + * + * @return Mapper The created Mapper type object + * + * @throws BuildException + */ + public function createMapper() + { + if ($this->mapperElement !== null) { + throw new BuildException( + 'Cannot define more than one mapper', $this->location + ); + } + $this->mapperElement = new Mapper($this->project); + return $this->mapperElement; + } + + + + /** + * Creates a filterchain, stores and returns it + * + * @return FilterChain The created filterchain object + */ + public function createFilterChain() + { + $num = array_push($this->filterChains, new FilterChain($this->project)); + return $this->filterChains[$num-1]; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php new file mode 100755 index 00000000..32fbc212 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php @@ -0,0 +1,51 @@ +<?php +/** + * $Id: ae09aa1a433f4de854fa7c27903e7eb0957bc90b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php'; + +/** + * Dummy result formatter used to count SimpleTest results + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: ae09aa1a433f4de854fa7c27903e7eb0957bc90b $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestCountResultFormatter extends SimpleTestResultFormatter +{ + const SUCCESS = 0; + const FAILURES = 1; + const ERRORS = 2; + + function getRetCode() + { + if ($this->getExceptionCount() != 0) + { + return self::ERRORS; + } + else if ($this->getFailCount() != 0) + { + return self::FAILURES; + } + + return self::SUCCESS; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php new file mode 100755 index 00000000..de78ab24 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php @@ -0,0 +1,119 @@ +<?php +/** + * $Id: d7e7e397e81588c3eafcb9e758666fec0fa166f5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php'; + +/** + * Prints plain text output of the test to a specified Writer. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: d7e7e397e81588c3eafcb9e758666fec0fa166f5 $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestDebugResultFormatter extends SimpleTestResultFormatter +{ + protected $current_case = ""; + protected $current_test = ""; + private $failingTests = array(); + + function printFailingTests() { + foreach ($this->failingTests as $test) { + $this->out->write($test . "\n"); + } + } + + function paintCaseStart($test_name) + { + parent::paintCaseStart($test_name); + $this->paint( "Testsuite: $test_name\n"); + $this->current_case = $test_name; + } + + function paintMethodStart($test_name) + { + parent::paintMethodStart($test_name); + $this->current_test = $test_name; + //$msg = "{$this->current_case} :: $test_name\n"; + $msg = " TestCase: $test_name"; + $this->paint($msg); + } + + function paint($msg) { + if ($this->out == null ) { + print $msg; + } else { + $this->out->write($msg); + } + } + + function paintMethodEnd($test_name) { + parent::paintMethodEnd($test_name); + $this->paint("\n"); + } + + function paintCaseEnd($test_name) + { + parent::paintCaseEnd($test_name); + $this->current_case = ""; + /* Only count suites where more than one test was run */ + + if ($this->getRunCount() && false) + { + $sb = ""; + $sb.= "Tests run: " . $this->getRunCount(); + $sb.= ", Failures: " . $this->getFailureCount(); + $sb.= ", Errors: " . $this->getErrorCount(); + $sb.= ", Time elapsed: " . $this->getElapsedTime(); + $sb.= " sec\n"; + $this->paint($sb); + } + + } + + function paintError($message) + { + parent::paintError($message); + $this->formatError("ERROR", $message); + $this->failingTests[] = $this->current_case . "->" . $this->current_test; + } + + function paintFail($message) + { + parent::paintFail($message); + $this->formatError("FAILED", $message); + $this->failingTests[] = $this->current_case . "->" . $this->current_test; + } + function paintException($message) + { + parent::paintException($message); + $this->failingTests[] = $this->current_case . "->" . $this->current_test; + $this->formatError("Exception", $message); + } + + + + private function formatError($type, $message) + { + $this->paint("ERROR: $type: $message"); + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php new file mode 100755 index 00000000..5ae9ba23 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php @@ -0,0 +1,68 @@ +<?php +/** + * $Id: 2441f1b83b9f9d1aeb2a4afd7e049c840d70bbd9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/phpunit/FormatterElement.php'; + +/** + * Child class of "FormatterElement", overrides setType to provide other + * formatter classes for SimpleTest + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 2441f1b83b9f9d1aeb2a4afd7e049c840d70bbd9 $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestFormatterElement extends FormatterElement +{ + function setType($type) + { + $this->type = $type; + + if ($this->type == "xml") + { + require_once 'phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php'; + $destFile = new PhingFile($this->toDir, 'testsuites.xml'); + $this->formatter = new SimpleTestXmlResultFormatter(); + } + else + if ($this->type == "plain") + { + require_once 'phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php'; + $this->formatter = new SimpleTestPlainResultFormatter(); + } + else + if ($this->type == "summary") + { + require_once 'phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php'; + $this->formatter = new SimpleTestSummaryResultFormatter(); + } + else + if ($this->type == "debug") + { + require_once 'phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php'; + $this->formatter = new SimpleTestDebugResultFormatter(); + } + else + { + throw new BuildException("Formatter '" . $this->type . "' not implemented"); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php new file mode 100755 index 00000000..cfba9533 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php @@ -0,0 +1,95 @@ +<?php +/** + * $Id: c0fa060b8f439f7d0013a0ec016ede4c5a76b42d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php'; + +/** + * Prints plain text output of the test to a specified Writer. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: c0fa060b8f439f7d0013a0ec016ede4c5a76b42d $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestPlainResultFormatter extends SimpleTestResultFormatter +{ + private $inner = ""; + + function getExtension() + { + return ".txt"; + } + + function getPreferredOutfile() + { + return "testresults"; + } + + function paintCaseStart($test_name) + { + parent::paintCaseStart($test_name); + + $this->inner = ""; + } + + function paintCaseEnd($test_name) + { + parent::paintCaseEnd($test_name); + + $sb = ""; + /* Only count suites where more than one test was run */ + if ($this->getRunCount()) + { + $sb.= "Testsuite: $test_name\n"; + $sb.= "Tests run: " . $this->getRunCount(); + $sb.= ", Failures: " . $this->getFailureCount(); + $sb.= ", Errors: " . $this->getErrorCount(); + $sb.= ", Time elapsed: " . $this->getElapsedTime(); + $sb.= " sec\n"; + + if ($this->out != NULL) + { + $this->out->write($sb); + $this->out->write($this->inner); + } + } + } + + function paintError($message) + { + parent::paintError($message); + + $this->formatError("ERROR", $message); + } + + function paintFail($message) + { + parent::paintFail($message); + + $this->formatError("FAILED", $message); + } + + private function formatError($type, $message) + { + $this->inner.= $this->getTestName() . " " . $type . "\n"; + $this->inner.= $message . "\n"; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.php new file mode 100755 index 00000000..8efcbb63 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.php @@ -0,0 +1,161 @@ +<?php +/** + * $Id: b9fbde1e1a21cccbcf6c3bdc29765cf0cf681e31 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'simpletest/scorer.php'; + +require_once 'phing/system/io/Writer.php'; + +/** + * This abstract class describes classes that format the results of a SimpleTest testrun. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: b9fbde1e1a21cccbcf6c3bdc29765cf0cf681e31 $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +abstract class SimpleTestResultFormatter extends SimpleReporter +{ + protected $out = NULL; + + protected $project = NULL; + + private $timer = NULL; + + private $runCount = 0; + + private $failureCount = 0; + + private $errorCount = 0; + + private $currentTest = ""; + + /** + * Sets the writer the formatter is supposed to write its results to. + */ + function setOutput(Writer $out) + { + $this->out = $out; + } + + /** + * Returns the extension used for this formatter + * + * @return string the extension + */ + function getExtension() + { + return ""; + } + + /** + * Sets the project + * + * @param Project the project + */ + function setProject(Project $project) + { + $this->project = $project; + } + + function getPreferredOutfile() + { + return ""; + } + + function paintMethodStart($test_name) + { + parent::paintMethodStart($test_name); + + $this->currentTest = $test_name; + } + + function paintMethodEnd($test_name) + { + parent::paintMethodEnd($test_name); + + $this->runCount++; + } + + function paintCaseStart($test_name) + { + parent::paintCaseStart($test_name); + + $this->runCount = 0; + $this->failureCount = 0; + $this->errorCount = 0; + + $this->timer = new Timer(); + $this->timer->start(); + } + + function paintCaseEnd($test_name) + { + parent::paintCaseEnd($test_name); + + $this->timer->stop(); + } + + function paintError($message) + { + parent::paintError($message); + + $this->errorCount++; + } + + function paintFail($message) + { + parent::paintFail($message); + + $this->failureCount++; + } + + function getRunCount() + { + return $this->runCount; + } + + function getFailureCount() + { + return $this->failureCount; + } + + function getErrorCount() + { + return $this->errorCount; + } + + function getTestName() + { + return $this->currentTest; + } + + function getElapsedTime() + { + if ($this->timer) + { + return $this->timer->getElapsedTime(); + } + else + { + return 0; + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php new file mode 100755 index 00000000..e0a78f11 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php @@ -0,0 +1,53 @@ +<?php +/** + * $Id: cd15496bfbce39bfd20fe17d52f9348848df0706 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php'; + +/** + * Prints short summary output of the test to Phing's logging system. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: cd15496bfbce39bfd20fe17d52f9348848df0706 $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestSummaryResultFormatter extends SimpleTestResultFormatter +{ + function paintCaseEnd($test_name) + { + parent::paintCaseEnd($test_name); + + /* Only count suites where more than one test was run */ + if ($this->getRunCount()) + { + $sb.= "Tests run: " . $this->getRunCount(); + $sb.= ", Failures: " . $this->getFailureCount(); + $sb.= ", Errors: " . $this->getErrorCount(); + $sb.= ", Time elapsed: " . $this->getElapsedTime(); + $sb.= " sec\n"; + + if ($this->out != NULL) + { + $this->out->write($sb); + } + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php new file mode 100755 index 00000000..8082e1ee --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php @@ -0,0 +1,264 @@ +<?php +/** + * $Id: d53b946f773798618069fe162d47ac5f6643662a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/Writer.php'; +require_once 'phing/util/LogWriter.php'; + +/** + * Runs SimpleTest tests. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: d53b946f773798618069fe162d47ac5f6643662a $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestTask extends Task +{ + private $formatters = array(); + private $haltonerror = false; + private $haltonfailure = false; + private $failureproperty; + private $errorproperty; + private $printsummary = false; + private $testfailed = false; + private $debug = false; + + /** + * Initialize Task. + * This method includes any necessary SimpleTest libraries and triggers + * appropriate error if they cannot be found. This is not done in header + * because we may want this class to be loaded w/o triggering an error. + */ + function init() { + @include_once 'simpletest/scorer.php'; + + if (!class_exists('SimpleReporter')) { + throw new BuildException("SimpleTestTask depends on SimpleTest package being installed.", $this->getLocation()); + } + + require_once 'simpletest/reporter.php'; + require_once 'simpletest/xml.php'; + require_once 'simpletest/test_case.php'; + require_once 'phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php'; + require_once 'phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php'; + require_once 'phing/tasks/ext/simpletest/SimpleTestFormatterElement.php'; + } + + function setFailureproperty($value) + { + $this->failureproperty = $value; + } + + function setErrorproperty($value) + { + $this->errorproperty = $value; + } + + function setHaltonerror($value) + { + $this->haltonerror = $value; + } + + function setHaltonfailure($value) + { + $this->haltonfailure = $value; + } + + function setPrintsummary($printsummary) + { + $this->printsummary = $printsummary; + } + + public function setDebug($debug) + { + $this->debug = $debug; + } + + public function getDebug() + { + return $this->debug; + } + + /** + * Add a new formatter to all tests of this task. + * + * @param SimpleTestFormatterElement formatter element + */ + function addFormatter(SimpleTestFormatterElement $fe) + { + $this->formatters[] = $fe; + } + + /** + * Add a new fileset containing the XML results to aggregate + * + * @param FileSet the new fileset containing XML results. + */ + function addFileSet(FileSet $fileset) + { + $this->filesets[] = $fileset; + } + + /** + * Iterate over all filesets and return the filename of all files + * that end with .php. + * + * @return array an array of filenames + */ + private function getFilenames() + { + $filenames = array(); + + foreach ($this->filesets as $fileset) + { + $ds = $fileset->getDirectoryScanner($this->project); + $ds->scan(); + + $files = $ds->getIncludedFiles(); + + foreach ($files as $file) + { + if (strstr($file, ".php")) + { + $filenames[] = $ds->getBaseDir() . "/" . $file; + } + } + } + + return $filenames; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $suite= new TestSuite(); + + $filenames = $this->getFilenames(); + + foreach ($filenames as $testfile) + { + $suite->addFile($testfile); + } + + if ($this->debug) + { + $fe = new SimpleTestFormatterElement(); + $fe->setType('debug'); + $fe->setUseFile(false); + $this->formatters[] = $fe; + } + + if ($this->printsummary) + { + $fe = new SimpleTestFormatterElement(); + $fe->setType('summary'); + $fe->setUseFile(false); + $this->formatters[] = $fe; + } + + foreach ($this->formatters as $fe) + { + $formatter = $fe->getFormatter(); + $formatter->setProject($this->getProject()); + + if ($fe->getUseFile()) + { + $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile()); + + $writer = new FileWriter($destFile->getAbsolutePath()); + + $formatter->setOutput($writer); + } + else + { + $formatter->setOutput($this->getDefaultOutput()); + } + } + + $this->execute($suite); + + if ($this->testfailed && $this->formatters[0]->getFormatter() instanceof SimpleTestDebugResultFormatter ) + { + $this->getDefaultOutput()->write("Failed tests: "); + $this->formatters[0]->getFormatter()->printFailingTests(); + } + + if ($this->testfailed) + { + throw new BuildException("One or more tests failed"); + } + } + + private function execute($suite) + { + $counter = new SimpleTestCountResultFormatter(); + $reporter = new MultipleReporter(); + $reporter->attachReporter($counter); + + foreach ($this->formatters as $fe) + { + // SimpleTest 1.0.1 workaround + $formatterList[] = $fe->getFormatter(); + + $reporter->attachReporter(end($formatterList)); + } + + $suite->run($reporter); + + $retcode = $counter->getRetCode(); + + if ($retcode == SimpleTestCountResultFormatter::ERRORS) + { + if ($this->errorproperty) + { + $this->project->setNewProperty($this->errorproperty, true); + } + + if ($this->haltonerror) + { + $this->testfailed = true; + } + } + elseif ($retcode == SimpleTestCountResultFormatter::FAILURES) + { + if ($this->failureproperty) + { + $this->project->setNewProperty($this->failureproperty, true); + } + + if ($this->haltonfailure) + { + $this->testfailed = true; + } + } + } + + private function getDefaultOutput() + { + return new LogWriter($this); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php new file mode 100755 index 00000000..5ffa668b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php @@ -0,0 +1,178 @@ +<?php +/** + * $Id: 03b9f976a961a2688d51c9429087a98c31326f42 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php'; +require_once 'simpletest/xml.php'; + +/** + * Prints plain text output of the test to a specified Writer. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 03b9f976a961a2688d51c9429087a98c31326f42 $ + * @package phing.tasks.ext.simpletest + * @since 2.2.0 + */ +class SimpleTestXmlResultFormatter extends SimpleTestResultFormatter +{ + /** + * @var XmlReporter + */ + private $logger = NULL; + + private $xmlData = ""; + + function __construct() + { + $this->logger = new XmlReporter(); + } + + function getExtension() + { + return ".xml"; + } + + function getPreferredOutfile() + { + return "testsuites"; + } + + private function captureStart() + { + ob_start(); + } + + private function captureStop() + { + $this->xmlData .= ob_get_contents(); + ob_end_clean(); + } + + function paintGroupStart($test_name, $size) + { + parent::paintGroupStart($test_name, $size); + + $this->captureStart(); + $this->logger->paintGroupStart($test_name, $size); + $this->captureStop(); + } + + function paintGroupEnd($test_name) + { + parent::paintGroupEnd($test_name); + + $this->captureStart(); + $this->logger->paintGroupEnd($test_name); + $this->captureStop(); + + if (count($this->_test_stack) == 0) + { + if ($this->out) + { + $this->out->write($this->xmlData); + $this->out->close(); + } + } + } + + function paintCaseStart($test_name) + { + $this->captureStart(); + $this->logger->paintCaseStart($test_name); + $this->captureStop(); + } + + function paintCaseEnd($test_name) + { + $this->captureStart(); + $this->logger->paintCaseEnd($test_name); + $this->captureStop(); + } + + function paintMethodStart($test_name) + { + $this->captureStart(); + $this->logger->paintMethodStart($test_name); + $this->captureStop(); + } + + function paintMethodEnd($test_name) + { + $this->captureStart(); + $this->logger->paintMethodEnd($test_name); + $this->captureStop(); + } + + function paintPass($message) + { + $this->captureStart(); + $this->logger->paintPass($message); + $this->captureStop(); + } + + function paintError($message) + { + $this->captureStart(); + $this->logger->paintError($message); + $this->captureStop(); + } + + function paintFail($message) + { + $this->captureStart(); + $this->logger->paintFail($message); + $this->captureStop(); + } + + function paintException($exception) + { + $this->captureStart(); + $this->logger->paintException($exception); + $this->captureStop(); + } + + function paintSkip($message) + { + $this->captureStart(); + $this->logger->paintSkip($message); + $this->captureStop(); + } + + function paintMessage($message) + { + $this->captureStart(); + $this->logger->paintMessage($message); + $this->captureStop(); + } + + function paintFormattedMessage($message) + { + $this->captureStart(); + $this->logger->paintFormattedMessage($message); + $this->captureStop(); + } + + function paintSignal($type, $payload) + { + $this->captureStart(); + $this->logger->paintSignal($type, $payload); + $this->captureStop(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php new file mode 100755 index 00000000..ed9cb276 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php @@ -0,0 +1,347 @@ +<?php +/* + * $Id: b6c644f650a69cad32ced1d030685a7a7a46251c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/Task.php'; + +/** + * Base class for Subversion tasks + * + * @author Michiel Rook <mrook@php.net> + * @author Andrew Eddie <andrew.eddie@jamboworks.com> + * @version $Id: b6c644f650a69cad32ced1d030685a7a7a46251c $ + * @package phing.tasks.ext.svn + * @see VersionControl_SVN + * @since 2.2.0 + */ +abstract class SvnBaseTask extends Task +{ + private $workingCopy = ""; + + private $repositoryUrl = ""; + + private $svnPath = "/usr/bin/svn"; + + private $svn = NULL; + + private $mode = ""; + + private $svnArgs = array(); + + private $svnSwitches = array(); + + private $toDir = ""; + + protected $fetchMode = VERSIONCONTROL_SVN_FETCHMODE_ASSOC; + + /** + * Initialize Task. + * This method includes any necessary SVN libraries and triggers + * appropriate error if they cannot be found. This is not done in header + * because we may want this class to be loaded w/o triggering an error. + */ + function init() { + include_once 'VersionControl/SVN.php'; + if (!class_exists('VersionControl_SVN')) { + throw new Exception("The SVN tasks depend on PEAR VersionControl_SVN package being installed."); + } + } + + /** + * Sets the path to the workingcopy + */ + function setWorkingCopy($workingCopy) + { + $this->workingCopy = $workingCopy; + } + + /** + * Returns the path to the workingcopy + */ + function getWorkingCopy() + { + return $this->workingCopy; + } + + /** + * Sets the path/URI to the repository + */ + function setRepositoryUrl($repositoryUrl) + { + $this->repositoryUrl = $repositoryUrl; + } + + /** + * Returns the path/URI to the repository + */ + function getRepositoryUrl() + { + return $this->repositoryUrl; + } + + /** + * Sets the path to the SVN executable + */ + function setSvnPath($svnPath) + { + $this->svnPath = $svnPath; + } + + /** + * Returns the path to the SVN executable + */ + function getSvnPath() + { + return $this->svnPath; + } + + // + // Args + // + + /** + * Sets the path to export/checkout to + */ + function setToDir($toDir) + { + $this->toDir = $toDir; + } + + /** + * Returns the path to export/checkout to + */ + function getToDir() + { + return $this->toDir; + } + + // + // Switches + // + + /** + * Sets the force switch + */ + function setForce($value) + { + $this->svnSwitches['force'] = $value; + } + + /** + * Returns the force switch + */ + function getForce() + { + return isset( $this->svnSwitches['force'] ) ? $this->svnSwitches['force'] : ''; + } + + /** + * Sets the username of the user to export + */ + function setUsername($value) + { + $this->svnSwitches['username'] = $value; + } + + /** + * Returns the username + */ + function getUsername() + { + return isset( $this->svnSwitches['username'] ) ? $this->svnSwitches['username'] : ''; + } + + /** + * Sets the password of the user to export + */ + function setPassword($value) + { + $this->svnSwitches['password'] = $value; + } + + /** + * Returns the password + */ + function getPassword() + { + return isset( $this->svnSwitches['password'] ) ? $this->svnSwitches['password'] : ''; + } + + /** + * Sets the no-auth-cache switch + */ + function setNoCache($value) + { + $this->svnSwitches['no-auth-cache'] = $value; + } + + /** + * Returns the no-auth-cache switch + */ + function getNoCache() + { + return isset( $this->svnSwitches['no-auth-cache'] ) ? $this->svnSwitches['no-auth-cache'] : ''; + } + + /** + * Sets the non-recursive switch + */ + function setRecursive($value) + { + $this->svnSwitches['non-recursive'] = is_bool($value) ? !$value : true; + } + + /** + * Returns the non-recursive switch + */ + function getRecursive() + { + return isset( $this->svnSwitches['non-recursive'] ) ? !$this->svnSwitches['non-recursive'] : true; + } + + /** + * Sets the ignore-externals switch + */ + function setIgnoreExternals($value) + { + $this->svnSwitches['ignore-externals'] = $value; + } + + /** + * Returns the ignore-externals switch + */ + function getIgnoreExternals() + { + return isset( $this->svnSwitches['ignore-externals'] ) ? $this->svnSwitches['ignore-externals'] : ''; + } + + /** + * Sets the trust-server-cert switch + */ + public function setTrustServerCert($value) + { + $this->svnSwitches['trust-server-cert'] = $value; + } + + /** + * Returns the trust-server-cert switch + */ + public function getTrustServerCert() + { + return isset($this->svnSwitches['trust-server-cert']) ? $this->svnSwitches['trust-server-cert'] : ''; + } + + /** + * Creates a VersionControl_SVN class based on $mode + * + * @param string The SVN mode to use (info, export, checkout, ...) + * @throws BuildException + */ + protected function setup($mode) + { + $this->mode = $mode; + + // Set up runtime options. Will be passed to all + // subclasses. + $options = array('fetchmode' => $this->fetchMode, 'svn_path' => $this->getSvnPath()); + + // Pass array of subcommands we need to factory + $this->svn = VersionControl_SVN::factory($mode, $options); + + $this->svn->use_escapeshellcmd = false; + + if (!empty($this->repositoryUrl)) + { + $this->svnArgs = array($this->repositoryUrl); + } + else + if (!empty($this->workingCopy)) + { + if (is_dir($this->workingCopy)) + { + if (in_array(".svn", scandir($this->workingCopy))) + { + $this->svnArgs = array($this->workingCopy); + } + else + { + throw new BuildException("'".$this->workingCopy."' doesn't seem to be a working copy"); + } + } + else + if ($mode=='info' ) + { + if (is_file($this->workingCopy)) + { + $this->svnArgs = array($this->workingCopy); + } + else + { + throw new BuildException("'".$this->workingCopy."' is not a directory nor a file"); + } + } + else + { + throw new BuildException("'".$this->workingCopy."' is not a directory"); + } + } + } + + /** + * Executes the constructed VersionControl_SVN instance + * + * @param array Additional arguments to pass to SVN. + * @param array Switches to pass to SVN. + * @return string Output generated by SVN. + */ + protected function run($args = array(), $switches = array()) + { + $svnstack = PEAR_ErrorStack::singleton('VersionControl_SVN'); + + $tempArgs = $this->svnArgs; + + $tempArgs = array_merge($tempArgs, $args); + + $tempSwitches = $this->svnSwitches; + + $tempSwitches = array_merge($tempSwitches, $switches); + + if ($output = $this->svn->run($tempArgs, $tempSwitches)) + { + return $output; + } + else + { + if (count($errs = $svnstack->getErrors())) + { + $err = current($errs); + + $errorMessage = $err['message']; + + if (isset($err['params']['errstr'])) { + $errorMessage = $err['params']['errstr']; + } + + throw new BuildException("Failed to run the 'svn " . $this->mode . "' command: " . $errorMessage); + } + } + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php new file mode 100644 index 00000000..76dc976a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php @@ -0,0 +1,67 @@ +<?php +/** + * $Id: f8844430c9e30d1c603452d8763fbd1114d80051 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Checks out a repository to a local directory + * + * @author Andrew Eddie <andrew.eddie@jamboworks.com> + * @version $Id: f8844430c9e30d1c603452d8763fbd1114d80051 $ + * @package phing.tasks.ext.svn + * @since 2.3.0 + */ +class SvnCheckoutTask extends SvnBaseTask +{ + /** + * Which Revision to Export + * + * @todo check if version_control_svn supports constants + * + * @var string + */ + private $revision = 'HEAD'; + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('checkout'); + + $this->log("Checking out SVN repository to '" . $this->getToDir() . "'". ($this->revision=='HEAD'?'':" (revision: {$this->revision})")); + + // revision + $switches = array( + 'r' => $this->revision, + ); + + $this->run(array($this->getToDir()), $switches); + } + + public function setRevision($revision) + { + $this->revision = $revision; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php new file mode 100644 index 00000000..7eb1ce7a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php @@ -0,0 +1,113 @@ +<?php +/** + * $Id: 6fdb36b57778f5c0cd46110fd36c8c261ced0e86 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Commits changes in a local working copy to the repository + * + * @author Johan Persson <johanp@aditus.nu> + * @version $Id: 6fdb36b57778f5c0cd46110fd36c8c261ced0e86 $ + * @package phing.tasks.ext.svn + * @since 2.4.0 + */ +class SvnCommitTask extends SvnBaseTask +{ + /** + * Commit message + */ + private $message = ''; + + /** + * Property name where we store the revision number of the just + * commited version. + */ + private $propertyName = "svn.committedrevision"; + + /** + * Sets the commit message + */ + function setMessage($message) + { + $this->message = $message; + } + + /** + * Gets the commit message + */ + function getMessage() + { + return $this->message; + } + + /** + * Sets the name of the property to use for returned revision + */ + function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * Returns the name of the property to use for returned revision + */ + function getPropertyName() + { + return $this->propertyName; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + if( trim($this->message) === '' ) + { + throw new BuildException('SVN Commit message can not be empty.'); + } + + $this->setup('commit'); + + $this->log("Commiting SVN working copy at '" . $this->getWorkingCopy() . "' with message '".$this->GetMessage()."'"); + + $output = $this->run(array(), array('message' => $this->GetMessage() ) ); + + if( preg_match('/[\s]*Committed revision[\s]+([\d]+)/', $output, $matches) ) + { + $this->project->setProperty($this->getPropertyName(), $matches[1]); + } + else + { + /** + * If no new revision was committed set revision to "empty". Remember that + * this is not necessarily an error. It could be that the specified working + * copy is identical to to the copy in the repository and in that case + * there will be no update and no new revision number. + */ + $this->project->setProperty($this->getPropertyName(), '' ); + } + + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php new file mode 100644 index 00000000..c1eb7089 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php @@ -0,0 +1,70 @@ +<?php +/** + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Copies a repository from the repository url to another + * + * @version $Id: 6f39598901c83ecaf8e7fcb9d4065f70b38324cb $ + * @package phing.tasks.ext.svn + * @since 2.3.0 + */ +class SvnCopyTask extends SvnBaseTask +{ + private $message = ""; + + /** + * Sets the message + */ + function setMessage($message) + { + $this->message = $message; + } + + /** + * Gets the message + */ + function getMessage() + { + return $this->message; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('copy'); + + $this->log("Copying SVN repository from '" . $this->getRepositoryUrl() . "' to '" . $this->getToDir() . "'"); + + $options = array(); + + if (strlen($this->getMessage()) > 0) { + $options['message'] = $this->getMessage(); + } + + $this->run(array($this->getToDir()), $options); + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnExportTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnExportTask.php new file mode 100755 index 00000000..e0b2e78b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnExportTask.php @@ -0,0 +1,70 @@ +<?php +/** + * $Id: ca2d150a53b870fe410f5434f4d500decc7cda73 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Exports/checks out a repository to a local directory + * with authentication + * + * @author Michiel Rook <mrook@php.net> + * @author Andrew Eddie <andrew.eddie@jamboworks.com> + * @version $Id: ca2d150a53b870fe410f5434f4d500decc7cda73 $ + * @package phing.tasks.ext.svn + * @since 2.2.0 + */ +class SvnExportTask extends SvnBaseTask +{ + /** + * Which Revision to Export + * + * @todo check if version_control_svn supports constants + * + * @var string + */ + private $revision = 'HEAD'; + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('export'); + + $this->log("Exporting SVN repository to '" . $this->getToDir() . "'"); + + $switches = array(); + + if (!empty($this->revision)) { + $switches['r'] = $this->revision; + } + + $this->run(array($this->getToDir()), $switches); + } + + public function setRevision($revision) + { + $this->revision = $revision; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php new file mode 100644 index 00000000..68111d74 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php @@ -0,0 +1,112 @@ +<?php +/** + * $Id: 36ffb2ececed4c83c9ca7ad3674b3fa11074f2a5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Parses the output of 'svn info --xml' and + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 36ffb2ececed4c83c9ca7ad3674b3fa11074f2a5 $ + * @package phing.tasks.ext.svn + * @see VersionControl_SVN + * @since 2.4.9 + */ +class SvnInfoTask extends SvnBaseTask +{ + private $propertyName = "svn.info"; + + private $element = 'url'; + private $subElement = null; + + /** + * Sets the name of the property to use + */ + public function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * Returns the name of the property to use + */ + public function getPropertyName() + { + return $this->propertyName; + } + + /** + * Sets the name of the xml element to use + */ + public function setElement($element) + { + $this->element = $element; + } + + /** + * Returns the name of the xml element to use + */ + public function getElement() + { + return $this->element; + } + + /** + * Sets the name of the xml sub element to use + */ + public function setSubElement($subElement) + { + $this->subElement = $subElement; + } + + /** + * Returns the name of the xml sub element to use + */ + public function getSubElement() + { + return $this->subElement; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('info'); + + $output = $this->run(array('--xml', '--incremental')); + + if ($xmlObj = @simplexml_load_string($output)) { + $object = $xmlObj->{$this->element}; + + if (!empty($this->subElement)) { + $object = $object->{$this->subElement}; + } + + $this->project->setProperty($this->getPropertyName(), (string) $object); + } else { + throw new BuildException("Failed to parse the output of 'svn info --xml'."); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.php new file mode 100755 index 00000000..7305c27b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.php @@ -0,0 +1,120 @@ +<?php +/** + * $Id: 74d61db8c11978a2383f071b7ab7ed0afae6953c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Stores the number of the last revision of a workingcopy in a property + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: 74d61db8c11978a2383f071b7ab7ed0afae6953c $ + * @package phing.tasks.ext.svn + * @see VersionControl_SVN + * @since 2.1.0 + */ +class SvnLastRevisionTask extends SvnBaseTask +{ + private $propertyName = "svn.lastrevision"; + private $forceCompatible = false; + private $lastChanged = false; + + /** + * Sets the name of the property to use + */ + function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * Returns the name of the property to use + */ + function getPropertyName() + { + return $this->propertyName; + } + + /** + * Sets whether to force compatibility with older SVN versions (< 1.2) + */ + public function setForceCompatible($force) + { + $this->forceCompatible = (bool) $force; + } + + /** + * Sets whether to retrieve the last changed revision + */ + public function setLastChanged($lastChanged) + { + $this->lastChanged = (bool) $lastChanged; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('info'); + + if ($this->forceCompatible) + { + $output = $this->run(); + + if ($this->lastChanged) { + $found = preg_match('/Rev:[\s]+([\d]+)/', $output, $matches); + } else { + $found = preg_match('/Last Changed Rev:[\s]+([\d]+)/', $output, $matches); + } + + if ($found) + { + $this->project->setProperty($this->getPropertyName(), $matches[1]); + } + else + { + throw new BuildException("Failed to parse the output of 'svn info'."); + } + } + else + { + $output = $this->run(array('--xml')); + + if ($xmlObj = @simplexml_load_string($output)) + { + if ($this->lastChanged) { + $lastRevision = (int)$xmlObj->entry->commit['revision']; + } else { + $lastRevision = (int)$xmlObj->entry['revision']; + } + + $this->project->setProperty($this->getPropertyName(), $lastRevision); + } + else + { + throw new BuildException("Failed to parse the output of 'svn info --xml'."); + } + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnListTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnListTask.php new file mode 100755 index 00000000..63d8445c --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnListTask.php @@ -0,0 +1,128 @@ +<?php +/** + * $Id: 5dfcc23bc58efaad0eb11ef4964bcd7be0fb99e9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Stores the output of a list command on a workingcopy or repositoryurl in a property. + * This stems from the SvnLastRevisionTask. + * + * @author Anton Stöckl <anton@stoeckl.de> + * @author Michiel Rook <mrook@php.net> (SvnLastRevisionTask) + * @version $Id: 5dfcc23bc58efaad0eb11ef4964bcd7be0fb99e9 $ + * @package phing.tasks.ext.svn + * @see VersionControl_SVN + * @since 2.1.0 + */ +class SvnListTask extends SvnBaseTask +{ + private $propertyName = "svn.list"; + private $forceCompatible = true; + private $limit = null; + private $orderDescending = false; + + /** + * Sets the name of the property to use + */ + function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * Returns the name of the property to use + */ + function getPropertyName() + { + return $this->propertyName; + } + + /** + * Sets whether to force compatibility with older SVN versions (< 1.2) + */ + public function setForceCompatible($force) + { + //$this->forceCompatible = (bool) $force; + // see below, we need this to be true as xml mode does not work + } + + /** + * Sets the max num of tags to display + */ + function setLimit($limit) + { + $this->limit = (int) $limit; + } + + /** + * Sets whether to sort tags in descending order + */ + function setOrderDescending($orderDescending) + { + $this->orderDescending = (bool) $orderDescending; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('list'); + + if ($this->forceCompatible) { + $output = $this->run(array('--verbose')); + $result = null; + + $lines = $output['.']['name']; + + if ($this->orderDescending) { + $lines = array_reverse($lines); + } + + $count = 0; + foreach ($lines as $line) { + if ($this->limit > 0 && $count >= $this->limit) { + break; + } + if (preg_match('@\s+(\d+)\s+(\S+)\s+(\S+ \S+ \S+)\s+(\S+)@', $line, $matches)) { + if ($matches[4] == '.') { + continue; + } + $result .= (!empty($result)) ? "\n" : ''; + $result .= $matches[1] . ' | ' . $matches[2] . ' | ' . $matches[3] . ' | ' . $matches[4]; + $count++; + } + } + + if (!empty($result)) { + $this->project->setProperty($this->getPropertyName(), $result); + } else { + throw new BuildException("Failed to parse the output of 'svn list --verbose'."); + } + } else { + // this is not possible at the moment as SvnBaseTask always uses fetchmode ASSOC + // which transfers everything into nasty assoc array instead of xml + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnLogTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnLogTask.php new file mode 100755 index 00000000..7f5c4025 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnLogTask.php @@ -0,0 +1,108 @@ +<?php +/** + * $Id: fc8bc4cf4caa997c13dd66095997fa5478c47959 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Stores the output of a log command on a workingcopy or repositoryurl in a property. + * This stems from the SvnLastRevisionTask. + * + * @author Anton Stöckl <anton@stoeckl.de> + * @author Michiel Rook <mrook@php.net> (SvnLastRevisionTask) + * @version $Id: fc8bc4cf4caa997c13dd66095997fa5478c47959 $ + * @package phing.tasks.ext.svn + * @see VersionControl_SVN + * @since 2.1.0 + */ +class SvnLogTask extends SvnBaseTask +{ + private $propertyName = "svn.log"; + private $forceCompatible = true; + private $limit = null; + + /** + * Sets the name of the property to use + */ + function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * Returns the name of the property to use + */ + function getPropertyName() + { + return $this->propertyName; + } + + /** + * Sets whether to force compatibility with older SVN versions (< 1.2) + */ + public function setForceCompatible($force) + { + //$this->forceCompatible = (bool) $force; + // see below, we need this to be true as xml mode does not work + } + + /** + * Sets the max num of log entries to get from svn + */ + function setLimit($limit) + { + $this->limit = (int) $limit; + } + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('log'); + + $switches= array(); + if ($this->limit > 0) { + $switches['limit'] = $this->limit; + } + + if ($this->forceCompatible) { + $output = $this->run(array(), $switches); + $result = null; + + foreach ($output as $line) { + $result .= (!empty($result)) ? "\n" : ''; + $result .= "{$line['REVISION']} | {$line['AUTHOR']} | {$line['DATE']} | {$line['MSG']}"; + } + + if (!empty($result)) { + $this->project->setProperty($this->getPropertyName(), $result); + } else { + throw new BuildException("Failed to parse the output of 'svn log'."); + } + } else { + // this is not possible at the moment as SvnBaseTask always uses fetchmode ASSOC + // which transfers everything into nasty assoc array instead of xml + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php new file mode 100644 index 00000000..cb6c5ca4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php @@ -0,0 +1,73 @@ +<?php +/** + * $Id: 2beb14d928ee47f36cceb9467b4a2ac9d2c81ef4 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Switches a repository at a given local directory to a different location + * + * @author Dom Udall <dom.udall@clock.co.uk> + * @version $Id: 2beb14d928ee47f36cceb9467b4a2ac9d2c81ef4 $ + * @package phing.tasks.ext.svn + * @since 2.4.3 + */ +class SvnSwitchTask extends SvnBaseTask +{ + /** + * Which Revision to Export + * + * @todo check if version_control_svn supports constants + * + * @var string + */ + private $revision = 'HEAD'; + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('switch'); + + $this->log("Switching SVN repository at '" . $this->getToDir() . "' to '" . $this->getRepositoryUrl() . "' " + . ($this->getRevision()=='HEAD'?'':" (revision: {$this->getRevision()})")); + + // revision + $switches = array( + 'r' => $this->getRevision(), + ); + + $this->run(array($this->getToDir()), $switches); + } + + public function setRevision($revision) + { + $this->revision = $revision; + } + + public function getRevision() + { + return $this->revision; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.php b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.php new file mode 100644 index 00000000..c70039fc --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.php @@ -0,0 +1,67 @@ +<?php +/** + * $Id: 9f5e4de2948b03eb6c9e459b1065bcedd851c025 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/tasks/ext/svn/SvnBaseTask.php'; + +/** + * Updates a repository in local directory + * + * @author Andrew Eddie <andrew.eddie@jamboworks.com> + * @version $Id: 9f5e4de2948b03eb6c9e459b1065bcedd851c025 $ + * @package phing.tasks.ext.svn + * @since 2.3.0 + */ +class SvnUpdateTask extends SvnBaseTask +{ + /** + * Which Revision to Export + * + * @todo check if version_control_svn supports constants + * + * @var string + */ + private $revision = 'HEAD'; + + /** + * The main entry point + * + * @throws BuildException + */ + function main() + { + $this->setup('update'); + + $this->log("Updating SVN repository at '" . $this->getToDir() . "'". ($this->revision=='HEAD'?'':" (revision: {$this->revision})")); + + // revision + $switches = array( + 'r' => $this->revision, + ); + + $this->run(array($this->getToDir()), $switches); + } + + public function setRevision($revision) + { + $this->revision = $revision; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php b/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php new file mode 100644 index 00000000..33d2e4e3 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php @@ -0,0 +1,510 @@ +<?php + +/* + * $Id: cdbb2883ab70c650896a465a872b3da30f13eb00 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; +include_once 'phing/util/SourceFileScanner.php'; +include_once 'phing/mappers/MergeMapper.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * Encodes files using Zeng Guard Encoder + * + * @author Petr Rybak <petr@rynawe.net> + * @version $Id: cdbb2883ab70c650896a465a872b3da30f13eb00 $ + * @package phing.tasks.ext.zendguard + * @since 2.4.3 + */ +class ZendGuardEncodeTask extends MatchingTask +{ + protected $filesets = array(); + protected $encodeCommand; + + + /** + * TASK PROPERTIES + * + * See http://static.zend.com/topics/Zend-Guard-User-Guidev5x.pdf + * for more information on how to use ZendGuard + * + */ + /** + * Permanently deletes (see warning below) the original source files specified in the + * SourceInputPath and saves the encoded files in its place. + * This option has no option parameter. + * When this option is use, do not use the output_file parameter. + * + * Warning: + * To avoid permanent loss of non-encoded scripts, make a backup. Deleted files + * cannot be restored or recovered and will be permanently deleted with this option. + * If you are unsure about deleting the source files, use the ––rename-source option + * instead + * + * @var bool + */ + protected $deleteSource = true; + /** + * Move the original source file to <input_file>.<renameSourceExt> and save the encoded file in its + * place. + * + * If specified deleteSource will be automatically disabled. + * + * @var string + */ + protected $renameSourceExt = null; + /** + * Turns short PHP tag (“<?” ) recognition either on or off. + * On or off must be specified as an argument when using this option. + * The default, when option is not used in the command-line, is - on + * + * @var bool + */ + protected $shortTags = true; + /** + * Turn ASP tag (“<%” ) recognition on/off. (default: off). On or off must be specified + * as an argument when using this option. + * The default, when this option is not used in the command-line, is - off + * + * @var bool + */ + protected $aspTags = false; + /** + * + * Disables the PHP-compatible header that is added to the top of every encoded file + * by default. Encoded files generated with this option will not display a meaningful + * error when loaded by PHP that doesn't have the Zend Optimizer properly installed. + * Using this option saves approximately 1.5KB for every encoded file. Do not use it + * unless disk space constraints are critica + * + * @var bool + */ + protected $noHeader = false; + /** + * If cryptography should be used to encode the source code + * + * @var bool + */ + protected $useCrypto = false; + /** + * Force cooperation with other encoded files only. This option generates files that + * work exclusively with associated encoded files. Associated encoded files are + * those generated by the same company. Files that do not share the same encoded + * company association cannot call these files + * + * @var bool + */ + protected $encodedOnly = false; + /** + * Allow encoding previously encoded files. (NOT recommended!) + * + * @var bool + */ + protected $forceEncode = false; + /** + * Make an encoded file to expire on the given date. Date is in yyyy-mm-dd format. + * + * @var string + */ + protected $expires = null; + /** + * Level of obfuscation. Defaults to 0 (no obfuscation). + * + * @var int + */ + protected $obfuscationLevel = 0; + /** + * Optimization mask. (default value: [+++++++]) + * opt_mask is an integer representing a bit-mask. + * The default value enables all of the optimization passes. + * Each optimization pass of the Zend Optimizer can be turned on or off based on + * the mask entered + * + * @var int + */ + protected $optMask = null; + /** + * Path to the zend encoder binary + * + * @var string + */ + protected $zendEncoderPath = null; + /** + * Path to private key for licensing + * + * @var string + */ + protected $privateKeyPath = null; + /** + * Enable licensing. + * If enabled, productName must be defined. + * + * @var bool + */ + protected $licenseProduct = false; + /** + * If true the ownership, permissions and timestamps + * of the encoded files won't be preserved. + * + * @var bool + */ + protected $ignoreFileModes = false; + /** + * Enable signing + * If enabled, productName must be defined. + * + * @var bool + */ + protected $signProduct = false; + /** + * Product name. Must be defined if licenseProduct + * or signProduct is set to 1 + * + * @var string + */ + protected $productName = null; + /** + * Embed the information in the specified file into the header of the encoded file + * (overrides noHeader) + * + * @var string + */ + protected $prologFile = null; + + /** + * TASK PROPERTIES SETTERS + */ + public function setZendEncoderPath($value) + { + $this->zendEncoderPath = $value; + } + + public function setPrivateKeyPath($value) + { + $this->privateKeyPath = $value; + } + + public function setShortTags($value) + { + $this->shortTags = (bool) $value; + } + + public function setAspTags($value) + { + $this->aspTags = (bool) $value; + } + + public function setDeleteSource($value) + { + $this->shortTags = (bool) $value; + } + + public function setUseCrypto($value) + { + $this->useCrypto = (bool) $value; + } + + public function setObfuscationLevel($value) + { + $this->obfuscationLevel = (int) $value; + } + + public function setLicenseProduct($value) + { + $this->licenseProduct = (bool) $value; + } + + public function setPrologFile($value) + { + $this->prologFile = $value; + } + + public function setSignProduct($value) + { + $this->signProduct = (bool) $value; + } + + public function setForceEncode($value) + { + $this->forceEncode = (bool) $value; + } + + public function setEncodedOnly($value) + { + $this->encodedOnly = (bool) $value; + } + + public function setIgnoreFileModes($value) + { + $this->ignoreFileModes = (bool) $value; + } + + public function setExpires($value) + { + $this->expires = $value; + } + + public function setProductName($value) + { + $this->productName = $value; + } + + public function setOptMask($value) + { + $this->optMask = (int) $value; + } + + public function setRenameSourceExt($value) + { + $this->renameSourceExt = $value; + } + + public function setNoHeader($value) + { + $this->noHeader = (bool) $value; + } + + /** + * Add a new fileset. + * + * @return FileSet + */ + public function createFileSet() + { + $this->fileset = new ZendGuardFileSet(); + $this->filesets[] = $this->fileset; + return $this->fileset; + } + + /** + * Verifies that the configuration is correct + * + * @throws BuildException + */ + protected function verifyConfiguration() + { + // Check that the zend encoder path is specified + if (empty($this->zendEncoderPath)) { + throw new BuildException("Zend Encoder path must be specified"); + } + + // verify that the zend encoder binary exists + if (!file_exists($this->zendEncoderPath)) { + throw new BuildException("Zend Encoder not found on path " . $this->zendEncoderPath); + } + + // if either sign or license is required the private key path needs to be defined + // and the file has to exist and product name has to be specified + if ($this->signProduct || $this->licenseProduct) { + if (empty($this->privateKeyPath)) { + throw new BuildException("Licensing or signing requested but privateKeyPath not provided."); + } + if (!is_readable($this->privateKeyPath)) { + throw new BuildException("Licensing or signing requested but private key path doesn't exist or is unreadable."); + } + if (empty($this->productName)) { + throw new BuildException("Licensing or signing requested but product name not provided."); + } + } + + // verify prolog file exists + if (!empty($this->prologFile)) { + if (!file_exists($this->prologFile)) { + throw new BuildException("The prolog file doesn't exist: " . $this->prologFile); + } + } + } + + /** + * Do the work + * + * @throws BuildException + */ + public function main() + { + $this->verifyConfiguration(); + $this->prepareEncoderCommand(); + + try { + if (empty($this->filesets)) { + throw new BuildException("You must supply nested fileset.", + $this->getLocation()); + } + + $encodedFilesCounter = 0; + + foreach ($this->filesets as $fs) { + /* @var $fs FileSet */ + + /* @var $fsBasedir PhingFile */ + $fsBasedir = $fs->getDir($this->project)->getAbsolutePath(); + + $files = $fs->getFiles($this->project, false); + + foreach ($files as $file) { + $f = new PhingFile($fsBasedir, $file); + + if ($f->isFile()) { + $path = $f->getAbsolutePath(); + + $this->log("Encoding " . $path, Project::MSG_VERBOSE); + $this->encodeFile($path); + + $encodedFilesCounter++; + } + } + } + + $this->log("Encoded files: " . $encodedFilesCounter); + } catch (IOException $ioe) { + $msg = "Problem encoding files: " . $ioe->getMessage(); + throw new BuildException($msg, $ioe, $this->getLocation()); + } + } + + /** + * Prepares the main part of the command that will be + * used to encode the given file(s). + */ + protected function prepareEncoderCommand() + { + $command = $this->zendEncoderPath . " \\\n"; + + if (!empty($this->renameSourceExt)) { + $command .= " --rename-source " . $this->renameSourceExt . " \\\n"; + } elseif ($this->deleteSource) { + // delete source + $command .= " --delete-source \\\n"; + } + + // short tags + $command .= " --short-tags " . (($this->shortTags) ? 'on' : 'off') . " \\\n"; + + // asp tags + $command .= " --asp-tags " . (($this->aspTags) ? 'on' : 'off') . " \\\n"; + + // use crypto + if ($this->useCrypto) { + $command .= " --use-crypto \\\n"; + } + + // ignore file modes + if ($this->ignoreFileModes) { + $command .= " --ignore-file-modes \\\n"; + } + + // force encode + if ($this->forceEncode) { + $command .= " --force-encode \\\n"; + } + + // expires + if (!empty($this->expires)) { + $command .= " --expires " . $this->expires . " \\\n"; + } + + // insert prolog file name or no-header + if (!empty($this->prologFile)) { + $command .= " --prolog-filename " . $this->prologFile . " \\\n"; + } elseif ($this->noHeader) { + // no-header + $command .= " --no-header \\\n"; + } + + // obfuscation level + if ($this->obfuscationLevel > 0) { + $command .= " --obfuscation-level " . $this->obfuscationLevel . " \\\n"; + } + + // encoded only + if ($this->encodedOnly) { + $command .= " --encoded-only \\\n"; + } + + // opt mask + if (null !== $this->optMask) { + $command .= " --optimizations " . $this->optMask . " \\\n"; + } + + // Signing or licensing + if ($this->signProduct) { + $command .= " --sign-product " . $this->productName . " --private-key " . $this->privateKeyPath . " \\\n"; + } elseif ($this->licenseProduct) { + $command .= " --license-product " . $this->productName . " --private-key " . $this->privateKeyPath . " \\\n"; + } + + // add a blank space + $command .= " "; + + $this->encodeCommand = $command; + + } + + /** + * Encodes a file using currently defined Zend Guard settings + * + * @param string $filePath Path to the encoded file + */ + protected function encodeFile($filePath) + { + $command = $this->encodeCommand . $filePath . ' 2>&1'; + + $this->log('Running: ' . $command, Project::MSG_VERBOSE); + + $tmp = exec($command, $output, $return_var); + if ($return_var !== 0) { + throw new BuildException("Encoding failed. \n Msg: " . $tmp . " \n Encode command: " . $command); + } + + return true; + } + +} + +/** + * This is a FileSet with the to specify permissions. + * + * Permissions are currently not implemented by PEAR Archive_Tar, + * but hopefully they will be in the future. + * + * @package phing.tasks.ext.zendguard + */ +class ZendGuardFileSet extends FileSet +{ + private $files = null; + + /** + * Get a list of files and directories specified in the fileset. + * @return array a list of file and directory names, relative to + * the baseDir for the project. + */ + public function getFiles(Project $p, $includeEmpty = true) + { + + if ($this->files === null) { + + $ds = $this->getDirectoryScanner($p); + $this->files = $ds->getIncludedFiles(); + } // if ($this->files===null) + + return $this->files; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.php b/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.php new file mode 100644 index 00000000..2a3f71ca --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.php @@ -0,0 +1,524 @@ +<?php + +/* + * $Id: 96af59b9cbecaf7f146dffab1d0b5a806a56b47f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Produce license files using Zeng Guard. + * The task can produce a license file from the given + * license properties or it can use a template. + * + * @author Petr Rybak <petr@rynawe.net> + * @version $Id: 96af59b9cbecaf7f146dffab1d0b5a806a56b47f $ + * @package phing.tasks.ext.zendguard + * @since 2.4.3 + */ +class ZendGuardLicenseTask extends Task +{ + protected $zendsignCommand; + private $tmpLicensePath; + + /** + * TASK PROPERTIES + * + * See http://static.zend.com/topics/Zend-Guard-User-Guidev5x.pdf + * for more information on how to use ZendGuard + * + */ + /** + * Path to Zend Guard zendenc_sign executable + * + * @var string + */ + protected $zendsignPath; + /** + * Path to private key that will be used to sign the license + * + * @var string + */ + protected $privateKeyPath; + /** + * Where to store the signed license file + * + * @var string + */ + protected $outputFile; + /** + * Path to license template. If specified all + * license properties will be ignored and the + * template will be used to generate the file. + * + * @var string + */ + protected $licenseTemplate; + /** + * The name assigned to Product. This must be the same name used when encoding + * the PHP files. + * + * REQUIRED + * + * @var string + */ + protected $productName; + /** + * The Name of the Registered owner of the license. + * + * REQUIRED + * + * @var string + */ + protected $registeredTo; + /** + * Expiration date of the license. Used if the license is issued with a date restriction. + * Possible values: + * - 'Never', '0' or false: the license won't expire + * - A Date in format DD-MM-YYYY to set expiration for that date + * - Relative date supported by the PHP strtotime function (e.g. +1 month) + * + * REQUIRED + * + * @var string + */ + protected $expires; + /** + * Limits the use of the license to IP addresses that fall within specification. Supports + * wildcards for any of the IP place holders, as well as the two types of net masks + * (filters). + * Netmask pair An IP a.b.c.d, and a netmask w.x.y.z. (That is., 10.1.0.0/255.255.0.0), + * where the binary of mask is applied to filter IP addresses. + * ip/nnn (similar to a CIDR specification) This mask consists of nnn high-order 1 bits. + * (That is, 10.1.0.0/16 is the same as 10.1.0.0/255.255.0.0). Instead of spelling out + * the bits of the subnet mask, this mask notation is simply listed as the number of 1s + * bits that start the mask. Rather than writing the address and subnet mask as + * 192.60.128.0/255.255.252.0 the network address would be written simply as: + * 192.60.128.0/22 which indicates starting address of the network and number of 1s + * bits (22) in the network portion of the address. The mask in binary is + * (11111111.11111111.11111100.00000000). + * + * OPTIONAL + * + * Example (Wildcard): + * IP-Range = 10.1.*.* + * Example (Net Mask): + * IP-Range = 10.1.0.0/255.255.0.0 + * Example (Net Mask): + * IP-Range = 10.1.0.0/16 + * + * @var string + */ + protected $ipRange; + /** + * Coded string (Zend Host ID) used to lock the license to a specific hardware. The + * Zend Host ID obtained from the machine where the encoded files and license are + * to be installed. The Zend Host ID code can be obtained by using the zendid utility. + * For more details, see Getting the Zend Host ID. + * + * REQUIRED if Hardware-Locked is set equal to YES. + * Meaningless if Hardware-Locked is set equal to NO. + * + * User semicolon to enter more than one Host-ID + * + * Example: + * Host-ID = H:MFM43-Q9CXC-B9EDX-GWYSU;H:MFM43-Q9CXC-B9EDX-GWYTY + * + * @var string + */ + protected $hostID; + /** + * Option that indicates if the license will be locked to a specific machine + * using the Zend Host ID code(s). If set to YES, the Host-ID is required. + * + * OPTIONAL + * + * @var bool + */ + protected $hardwareLocked; + /** + * Semi-colon separated user defined values that will be part of the license. These values + * CANNOT be modified after the license is produced. Modification + * would invalidate the license. + * + * OPTIONAL + * Example: + * Tea=Mint Flavor;Coffee=Arabica + * + * @var string + */ + protected $userDefinedValues; + /** + * Semi-colon separated user defined x-values that will be part of the license. These values + * CAN be modified after the license is produced. Modification + * won't invalidate the license. + * + * OPTIONAL + * Example: + * Tea=Mint Flavor;Coffee=Arabica + * + * @var string + */ + protected $xUserDefinedValues; + + + public function setLicenseTemplate($value) + { + $this->licenseTemplate = $value; + } + + public function setProductName($productName) + { + $this->productName = $productName; + } + + public function setRegisteredTo($registeredTo) + { + $this->registeredTo = $registeredTo; + } + + /** + * Process the expires property. If the value is + * empty (false, '', ...) it will set the value to 'Never' + * Otherwise it will run the value through strtotime so relative + * date and time notation can be used (e.g. +1 month) + * + * @param mixed $expires + * + * @return string + */ + public function setExpires($expires) + { + // process the expires value + if (false === $expires || '0' === $expires || strtolower($expires) == 'never' || '' === $expires) { + $this->expires = 'Never'; + } else { + $time = strtotime($expires); + if (!$time) { + throw new BuildException("Unsupported expires format: " . $expires); + } + $this->expires = date('d-M-Y', $time); + } + } + + public function setIpRange($iprange) + { + $this->ipRange = $iprange; + } + + public function setHostID($hostID) + { + $this->hostID = $hostID; + } + + public function setHardwareLocked($hardwareLocked) + { + $this->hardwareLocked = (bool) $hardwareLocked; + } + + public function setUserDefinedValues($userDefinedValues) + { + $this->userDefinedValues = $userDefinedValues; + } + + public function setXUserDefinedValues($xUserDefinedValues) + { + $this->xUserDefinedValues = $xUserDefinedValues; + } + + public function setZendsignPath($zendsignPath) + { + $this->zendsignPath = $zendsignPath; + } + + public function setPrivateKeyPath($privateKeyPath) + { + $this->privateKeyPath = $privateKeyPath; + } + + public function setOutputFile($outputFile) + { + $this->outputFile = $outputFile; + } + + /** + * Verifies that the configuration is correct + * + * @throws BuildException + */ + protected function verifyConfiguration() + { + // Check that the zend encoder path is specified + if (empty($this->zendsignPath)) { + throw new BuildException("Zendenc_sign path must be specified"); + } + // verify that the zend encoder binary exists + if (!file_exists($this->zendsignPath)) { + throw new BuildException("Zendenc_sign not found on path " . $this->zendsignPath); + } + + // verify that the private key path is defined + if (empty($this->privateKeyPath)) { + throw new BuildException("You must define privateKeyPath."); + } + // verify that the private key file is readable + if (!is_readable($this->privateKeyPath)) { + throw new BuildException("Private key file is not readable: " . $this->privateKeyPath); + } + + // if template is passed, verify that it is readable + if (!empty($this->licenseTemplate)) { + if (!is_readable($this->licenseTemplate)) { + throw new BuildException("License template file is not readable " . $this->licenseTemplate); + } + } + + // check that output file path is defined + if (empty($this->outputFile)) { + throw new BuildException("Path where to store the result file needs to be defined in outputFile property"); + } + + // if license template is NOT provided check that all required parameters are defined + if (empty($this->licenseTemplate)) { + + // check productName + if (empty($this->productName)) { + throw new BuildException("Property must be defined: productName"); + } + + // check expires + if (null === $this->expires) { + throw new BuildException("Property must be defined: expires"); + } + + // check registeredTo + if (empty($this->registeredTo)) { + throw new BuildException("Property must be defined: registeredTo"); + } + + // check hardwareLocked + if (null === $this->hardwareLocked) { + throw new BuildException("Property must be defined: hardwareLocked"); + } + + // if hardwareLocked is set to true, check that Host-ID is set + if ($this->hardwareLocked) { + if (empty($this->hostID)) { + throw new BuildException("If you set hardwareLocked to true hostID must be provided"); + } + } + } + } + + /** + * Do the work + * + * @throws BuildException + */ + public function main() + { + try { + $this->verifyConfiguration(); + + $this->generateLicense(); + } catch (Exception $e) { + // remove the license temp file if it was created + $this->cleanupTmpFiles(); + + throw $e; + } + $this->cleanupTmpFiles(); + } + + /** + * If temporary license file was created during the process + * this will remove it + * + * @return void + */ + private function cleanupTmpFiles() + { + if (!empty($this->tmpLicensePath) && file_exists($this->tmpLicensePath)) { + $this->log("Deleting temporary license template " . $this->tmpLicensePath, Project::MSG_VERBOSE); + + unlink($this->tmpLicensePath); + } + } + + /** + * Prepares and returns the command that will be + * used to create the license. + * + * @return string + */ + protected function prepareSignCommand() + { + $command = $this->zendsignPath; + + // add license path + $command .= ' ' . $this->getLicenseTemplatePath(); + + // add result file path + $command .= ' ' . $this->outputFile; + + // add key path + $command .= ' ' . $this->privateKeyPath; + + + $this->zendsignCommand = $command; + + return $command; + } + + /** + * Checks if the license template path is defined + * and returns it. + * If it the license template path is not defined + * it will generate a temporary template file and + * provide it as a template. + * + * @return string + */ + protected function getLicenseTemplatePath() + { + if (!empty($this->licenseTemplate)) { + return $this->licenseTemplate; + } else { + return $this->generateLicenseTemplate(); + } + } + + /** + * Creates the signed license at the defined output path + * + * @return void + */ + protected function generateLicense() + { + $command = $this->prepareSignCommand() . ' 2>&1'; + + $this->log('Creating license at ' . $this->outputFile); + + $this->log('Running: ' . $command, Project::MSG_VERBOSE); + $tmp = exec($command, $output, $return_var); + + // Check for exit value 1. Zendenc_sign command for some reason + // returns 0 in case of failure and 1 in case of success... + if ($return_var !== 1) { + throw new BuildException("Creating license failed. \n\nZendenc_sign msg:\n" . join("\n", $output) . "\n\n"); + } + } + + /** + * It will generate a temporary license template + * based on the properties defined. + * + * @return string Path of the temporary license template file + */ + protected function generateLicenseTemplate() + { + $this->tmpLicensePath = tempnam(sys_get_temp_dir(), 'zendlicense'); + + $this->log("Creating temporary license template " . $this->tmpLicensePath, Project::MSG_VERBOSE); + if (file_put_contents($this->tmpLicensePath, $this->generateLicenseTemplateContent()) === false) { + throw new BuildException("Unable to create temporary template license file: " . $this->tmpLicensePath); + } + + return $this->tmpLicensePath; + } + + /** + * Generates license template content based + * on the defined parameters + * + * @return string + */ + protected function generateLicenseTemplateContent() + { + $contentArr = array(); + + // Product Name + $contentArr[] = array('Product-Name', $this->productName); + // Registered to + $contentArr[] = array('Registered-To', $this->registeredTo); + // Hardware locked + $contentArr[] = array('Hardware-Locked', ($this->hardwareLocked ? 'Yes' : 'No')); + + // Expires + $contentArr[] = array('Expires', $this->expires); + + // IP-Range + if (!empty($this->ipRange)) { + $contentArr[] = array('IP-Range', $this->ipRange); + } + // Host-ID + if (!empty($this->hostID)) { + foreach (explode(';', $this->hostID) as $hostID) { + $contentArr[] = array('Host-ID', $hostID); + } + } else { + $contentArr[] = array('Host-ID', 'Not-Locked'); + } + + // parse user defined fields + if (!empty($this->userDefinedValues)) { + $this->parseAndAddUserDefinedValues($this->userDefinedValues, $contentArr); + } + // parse user defined x-fields + if (!empty($this->xUserDefinedValues)) { + $this->parseAndAddUserDefinedValues($this->xUserDefinedValues, $contentArr, 'X-'); + } + + // merge all the values + $content = ''; + foreach ($contentArr as $valuePair) { + + list($key, $value) = $valuePair; + + $content .= $key . " = " . $value . "\n"; + } + + return $content; + } + + /** + * Parse the given string in format like key1=value1;key2=value2;... and + * converts it to array + * (key1=>value1, key2=value2, ...) + * + * @param stirng $valueString Semi-colon separated value pairs + * @param array $valueArray Array to which the values will be added + * @param string $keyPrefix Prefix to use when adding the key + * + * @return void + */ + protected function parseAndAddUserDefinedValues($valueString, array &$valueArray, $keyPrefix = '', + $pairSeparator = ';') + { + // explode the valueString (semicolon) + $valuePairs = explode($pairSeparator, $valueString); + if (!empty($valuePairs)) { + foreach ($valuePairs as $valuePair) { + list($key, $value) = explode('=', $valuePair, 2); + + // add pair into the valueArray + $valueArray[] = array($keyPrefix . $key, $value); + } + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/AdhocTask.php b/buildscripts/phing/classes/phing/tasks/system/AdhocTask.php new file mode 100755 index 00000000..ce0ca210 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/AdhocTask.php @@ -0,0 +1,88 @@ +<?php +/* + * $Id: 03a742eb03e5e1515c297d941970a86b00e1f69e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Abstract class for creating adhoc Phing components in buildfile. + * + * By itself this class can be used to declare a single class within your buildfile. + * You can then reference this class in any task that takes custom classes (selectors, + * mappers, filters, etc.) + * + * Subclasses exist for conveniently declaring and registering tasks and types. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class AdhocTask extends Task { + + /** + * The PHP script + * @var string + */ + protected $script; + + protected $newClasses = array(); + + /** + * Main entry point + */ + public function main() { + $this->execute(); + if ($this->newClasses) { + foreach($this->newClasses as $classname) { + $this->log("Added adhoc class " . $classname, Project::MSG_VERBOSE); + } + } else { + $this->log("Adhoc task executed but did not result in any new classes.", Project::MSG_VERBOSE); + } + } + + /** + * Get array of names of newly defined classes. + * @return array + */ + protected function getNewClasses() { + return $this->newClasses; + } + + /** + * Load the adhoc class, and perform any core validation. + * @return string The classname of the ProjectComponent class. + * @throws BuildException - if more than one class is defined. + */ + protected function execute() { + $classes = get_declared_classes(); + eval($this->script); + $this->newClasses = array_diff(get_declared_classes(), $classes); + } + + /** + * Set the script. + * @param string $script + */ + public function addText($script) { + $this->script = $script; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/AdhocTaskdefTask.php b/buildscripts/phing/classes/phing/tasks/system/AdhocTaskdefTask.php new file mode 100755 index 00000000..03f80b27 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/AdhocTaskdefTask.php @@ -0,0 +1,103 @@ +<?php + +/* + * $Id: da14cd0fdc73a0eb13ecc8c0b5ae693550a052b1 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/AdhocTask.php'; + +/** + * A class for creating adhoc tasks in build file. + * + * <target name="test-adhoc"> + * <adhoc-task name="foo"><![CDATA[ + * + * class FooTest extends Task { + * private $bar; + * + * function setBar($bar) { + * $this->bar = $bar; + * } + * + * function main() { + * $this->log("In FooTest: " . $this->bar); + * } + * } + * + * ]]></adhoc-task> + * + * <foo bar="B.L.I.N.G"/> + * </target> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class AdhocTaskdefTask extends AdhocTask +{ + + /** + * The tag that refers to this task. + */ + private $name; + + /** + * Set the tag that will represent this adhoc task/type. + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** Main entry point */ + public function main() + { + if ($this->name === null) + { + throw new BuildException("The name attribute is required for adhoc task definition.",$this->location); + } + + $taskdefs = $this->getProject()->getTaskDefinitions(); + + if (!isset($taskdefs[$this->name])) + { + $this->execute(); + + $classes = $this->getNewClasses(); + + if (count($classes) < 1) + { + throw new BuildException("You must define at least one class for AdhocTaskdefTask."); + } + + $classname = array_pop($classes); + + // instantiate it to make sure it is an instance of Task + $t = new $classname(); + if (!($t instanceof Task)) + { + throw new BuildException("The adhoc class you defined must be an instance of phing.Task", $this->location); + } + + $this->log("Task " . $this->name . " will be handled by class " . $classname, Project::MSG_VERBOSE); + $this->project->addTaskDefinition($this->name, $classname); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/AdhocTypedefTask.php b/buildscripts/phing/classes/phing/tasks/system/AdhocTypedefTask.php new file mode 100755 index 00000000..80be2fc9 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/AdhocTypedefTask.php @@ -0,0 +1,71 @@ +<?php + +/* + * $Id: 679ed701a01fef022a2da8ff5db16cff658b77e6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/AdhocTask.php'; + +/** + * A class for creating adhoc datatypes in build file. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class AdhocTypedefTask extends AdhocTask { + + /** + * The tag that refers to this task. + */ + private $name; + + /** + * Set the tag that will represent this adhoc task/type. + * @param string $name + */ + public function setName($name) { + $this->name = $name; + } + + /** Main entry point */ + public function main() { + + if ($this->name === null) { + throw new BuildException("The name attribute is required for adhoc task definition.",$this->location); + } + + $this->execute(); + + $classes = $this->getNewClasses(); + if (count($classes) !== 1) { + throw new BuildException("You must define one (and only one) class for AdhocTypedefTask."); + } + $classname = array_shift($classes); + + // instantiate it to make sure it is an instance of ProjectComponent + $t = new $classname(); + if (!($t instanceof ProjectComponent)) { + throw new BuildException("The adhoc class you defined must be an instance of phing.ProjectComponent", $this->location); + } + + $this->log("Datatype " . $this->name . " will be handled by class " . $classname, Project::MSG_VERBOSE); + $this->project->addDataTypeDefinition($this->name, $classname); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/AppendTask.php b/buildscripts/phing/classes/phing/tasks/system/AppendTask.php new file mode 100755 index 00000000..a7c4dcda --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/AppendTask.php @@ -0,0 +1,240 @@ +<?php +/* + * $Id: 056086ae305f5447baee1a4c7b1ad59ceb8cf50b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/types/FileList.php'; +include_once 'phing/types/FileSet.php'; + +/** + * Appends text, contents of a file or set of files defined by a filelist to a destination file. + * + * <code> + * <append text="And another thing\n" destfile="badthings.log"/> + * </code> + * OR + * <code> + * <append file="header.html" destfile="fullpage.html"/> + * <append file="body.html" destfile="fullpage.html"/> + * <append file="footer.html" destfile="fullpage.html"/> + * </code> + * OR + * <code> + * <append destfile="${process.outputfile}"> + * <filterchain> + * <xsltfilter style="${process.stylesheet}"> + * <param name="mode" expression="${process.xslt.mode}"/> + * <param name="file_name" expression="%{task.append.current_file.basename}"/> <!-- Example of using a RegisterSlot variable --> + * </xsltfilter> + * </filterchain> + * <filelist dir="book/" listfile="book/PhingGuide.book"/> + * </append> + * </code> + * @package phing.tasks.system + * @version $Id$ + */ +class AppendTask extends Task { + + /** Append stuff to this file. */ + private $to; + + /** Explicit file to append. */ + private $file; + + /** Any filesets of files that should be appended. */ + private $filesets = array(); + + /** Any filelists of files that should be appended. */ + private $filelists = array(); + + /** Any filters to be applied before append happens. */ + private $filterChains = array(); + + /** Text to append. (cannot be used in conjunction w/ files or filesets) */ + private $text; + + /** Sets specific file to append. */ + function setFile(PhingFile $f) { + $this->file = $f; + } + + /** + * Set target file to append to. + * @deprecated Will be removed with final release. + */ + function setTo(PhingFile $f) { + $this->log("The 'to' attribute is deprecated in favor of 'destFile'; please update your code.", Project::MSG_WARN); + $this->to = $f; + } + + /** + * The more conventional naming for method to set destination file. + * @param PhingFile $f + */ + function setDestFile(PhingFile $f) { + $this->to = $f; + } + + /** + * Supports embedded <filelist> element. + * @return FileList + */ + function createFileList() { + $num = array_push($this->filelists, new FileList()); + return $this->filelists[$num-1]; + } + + /** + * Nested creator, adds a set of files (nested <fileset> attribute). + * This is for when you don't care what order files get appended. + * @return FileSet + */ + function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Creates a filterchain + * + * @return FilterChain The created filterchain object + */ + function createFilterChain() { + $num = array_push($this->filterChains, new FilterChain($this->project)); + return $this->filterChains[$num-1]; + } + + /** + * Sets text to append. (cannot be used in conjunction w/ files or filesets). + * @param string $txt + */ + function setText($txt) { + $this->text = (string) $txt; + } + + /** + * Sets text to append. Supports CDATA. + * @param string $txt + */ + function addText($txt) { + $this->text = (string) $txt; + } + + + /** Append the file(s). */ + function main() { + + if ($this->to === null) { + throw new BuildException("You must specify the 'destFile' attribute"); + } + + if ($this->file === null && empty($this->filelists) && empty($this->filesets) && $this->text === null) { + throw new BuildException("You must specify a file, use a filelist, or specify a text value."); + } + + if ($this->text !== null && ($this->file !== null || !empty($this->filelists))) { + throw new BuildException("Cannot use text attribute in conjunction with file or filelists."); + } + + // create a filwriter to append to "to" file. + $writer = new FileWriter($this->to, $append=true); + + if ($this->text !== null) { + + // simply append the text + $this->log("Appending string to " . $this->to->getPath()); + + // for debugging primarily, maybe comment + // out for better performance(?) + $lines = explode("\n", $this->text); + foreach($lines as $line) { + $this->log($line, Project::MSG_VERBOSE); + } + + $writer->write($this->text); + + } else { + + // append explicitly-specified file + if ($this->file !== null) { + try { + $this->appendFile($writer, $this->file); + } catch (Exception $ioe) { + $this->log("Unable to append contents of file " . $this->file->getAbsolutePath() . ": " . $ioe->getMessage(), Project::MSG_WARN); + } + } + + // append the files in the filelists + foreach($this->filelists as $fl) { + try { + $files = $fl->getFiles($this->project); + $this->appendFiles($writer, $files, $fl->getDir($this->project)); + } catch (BuildException $be) { + $this->log($be->getMessage(), Project::MSG_WARN); + } + } + + // append any files in filesets + foreach($this->filesets as $fs) { + try { + $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles(); + $this->appendFiles($writer, $files, $fs->getDir($this->project)); + } catch (BuildException $be) { + $this->log($be->getMessage(), Project::MSG_WARN); + } + } + + } // if ($text ) {} else {} + + $writer->close(); + } + + /** + * Append an array of files in a directory. + * @param FileWriter $writer The FileWriter that is appending to target file. + * @param array $files array of files to delete; can be of zero length + * @param PhingFile $dir directory to work from + */ + private function appendFiles(FileWriter $writer, $files, PhingFile $dir) { + if (!empty($files)) { + $this->log("Attempting to append " . count($files) . " files" .($dir !== null ? ", using basedir " . $dir->getPath(): "")); + $basenameSlot = Register::getSlot("task.append.current_file"); + $pathSlot = Register::getSlot("task.append.current_file.path"); + foreach($files as $filename) { + try { + $f = new PhingFile($dir, $filename); + $basenameSlot->setValue($filename); + $pathSlot->setValue($f->getPath()); + $this->appendFile($writer, $f); + } catch (Exception $ioe) { + $this->log("Unable to append contents of file " . $f->getAbsolutePath() . ": " . $ioe->getMessage(), Project::MSG_WARN); + } + } + } // if !empty + } + + private function appendFile(FileWriter $writer, PhingFile $f) { + $in = FileUtils::getChainedReader(new FileReader($f), $this->filterChains, $this->project); + while(-1 !== ($buffer = $in->read())) { // -1 indicates EOF + $writer->write($buffer); + } + $this->log("Appending contents of " . $f->getPath() . " to " . $this->to->getPath()); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/AvailableTask.php b/buildscripts/phing/classes/phing/tasks/system/AvailableTask.php new file mode 100755 index 00000000..bd5701e4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/AvailableTask.php @@ -0,0 +1,172 @@ +<?php +/* + * $Id: 7388bc66c9574987edd02ad3b2d6f14462f6b157 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/tasks/system/condition/ConditionBase.php'; + +/** + * <available> task. + * + * Note: implements condition interface (see condition/Condition.php) + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.tasks.system + */ +class AvailableTask extends Task { + + /** Property to check for. */ + private $property; + + /** Value property should be set to. */ + private $value = "true"; + + /** Resource to check for */ + private $resource; + + private $type = null; + private $filepath = null; + + private $followSymlinks = false; + + function setProperty($property) { + $this->property = (string) $property; + } + + function setValue($value) { + $this->value = (string) $value; + } + + function setFile(PhingFile $file) { + $this->file = $file; + } + + function setResource($resource) { + $this->resource = (string) $resource; + } + + function setType($type) { + $this->type = (string) strtolower($type); + } + + public function setFollowSymlinks($followSymlinks) + { + $this->followSymlinks = (bool) $followSymlinks; + } + + /** + * Set the path to use when looking for a file. + * + * @param Path $filepath a Path instance containing the search path for files. + */ + public function setFilepath(Path $filepath) { + if ($this->filepath === null) { + $this->filepath = $filepath; + } else { + $this->filepath->append($filepath); + } + } + + /** + * Creates a path to be configured + * + * @return Path + */ + public function createFilepath() { + if ($this->filepath === null) { + $this->filepath = new Path($this->project); + } + return $this->filepath->createPath(); + } + + function main() { + if ($this->property === null) { + throw new BuildException("property attribute is required", $this->location); + } + if ($this->evaluate()) { + $this->project->setProperty($this->property, $this->value); + } + } + + function evaluate() { + if ($this->file === null && $this->resource === null) { + throw new BuildException("At least one of (file|resource) is required", $this->location); + } + + if ($this->type !== null && ($this->type !== "file" && $this->type !== "dir")) { + throw new BuildException("Type must be one of either dir or file", $this->location); + } + + if (($this->file !== null) && !$this->_checkFile()) { + $this->log("Unable to find " . $this->file->__toString() . " to set property " . $this->property, Project::MSG_VERBOSE); + return false; + } + + if (($this->resource !== null) && !$this->_checkResource($this->resource)) { + $this->log("Unable to load resource " . $this->resource . " to set property " . $this->property, Project::MSG_VERBOSE); + return false; + } + + return true; + } + + // this is prepared for the path type + private function _checkFile() { + if ($this->filepath === null) { + return $this->_checkFile1($this->file); + } else { + $paths = $this->filepath->listPaths(); + for($i=0,$pcnt=count($paths); $i < $pcnt; $i++) { + $this->log("Searching " . $paths[$i], Project::MSG_VERBOSE); + $tmp = new PhingFile($paths[$i], $this->file->getName()); + if($tmp->isFile()) { + return true; + } + } + } + return false; + } + + private function _checkFile1(PhingFile $file) { + // Resolve symbolic links + if ($this->followSymlinks && $file->isLink()) { + $file = new PhingFile($file->getLinkTarget()); + } + + if ($this->type !== null) { + if ($this->type === "dir") { + return $file->isDirectory(); + } else if ($this->type === "file") { + return $file->isFile(); + } + } + return $file->exists(); + } + + private function _checkResource($resource) { + if (null != ($resourcePath = Phing::getResourcePath($resource))) { + return $this->_checkFile1(new PhingFile($resourcePath)); + } else { + return false; + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/ChmodTask.php b/buildscripts/phing/classes/phing/tasks/system/ChmodTask.php new file mode 100755 index 00000000..b2300459 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ChmodTask.php @@ -0,0 +1,203 @@ +<?php +/* + * $Id: fad30caf32ff1ee9ff98f63849d61053ab88f645 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/types/FileSet.php'; + +/** + * Task that changes the permissions on a file/directory. + * + * @author Manuel Holtgrewe <grin@gmx.net> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: fad30caf32ff1ee9ff98f63849d61053ab88f645 $ + * @package phing.tasks.system + */ +class ChmodTask extends Task { + + private $file; + + private $mode; + + private $filesets = array(); + + private $filesystem; + + private $quiet = false; + private $failonerror = true; + private $verbose = true; + + /** + * This flag means 'note errors to the output, but keep going' + * @see setQuiet() + */ + function setFailonerror($bool) { + $this->failonerror = $bool; + } + + /** + * Set quiet mode, which suppresses warnings if chmod() fails. + * @see setFailonerror() + */ + function setQuiet($bool) { + $this->quiet = $bool; + if ($this->quiet) { + $this->failonerror = false; + } + } + + /** + * Set verbosity, which if set to false surpresses all but an overview + * of what happened. + */ + function setVerbose($bool) { + $this->verbose = (bool)$bool; + } + + /** + * Sets a single source file to touch. If the file does not exist + * an empty file will be created. + */ + function setFile(PhingFile $file) { + $this->file = $file; + } + + function setMode($str) { + $this->mode = $str; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + */ + function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Execute the touch operation. + * @return void + */ + function main() { + // Check Parameters + $this->checkParams(); + $this->chmod(); + } + + /** + * Ensure that correct parameters were passed in. + * @return void + */ + private function checkParams() { + + if ($this->file === null && empty($this->filesets)) { + throw new BuildException("Specify at least one source - a file or a fileset."); + } + + if ($this->mode === null) { + throw new BuildException("You have to specify an octal mode for chmod."); + } + + // check for mode to be in the correct format + if (!preg_match('/^([0-7]){3,4}$/', $this->mode)) { + throw new BuildException("You have specified an invalid mode."); + } + + } + + /** + * Does the actual work. + * @return void + */ + private function chmod() { + + if (strlen($this->mode) === 4) { + $mode = octdec($this->mode); + } else { + // we need to prepend the 0 before converting + $mode = octdec("0". $this->mode); + } + + // counters for non-verbose output + $total_files = 0; + $total_dirs = 0; + + // one file + if ($this->file !== null) { + $total_files = 1; + $this->chmodFile($this->file, $mode); + } + + // filesets + foreach($this->filesets as $fs) { + + $ds = $fs->getDirectoryScanner($this->project); + $fromDir = $fs->getDir($this->project); + + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + $filecount = count($srcFiles); + $total_files = $total_files + $filecount; + for ($j = 0; $j < $filecount; $j++) { + $this->chmodFile(new PhingFile($fromDir, $srcFiles[$j]), $mode); + } + + $dircount = count($srcDirs); + $total_dirs = $total_dirs + $dircount; + for ($j = 0; $j < $dircount; $j++) { + $this->chmodFile(new PhingFile($fromDir, $srcDirs[$j]), $mode); + } + } + + if (!$this->verbose) { + $this->log('Total files changed to ' . vsprintf('%o', $mode) . ': ' . $total_files); + $this->log('Total directories changed to ' . vsprintf('%o', $mode) . ': ' . $total_dirs); + } + + } + + /** + * Actually change the mode for the file. + * @param PhingFile $file + * @param int $mode + */ + private function chmodFile(PhingFile $file, $mode) { + if ( !$file->exists() ) { + throw new BuildException("The file " . $file->__toString() . " does not exist"); + } + + try { + $file->setMode($mode); + if ($this->verbose) { + $this->log("Changed file mode on '" . $file->__toString() ."' to " . vsprintf("%o", $mode)); + } + } catch (Exception $e) { + if($this->failonerror) { + throw $e; + } else { + $this->log($e->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + +} + + diff --git a/buildscripts/phing/classes/phing/tasks/system/ChownTask.php b/buildscripts/phing/classes/phing/tasks/system/ChownTask.php new file mode 100755 index 00000000..60b8105c --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ChownTask.php @@ -0,0 +1,216 @@ +<?php +/* + * $Id: f7234abd52e7f80177f4b121436ae7276370993c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/types/FileSet.php'; + +/** + * Task that changes the permissions on a file/directory. + * + * @author Mehmet Emre Yilmaz <mehmety@gmail.com> + * @version $Id: f7234abd52e7f80177f4b121436ae7276370993c $ + * @package phing.tasks.system + */ +class ChownTask extends Task { + + private $file; + + private $user; + private $group; + + private $filesets = array(); + + private $filesystem; + + private $quiet = false; + private $failonerror = true; + private $verbose = true; + + /** + * This flag means 'note errors to the output, but keep going' + * @see setQuiet() + */ + function setFailonerror($bool) { + $this->failonerror = $bool; + } + + /** + * Set quiet mode, which suppresses warnings if chown() fails. + * @see setFailonerror() + */ + function setQuiet($bool) { + $this->quiet = $bool; + if ($this->quiet) { + $this->failonerror = false; + } + } + + /** + * Set verbosity, which if set to false surpresses all but an overview + * of what happened. + */ + function setVerbose($bool) { + $this->verbose = (bool)$bool; + } + + /** + * Sets a single source file to touch. If the file does not exist + * an empty file will be created. + */ + function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Sets the user + */ + function setUser($user) { + $this->user = $user; + } + + /** + * Sets the group + */ + function setGroup($group) { + $this->group = $group; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + */ + function addFileSet(FileSet $fs) { + $this->filesets[] = $fs; + } + + /** + * Execute the touch operation. + * @return void + */ + function main() { + // Check Parameters + $this->checkParams(); + $this->chown(); + } + + /** + * Ensure that correct parameters were passed in. + * @return void + */ + private function checkParams() { + + if ($this->file === null && empty($this->filesets)) { + throw new BuildException("Specify at least one source - a file or a fileset."); + } + + if ($this->user === null && $this->group === null) { + throw new BuildException("You have to specify either an owner or a group for chown."); + } + } + + /** + * Does the actual work. + * @return void + */ + private function chown() { + $userElements = explode('.', $this->user); + + $user = $userElements[0]; + + if (count($userElements) > 1) { + $group = $userElements[1]; + } else { + $group = $this->group; + } + + // counters for non-verbose output + $total_files = 0; + $total_dirs = 0; + + // one file + if ($this->file !== null) { + $total_files = 1; + $this->chownFile($this->file, $user, $group); + } + + // filesets + foreach($this->filesets as $fs) { + + $ds = $fs->getDirectoryScanner($this->project); + $fromDir = $fs->getDir($this->project); + + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + $filecount = count($srcFiles); + $total_files = $total_files + $filecount; + for ($j = 0; $j < $filecount; $j++) { + $this->chownFile(new PhingFile($fromDir, $srcFiles[$j]), $user, $group); + } + + $dircount = count($srcDirs); + $total_dirs = $total_dirs + $dircount; + for ($j = 0; $j < $dircount; $j++) { + $this->chownFile(new PhingFile($fromDir, $srcDirs[$j]), $user, $group); + } + } + + if (!$this->verbose) { + $this->log('Total files changed to ' . $user . ($group ? "." . $group : "") . ': ' . $total_files); + $this->log('Total directories changed to ' . $user . ($group ? "." . $group : "") . ': ' . $total_dirs); + } + + } + + /** + * Actually change the mode for the file. + * @param PhingFile $file + * @param string $user + * @param string $group + */ + private function chownFile(PhingFile $file, $user, $group = "") { + if ( !$file->exists() ) { + throw new BuildException("The file " . $file->__toString() . " does not exist"); + } + + try { + if (!empty($user)) { + $file->setUser($user); + } + + if (!empty($group)) { + $file->setGroup($group); + } + + if ($this->verbose) { + $this->log("Changed file owner on '" . $file->__toString() ."' to " . $user . ($group ? "." . $group : "")); + } + } catch (Exception $e) { + if($this->failonerror) { + throw $e; + } else { + $this->log($e->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + +} + + diff --git a/buildscripts/phing/classes/phing/tasks/system/ConditionTask.php b/buildscripts/phing/classes/phing/tasks/system/ConditionTask.php new file mode 100755 index 00000000..040fff8a --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ConditionTask.php @@ -0,0 +1,74 @@ +<?php +/* + * $Id: 31dfd9d24212a4cf5afcce43a9ae337669bafc35 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/tasks/system/condition/ConditionBase.php'; + +/** + * <condition> task as a generalization of <available> + * + * <p>This task supports boolean logic as well as pluggable conditions + * to decide, whether a property should be set.</p> + * + * <p>This task does not extend Task to take advantage of + * ConditionBase.</p> + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.tasks.system + */ +class ConditionTask extends ConditionBase { + + private $property; + private $value = "true"; + + /** + * The name of the property to set. Required. + */ + function setProperty($p) { + $this->property = $p; + } + + /** + * The value for the property to set. Defaults to "true". + */ + function setValue($v) { + $this->value = $v; + } + + /** + * See whether our nested condition holds and set the property. + */ + function main() { + + if ($this->countConditions() > 1) { + throw new BuildException("You must not nest more than one condition into <condition>"); + } + if ($this->countConditions() < 1) { + throw new BuildException("You must nest a condition into <condition>"); + } + $cs = $this->getIterator(); + if ($cs->current()->evaluate()) { + $this->project->setProperty($this->property, $this->value); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/CopyTask.php b/buildscripts/phing/classes/phing/tasks/system/CopyTask.php new file mode 100755 index 00000000..2533bb95 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/CopyTask.php @@ -0,0 +1,480 @@ +<?php +/* + * $Id: 86322e73609da671413e4c959082958c16a510cc $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/util/SourceFileScanner.php'; +include_once 'phing/mappers/IdentityMapper.php'; +include_once 'phing/mappers/FlattenMapper.php'; + +/** + * A phing copy task. Copies a file or directory to a new file + * or directory. Files are only copied if the source file is newer + * than the destination file, or when the destination file does not + * exist. It is possible to explictly overwrite existing files. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id: 86322e73609da671413e4c959082958c16a510cc $ + * @package phing.tasks.system + */ +class CopyTask extends Task { + + protected $file = null; // the source file (from xml attribute) + protected $destFile = null; // the destiantion file (from xml attribute) + protected $destDir = null; // the destination dir (from xml attribute) + protected $overwrite = false; // overwrite destination (from xml attribute) + protected $preserveLMT = false; // sync timestamps (from xml attribute) + protected $includeEmpty = true; // include empty dirs? (from XML) + protected $flatten = false; // apply the FlattenMapper right way (from XML) + protected $mapperElement = null; + + protected $fileCopyMap = array(); // asoc array containing mapped file names + protected $dirCopyMap = array(); // asoc array containing mapped file names + protected $completeDirMap= array(); // asoc array containing complete dir names + protected $fileUtils = null; // a instance of fileutils + protected $filesets = array(); // all fileset objects assigned to this task + protected $filelists = array(); // all filelist objects assigned to this task + protected $filterChains = array(); // all filterchains objects assigned to this task + + protected $verbosity = Project::MSG_VERBOSE; + + protected $mode = 0755; // mode to create directories with + + protected $haltonerror = true; // stop build on errors + + /** + * Sets up this object internal stuff. i.e. the Fileutils instance + * + * @return object The CopyTask instnace + * @access public + */ + function __construct() { + $this->fileUtils = new FileUtils(); + } + + /** + * Set the overwrite flag. IntrospectionHelper takes care of + * booleans in set* methods so we can assume that the right + * value (boolean primitive) is coming in here. + * + * @param boolean Overwrite the destination file(s) if it/they already exist + * @return void + * @access public + */ + function setOverwrite($bool) { + $this->overwrite = (boolean) $bool; + } + + /** + * Used to force listing of all names of copied files. + * @param boolean $verbosity + */ + function setVerbose($verbosity) { + if ($verbosity) { + $this->verbosity = Project::MSG_INFO; + } else { + $this->verbosity = Project::MSG_VERBOSE; + } + } + + /** + * @see CopyTask::setPreserveLastModified + */ + function setTstamp($bool) { + $this->setPreserveLastModified($bool); + } + + /** + * Set the preserve timestamp flag. IntrospectionHelper takes care of + * booleans in set* methods so we can assume that the right + * value (boolean primitive) is coming in here. + * + * @param boolean Preserve the timestamp on the destination file + * @return void + * @access public + */ + function setPreserveLastModified($bool) { + $this->preserveLMT = (boolean) $bool; + } + + /** + * Set the include empty dirs flag. IntrospectionHelper takes care of + * booleans in set* methods so we can assume that the right + * value (boolean primitive) is coming in here. + * + * @param boolean Flag if empty dirs should be cpoied too + * @return void + * @access public + */ + function setIncludeEmptyDirs($bool) { + $this->includeEmpty = (boolean) $bool; + } + + + /** + * Set the file. We have to manually take care of the + * type that is coming due to limited type support in php + * in and convert it manually if neccessary. + * + * @param string/object The source file. Either a string or an PhingFile object + * @return void + * @access public + */ + function setFile(PhingFile $file) { + $this->file = $file; + } + + + /** + * Set the toFile. We have to manually take care of the + * type that is coming due to limited type support in php + * in and convert it manually if neccessary. + * + * @param string/object The dest file. Either a string or an PhingFile object + * @return void + * @access public + */ + function setTofile(PhingFile $file) { + $this->destFile = $file; + } + + /** + * Sets the mode to create destination directories with (ignored on Windows). + * Default mode is 0755. + * + * @param integer Octal mode + * @return void + * @access public + */ + function setMode($mode) { + $this->mode = (int) base_convert($mode, 8, 10); + } + + /** + * Set the toDir. We have to manually take care of the + * type that is coming due to limited type support in php + * in and convert it manually if neccessary. + * + * @param string/object The directory, either a string or an PhingFile object + * @return void + * @access public + */ + function setTodir(PhingFile $dir) { + $this->destDir = $dir; + } + + /** + * Set the haltonerror attribute - when true, will + * make the build fail when errors are detected. + * + * @param boolean Flag if the build should be stopped on errors + * @return void + * @access public + */ + function setHaltonerror($haltonerror) { + $this->haltonerror = (boolean) $haltonerror; + } + + /** + * Nested creator, creates a FileSet for this task + * + * @param FileSet $fileset Set of files to copy + * + * @return void + */ + public function addFileSet(FileSet $fs) { + $this->filesets[] = $fs; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + * + * @access public + * @return object The created filelist object + */ + function createFileList() { + $num = array_push($this->filelists, new FileList()); + return $this->filelists[$num-1]; + } + /** + * Creates a filterchain + * + * @access public + * @return object The created filterchain object + */ + function createFilterChain() { + $num = array_push($this->filterChains, new FilterChain($this->project)); + return $this->filterChains[$num-1]; + } + + /** + * Nested creator, creates one Mapper for this task + * + * @access public + * @return object The created Mapper type object + * @throws BuildException + */ + function createMapper() { + if ($this->mapperElement !== null) { + throw new BuildException("Cannot define more than one mapper", $this->location); + } + $this->mapperElement = new Mapper($this->project); + return $this->mapperElement; + } + + /** + * The main entry point where everything gets in motion. + * + * @access public + * @return true on success + * @throws BuildException + */ + function main() { + + $this->validateAttributes(); + + if ($this->file !== null) { + if ($this->file->exists()) { + if ($this->destFile === null) { + $this->destFile = new PhingFile($this->destDir, (string) $this->file->getName()); + } + if ($this->overwrite === true || ($this->file->lastModified() > $this->destFile->lastModified())) { + $this->fileCopyMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath(); + } else { + $this->log($this->file->getName()." omitted, is up to date"); + } + } else { + // terminate build + $this->logError("Could not find file " . $this->file->__toString() . " to copy."); + } + } + + $project = $this->getProject(); + + // process filelists + foreach($this->filelists as $fl) { + $fromDir = $fl->getDir($project); + $srcFiles = $fl->getFiles($project); + $srcDirs = array($fl->getDir($project)); + + if (!$this->flatten && $this->mapperElement === null) + { + $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath(); + } + + $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs); + } + + // process filesets + foreach($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + if (!$this->flatten && $this->mapperElement === null) + { + $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath(); + } + + $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs); + } + + // go and copy the stuff + $this->doWork(); + + if ($this->destFile !== null) { + $this->destDir = null; + } + } + + /** + * Validates attributes coming in from XML + * + * @access private + * @return void + * @throws BuildException + */ + protected function validateAttributes() { + + if ($this->file === null && count($this->filesets) === 0 && count($this->filelists) === 0) { + throw new BuildException("CopyTask. Specify at least one source - a file, fileset or filelist."); + } + + if ($this->destFile !== null && $this->destDir !== null) { + throw new BuildException("Only one of destfile and destdir may be set."); + } + + if ($this->destFile === null && $this->destDir === null) { + throw new BuildException("One of destfile or destdir must be set."); + } + + if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) { + throw new BuildException("Use a fileset to copy directories."); + } + + if ($this->destFile !== null && count($this->filesets) > 0) { + throw new BuildException("Cannot concatenate multple files into a single file."); + } + + if ($this->destFile !== null) { + $this->destDir = new PhingFile($this->destFile->getParent()); + } + } + + /** + * Compares source files to destination files to see if they + * should be copied. + * + * @access private + * @return void + */ + private function _scan(&$fromDir, &$toDir, &$files, &$dirs) { + /* mappers should be generic, so we get the mappers here and + pass them on to builMap. This method is not redundan like it seems */ + $mapper = null; + if ($this->mapperElement !== null) { + $mapper = $this->mapperElement->getImplementation(); + } else if ($this->flatten) { + $mapper = new FlattenMapper(); + } else { + $mapper = new IdentityMapper(); + } + $this->buildMap($fromDir, $toDir, $files, $mapper, $this->fileCopyMap); + $this->buildMap($fromDir, $toDir, $dirs, $mapper, $this->dirCopyMap); + } + + /** + * Builds a map of filenames (from->to) that should be copied + * + * @access private + * @return void + */ + private function buildMap(&$fromDir, &$toDir, &$names, &$mapper, &$map) { + $toCopy = null; + if ($this->overwrite) { + $v = array(); + foreach($names as $name) { + $result = $mapper->main($name); + if ($result !== null) { + $v[] = $name; + } + } + $toCopy = $v; + } else { + $ds = new SourceFileScanner($this); + $toCopy = $ds->restrict($names, $fromDir, $toDir, $mapper); + } + + for ($i=0,$_i=count($toCopy); $i < $_i; $i++) { + $src = new PhingFile($fromDir, $toCopy[$i]); + $mapped = $mapper->main($toCopy[$i]); + $dest = new PhingFile($toDir, $mapped[0]); + $map[$src->getAbsolutePath()] = $dest->getAbsolutePath(); + } + } + + + /** + * Actually copies the files + * + * @access private + * @return void + * @throws BuildException + */ + protected function doWork() { + + // These "slots" allow filters to retrieve information about the currently-being-process files + $fromSlot = $this->getRegisterSlot("currentFromFile"); + $fromBasenameSlot = $this->getRegisterSlot("currentFromFile.basename"); + + $toSlot = $this->getRegisterSlot("currentToFile"); + $toBasenameSlot = $this->getRegisterSlot("currentToFile.basename"); + + $mapSize = count($this->fileCopyMap); + $total = $mapSize; + + // handle empty dirs if appropriate + if ($this->includeEmpty) { + $count = 0; + foreach ($this->dirCopyMap as $srcdir => $destdir) { + $s = new PhingFile((string) $srcdir); + $d = new PhingFile((string) $destdir); + if (!$d->exists()) { + if (!$d->mkdirs()) { + $this->logError("Unable to create directory " . $d->__toString()); + } else { + if ($this->preserveLMT) { + $d->setLastModified($s->lastModified()); + } + + $count++; + } + } + } + if ($count > 0) { + $this->log("Created ".$count." empty director" . ($count == 1 ? "y" : "ies") . " in " . $this->destDir->getAbsolutePath()); + } + } + + if ($mapSize > 0) { + $this->log("Copying ".$mapSize." file".(($mapSize) === 1 ? '' : 's')." to ". $this->destDir->getAbsolutePath()); + // walks the map and actually copies the files + $count=0; + foreach($this->fileCopyMap as $from => $to) { + if ($from === $to) { + $this->log("Skipping self-copy of " . $from, $this->verbosity); + $total--; + continue; + } + $this->log("From ".$from." to ".$to, $this->verbosity); + try { // try to copy file + + $fromFile = new PhingFile($from); + $toFile = new PhingFile($to); + + $fromSlot->setValue($fromFile->getPath()); + $fromBasenameSlot->setValue($fromFile->getName()); + + $toSlot->setValue($toFile->getPath()); + $toBasenameSlot->setValue($toFile->getName()); + + $this->fileUtils->copyFile($fromFile, $toFile, $this->overwrite, $this->preserveLMT, $this->filterChains, $this->getProject(), $this->mode); + + $count++; + } catch (IOException $ioe) { + $this->logError("Failed to copy " . $from . " to " . $to . ": " . $ioe->getMessage()); + } + } + } + } + + protected function logError($message, $location = NULL) + { + if ($this->haltonerror) { + throw new BuildException($message, $location); + } else { + $this->log($message, Project::MSG_ERR); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/CvsPassTask.php b/buildscripts/phing/classes/phing/tasks/system/CvsPassTask.php new file mode 100755 index 00000000..87a9b63c --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/CvsPassTask.php @@ -0,0 +1,173 @@ +<?php +/* + * $Id: c1e02a8d43f62c584ba2f1cd5a6f0cc690bceb94 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/system/io/BufferedReader.php'; +include_once 'phing/system/io/BufferedWriter.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * Adds an new entry to a CVS password file. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Jeff Martin <jeff@custommonkey.org> (Ant) + * @version $Id$ + * @package phing.tasks.system + */ +class CVSPassTask extends Task { + + /** CVS Root */ + private $cvsRoot; + /** Password file to add password to */ + private $passFile; + /** Password to add to file */ + private $password; + + /** Array contain char conversion data */ + private static $shifts = array( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 114, 120, 53, 79, 96, 109, 72, 108, 70, 64, 76, 67, 116, 74, 68, 87, + 111, 52, 75, 119, 49, 34, 82, 81, 95, 65, 112, 86, 118, 110, 122, 105, + 41, 57, 83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123, 91, 35, + 125, 55, 54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56, + 36, 121, 117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, + 58, 113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85, 223, + 225, 216, 187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190, + 199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203, 226, 193, + 174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238, 161, 179, 160, 212, + 207, 221, 254, 173, 202, 146, 224, 151, 140, 196, 205, 130, 135, 133, 143, 246, + 192, 159, 244, 239, 185, 168, 215, 144, 139, 165, 180, 157, 147, 186, 214, 176, + 227, 231, 219, 169, 175, 156, 206, 198, 129, 164, 150, 210, 154, 177, 134, 127, + 182, 128, 158, 208, 162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195, + 243, 233, 253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152 + ); + + /** + * Create a CVS task using the default cvspass file location. + */ + public function __construct() { + $this->passFile = new PhingFile( + Phing::getProperty("cygwin.user.home", + Phing::getProperty("user.home")) + . DIRECTORY_SEPARATOR . ".cvspass"); + } + + /** + * Does the work. + * + * @throws BuildException if someting goes wrong with the build + */ + public final function main() { + if ($this->cvsRoot === null) { + throw new BuildException("cvsroot is required"); + } + if ($this->password === null) { + throw new BuildException("password is required"); + } + + $this->log("cvsRoot: " . $this->cvsRoot, Project::MSG_DEBUG); + $this->log("password: " . $this->password, Project::MSG_DEBUG); + $this->log("passFile: " . $this->passFile->__toString(), Project::MSG_DEBUG); + + $reader = null; + $writer = null; + + try { + $buf = ""; + + if ($this->passFile->exists()) { + $reader = new BufferedReader(new FileReader($this->passFile)); + + $line = null; + while (($line = $reader->readLine()) !== null) { + if (!StringHelper::startsWith($this->cvsRoot, $line)) { + $buf .= $line . PHP_EOL; + } + } + } + + $pwdfile = $buf . $this->cvsRoot . " A" . $this->mangle($this->password); + + $this->log("Writing -> " . $pwdfile , Project::MSG_DEBUG); + + $writer = new BufferedWriter(new FileWriter($this->passFile)); + $writer->write($pwdfile); + $writer->newLine(); + + $writer->close(); + if ($reader) { + $reader->close(); + } + + } catch (IOException $e) { + if ($reader) { + try { + $reader->close(); + } catch (Exception $e) {} + } + + if ($writer) { + try { + $writer->close(); + } catch (Exception $e) {} + } + + throw new BuildException($e); + } + } + + /** + * "Encode" the password. + */ + private final function mangle($password){ + $buf = ""; + for ($i = 0, $plen = strlen($password); $i < $plen; $i++) { + $buf .= chr(self::$shifts[ord($password{$i})]); + } + return $buf; + } + + /** + * The CVS repository to add an entry for. + * @param string $cvsRoot + */ + public function setCvsroot($cvsRoot) { + $this->cvsRoot = $cvsRoot; + } + + /** + * Password file to add the entry to. + * @param PhingFile $passFile + */ + public function setPassfile(PhingFile $passFile) { + $this->passFile = $passFile; + } + + /** + * Password to be added to the password file. + * @param string $password + */ + public function setPassword($password) { + $this->password = $password; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/CvsTask.php b/buildscripts/phing/classes/phing/tasks/system/CvsTask.php new file mode 100755 index 00000000..3c12a597 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/CvsTask.php @@ -0,0 +1,540 @@ +<?php +/* + * $Id: 642c6947fa58e02a8c7893c8c4dfab2debfdd51d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/tasks/system/ExecTask.php'; +include_once 'phing/types/Commandline.php'; + +/** + * Task for performing CVS operations. + * + * NOTE: This implementation has been moved here from Cvs.java with + * the addition of some accessors for extensibility. Another task + * can extend this with some customized output processing. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author costin@dnt.ro (Ant) + * @author stefano@apache.org (Ant) + * @author Wolfgang Werner <wwerner@picturesafe.de> (Ant) + * @author Kevin Ross <kevin.ross@bredex.com> (Ant) + * @version $Id: 642c6947fa58e02a8c7893c8c4dfab2debfdd51d $ + * @package phing.tasks.system + */ +class CvsTask extends Task { + + /** + * Default compression level to use, if compression is enabled via + * setCompression( true ). + */ + const DEFAULT_COMPRESSION_LEVEL = 3; + + private $cmd; + + /** + * List of Commandline children + * @var array Commandline[] + */ + private $commandlines = array(); + + /** + * the CVSROOT variable. + */ + private $cvsRoot; + + /** + * the CVS_RSH variable. + */ + private $cvsRsh; + + /** + * the package/module to check out. + */ + private $cvsModule; + + /** + * the default command. + */ + private static $default_command = "checkout"; + + /** + * the CVS command to execute. + */ + private $command = null; + + /** + * suppress information messages. + */ + private $quiet = false; + + /** + * compression level to use. + */ + private $compression = 0; + + /** + * report only, don't change any files. + */ + private $noexec = false; + + /** + * CVS port + */ + private $port = 0; + + /** + * CVS password file + * @var File + */ + private $passFile = null; + + /** + * the directory where the checked out files should be placed. + * @var File + */ + private $dest; + + private $error; + + private $output; + + /** + * If true it will stop the build if cvs exits with error. + * Default is false. (Iulian) + * @var boolean + */ + private $failOnError = false; + + public function init() { + $this->cmd = new Commandline(); + } + + /** + * Sets up the environment for toExecute and then runs it. + * @param Commandline $toExecute + * @throws BuildException + */ + protected function runCommand(Commandline $toExecute) { + + // We are putting variables into the script's environment + // and not removing them (!) This should be fine, but is + // worth remembering and testing. + + if ($this->port > 0) { + putenv("CVS_CLIENT_PORT=".$this->port); + } + + // Need a better cross platform integration with <cvspass>, so + // use the same filename. + + if ($this->passFile === null) { + $defaultPassFile = new PhingFile(Phing::getProperty("cygwin.user.home", Phing::getProperty("user.home")) + . DIRECTORY_SEPARATOR . ".cvspass"); + if($defaultPassFile->exists()) { + $this->setPassfile($defaultPassFile); + } + } + + if ($this->passFile !== null) { + if ($this->passFile->isFile() && $this->passFile->canRead()) { + putenv("CVS_PASSFILE=" . $this->passFile->__toString()); + $this->log("Using cvs passfile: " . $this->passFile->__toString(), Project::MSG_INFO); + } elseif (!$this->passFile->canRead()) { + $this->log("cvs passfile: " . $this->passFile->__toString() + . " ignored as it is not readable", Project::MSG_WARN); + } else { + $this->log("cvs passfile: " . $this->passFile->__toString() + . " ignored as it is not a file", + Project::MSG_WARN); + } + } + + if ($this->cvsRsh !== null) { + putenv("CVS_RSH=".$this->cvsRsh); + } + + // Use the ExecTask to handle execution of the command + $exe = new ExecTask($this->project); + $exe->setProject($this->project); + + //exe.setAntRun(project); + if ($this->dest === null) { + $this->dest = $this->project->getBaseDir(); + } + + if (!$this->dest->exists()) { + $this->dest->mkdirs(); + } + + if ($this->output !== null) { + $exe->setOutput($this->output); + } + + if ($this->error !== null) { + $exe->setError($this->error); + } + + $exe->setDir($this->dest); + + if (is_object($toExecute)) { + $toExecuteStr = $toExecute->__toString(); // unfortunately no more automagic for initial 5.0.0 release :( + } + + $exe->setCommand($toExecuteStr); + + try { + $actualCommandLine = $toExecuteStr; // we converted to string above + $this->log($actualCommandLine, Project::MSG_INFO); + $retCode = $exe->main(); + $this->log("retCode=" . $retCode, Project::MSG_DEBUG); + /*Throw an exception if cvs exited with error. (Iulian)*/ + if ($this->failOnError && $retCode !== 0) { + throw new BuildException("cvs exited with error code " + . $retCode + . PHP_EOL + . "Command line was [" + . $toExecute->describeCommand() . "]", $this->getLocation()); + } + } catch (IOException $e) { + if ($this->failOnError) { + throw new BuildException($e, $this->getLocation()); + } else { + $this->log("Caught exception: " . $e, Project::MSG_WARN); + } + } catch (BuildException $e) { + if ($this->failOnError) { + throw $e; + } else { + $t = $e->getCause(); + if ($t === null) { + $t = $e; + } + $this->log("Caught exception: " . $t, Project::MSG_WARN); + } + } catch (Exception $e) { + if ($this->failOnError) { + throw new BuildException($e, $this->getLocation()); + } else { + $this->log("Caught exception: " . $e, Project::MSG_WARN); + } + } + } + + /** + * + * @return void + * @throws BuildException + */ + public function main() { + + $savedCommand = $this->getCommand(); + + if ($this->getCommand() === null && empty($this->commandlines)) { + // re-implement legacy behaviour: + $this->setCommand(self::$default_command); + } + + $c = $this->getCommand(); + $cloned = null; + if ($c !== null) { + $cloned = $this->cmd->__copy(); + $cloned->createArgument(true)->setLine($c); + $this->addConfiguredCommandline($cloned, true); + } + + try { + for ($i = 0, $vecsize=count($this->commandlines); $i < $vecsize; $i++) { + $this->runCommand($this->commandlines[$i]); + } + + // finally { + if ($cloned !== null) { + $this->removeCommandline($cloned); + } + $this->setCommand($savedCommand); + + } catch (Exception $e) { + // finally { + if ($cloned !== null) { + $this->removeCommandline($cloned); + } + $this->setCommand($savedCommand); + throw $e; + } + } + + /** + * The CVSROOT variable. + * + * @param string $root + */ + public function setCvsRoot($root) { + + // Check if not real cvsroot => set it to null + if ($root !== null) { + if (trim($root) == "") { + $root = null; + } + } + + $this->cvsRoot = $root; + } + + public function getCvsRoot() { + return $this->cvsRoot; + } + + /** + * The CVS_RSH variable. + * + * @param rsh + */ + public function setCvsRsh($rsh) { + // Check if not real cvsrsh => set it to null + if ($rsh !== null) { + if (trim($rsh) == "") { + $rsh = null; + } + } + + $this->cvsRsh = $rsh; + } + + public function getCvsRsh() { + return $this->cvsRsh; + } + + /** + * Port used by CVS to communicate with the server. + * + * @param int $port + */ + public function setPort($port){ + $this->port = $port; + } + + /** + * @return int + */ + public function getPort() { + return $this->port; + } + + /** + * Password file to read passwords from. + * + * @param passFile + */ + public function setPassfile(PhingFile $passFile) { + $this->passFile = $passFile; + } + + /** + * @return File + */ + public function getPassFile() { + return $this->passFile; + } + + /** + * The directory where the checked out files should be placed. + * + * @param PhingFile $dest + */ + public function setDest(PhingFile $dest) { + $this->dest = $dest; + } + + public function getDest() { + return $this->dest; + } + + /** + * The package/module to operate upon. + * + * @param string $p + */ + public function setModule($m) { + $this->cvsModule = $m; + } + + public function getModule(){ + return $this->cvsModule; + } + + /** + * The tag of the package/module to operate upon. + * @param string $p + */ + public function setTag($p) { + // Check if not real tag => set it to null + if ($p !== null && trim($p) !== "") { + $this->appendCommandArgument("-r"); + $this->appendCommandArgument($p); + } + } + + /** + * This needs to be public to allow configuration + * of commands externally. + */ + public function appendCommandArgument($arg) { + $this->cmd->createArgument()->setValue($arg); + } + + /** + * Use the most recent revision no later than the given date. + * @param p + */ + public function setDate($p) { + if ($p !== null && trim($p) !== "") { + $this->appendCommandArgument("-D"); + $this->appendCommandArgument($p); + } + } + + /** + * The CVS command to execute. + * @param string $c + */ + public function setCommand($c) { + $this->command = $c; + } + + public function getCommand() { + return $this->command; + } + + /** + * If true, suppress informational messages. + * @param boolean $q + */ + public function setQuiet($q) { + $this->quiet = $q; + } + + /** + * If true, report only and don't change any files. + * + * @param boolean $ne + */ + public function setNoexec($ne) { + $this->noexec = (boolean) $ne; + } + + /** + * Stop the build process if the command exits with + * a return code other than 0. + * Defaults to false. + * @param boolean $failOnError + */ + public function setFailOnError($failOnError) { + $this->failOnError = (boolean) $failOnError; + } + + /** + * Configure a commandline element for things like cvsRoot, quiet, etc. + * @return string + */ + protected function configureCommandline($c) { + if ($c === null) { + return; + } + $c->setExecutable("cvs"); + + if ($this->cvsModule !== null) { + $c->createArgument()->setLine($this->cvsModule); + } + if ($this->compression > 0 && $this->compression < 10) { + $c->createArgument(true)->setValue("-z" . $this->compression); + } + if ($this->quiet) { + $c->createArgument(true)->setValue("-q"); + } + if ($this->noexec) { + $c->createArgument(true)->setValue("-n"); + } + if ($this->cvsRoot !== null) { + $c->createArgument(true)->setLine("-d" . $this->cvsRoot); + } + } + + protected function removeCommandline(Commandline $c) { + $idx = array_search($c, $this->commandlines, true); + if ($idx === false) { + return false; + } + $this->commandlines = array_splice($this->commandlines, $idx, 1); + return true; + } + + /** + * Configures and adds the given Commandline. + * @param insertAtStart If true, c is + */ + public function addConfiguredCommandline(Commandline $c, $insertAtStart = false) { + if ($c === null) { + return; + } + $this->configureCommandline($c); + if ($insertAtStart) { + array_unshift($this->commandlines, $c); + } else { + array_push($this->commandlines, $c); + } + } + + /** + * If set to a value 1-9 it adds -zN to the cvs command line, else + * it disables compression. + * @param int $level + */ + public function setCompressionLevel($level) { + $this->compression = $level; + } + + /** + * If true, this is the same as compressionlevel="3". + * + * @param boolean $usecomp If true, turns on compression using default + * level, AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL. + */ + public function setCompression($usecomp) { + $this->setCompressionLevel($usecomp ? + self::DEFAULT_COMPRESSION_LEVEL : 0); + } + + /** + * File to which output should be written. + * @param PhingFile $output + */ + function setOutput(PhingFile $f) { + $this->output = $f; + } + + /** + * File to which error output should be written. + * @param PhingFile $output + */ + function setError(PhingFile $f) { + $this->error = $f; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/DeleteTask.php b/buildscripts/phing/classes/phing/tasks/system/DeleteTask.php new file mode 100755 index 00000000..8af8a632 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/DeleteTask.php @@ -0,0 +1,276 @@ +<?php +/* + * $Id: f25fc4c605ead65db9235c9fe328b2d47b7c8299 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Deletes a file or directory, or set of files defined by a fileset. + * + * @version $Id: f25fc4c605ead65db9235c9fe328b2d47b7c8299 $ + * @package phing.tasks.system + */ +class DeleteTask extends Task { + + protected $file; + protected $dir; + protected $filesets = array(); + protected $includeEmpty = false; + + protected $quiet = false; + protected $failonerror = true; + protected $verbosity = Project::MSG_VERBOSE; + + /** Any filelists of files that should be deleted. */ + private $filelists = array(); + + /** + * Set the name of a single file to be removed. + * @param PhingFile $file + */ + function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * Set the directory from which files are to be deleted. + * @param PhingFile $dir + */ + function setDir(PhingFile $dir) { + $this->dir = $dir; + } + + /** + * Used to force listing of all names of deleted files. + * @param boolean $verbosity + */ + function setVerbose($verbosity) { + if ($verbosity) { + $this->verbosity = Project::MSG_INFO; + } else { + $this->verbosity = Project::MSG_VERBOSE; + } + } + + /** + * If the file does not exist, do not display a diagnostic + * message or modify the exit status to reflect an error. + * This means that if a file or directory cannot be deleted, + * then no error is reported. This setting emulates the + * -f option to the Unix rm command. Default is false + * meaning things are verbose + */ + function setQuiet($bool) { + $this->quiet = $bool; + if ($this->quiet) { + $this->failonerror = false; + } + } + + /** this flag means 'note errors to the output, but keep going' */ + function setFailOnError($bool) { + $this->failonerror = $bool; + } + + + /** Used to delete empty directories.*/ + function setIncludeEmptyDirs($includeEmpty) { + $this->includeEmpty = (boolean) $includeEmpty; + } + + /** Nested creator, adds a set of files (nested fileset attribute). */ + function addFileSet(FileSet $fs) { + $this->filesets[] = $fs; + } + + /** Nested creator, adds a set of files (nested fileset attribute). */ + function createFileList() { + $num = array_push($this->filelists, new FileList()); + return $this->filelists[$num-1]; + } + + /** Delete the file(s). */ + function main() { + if ($this->file === null && $this->dir === null && count($this->filesets) === 0 && count($this->filelists) === 0) { + throw new BuildException("At least one of the file or dir attributes, or a fileset element, or a filelist element must be set."); + } + + if ($this->quiet && $this->failonerror) { + throw new BuildException("quiet and failonerror cannot both be set to true", $this->location); + } + + // delete a single file + if ($this->file !== null) { + if ($this->file->exists()) { + if ($this->file->isDirectory()) { + $this->log("Directory " . $this->file->__toString() . " cannot be removed using the file attribute. Use dir instead."); + } else { + $this->log("Deleting: " . $this->file->__toString()); + try { + $this->file->delete(); + } catch(Exception $e) { + $message = "Unable to delete file " . $this->file->__toString() .": " .$e->getMessage(); + if($this->failonerror) { + throw new BuildException($message); + } else { + $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + } else { + $this->log("Could not find file " . $this->file->getAbsolutePath() . " to delete.",Project::MSG_VERBOSE); + } + } + + // delete the directory + if ($this->dir !== null && $this->dir->exists() && $this->dir->isDirectory()) { + if ($this->verbosity === Project::MSG_VERBOSE) { + $this->log("Deleting directory " . $this->dir->__toString()); + } + $this->removeDir($this->dir); + } + + // delete the files in the filelists + foreach($this->filelists as $fl) { + try { + $files = $fl->getFiles($this->project); + $this->removeFiles($fl->getDir($this->project), $files, $empty=array()); + } catch (BuildException $be) { + // directory doesn't exist or is not readable + if ($this->failonerror) { + throw $be; + } else { + $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + + // delete the files in the filesets + foreach($this->filesets as $fs) { + try { + $ds = $fs->getDirectoryScanner($this->project); + $files = $ds->getIncludedFiles(); + $dirs = $ds->getIncludedDirectories(); + $this->removeFiles($fs->getDir($this->project), $files, $dirs); + } catch (BuildException $be) { + // directory doesn't exist or is not readable + if ($this->failonerror) { + throw $be; + } else { + $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + } + + /** + * Recursively removes a directory. + * @param PhingFile $d The directory to remove. + */ + private function removeDir($d) { + $list = $d->listDir(); + if ($list === null) { + $list = array(); + } + + foreach($list as $s) { + $f = new PhingFile($d, $s); + if ($f->isDirectory()) { + $this->removeDir($f); + } else { + $this->log("Deleting " . $f->__toString(), $this->verbosity); + try { + $f->delete(); + } catch (Exception $e) { + $message = "Unable to delete file " . $f->__toString() . ": " . $e->getMessage(); + if($this->failonerror) { + throw new BuildException($message); + } else { + $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + } + $this->log("Deleting directory " . $d->getAbsolutePath(), $this->verbosity); + try { + $d->delete(); + } catch (Exception $e) { + $message = "Unable to delete directory " . $d->__toString() . ": " . $e->getMessage(); + if($this->failonerror) { + throw new BuildException($message); + } else { + $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + + /** + * remove an array of files in a directory, and a list of subdirectories + * which will only be deleted if 'includeEmpty' is true + * @param PhingFile $d directory to work from + * @param array &$files array of files to delete; can be of zero length + * @param array &$dirs array of directories to delete; can of zero length + */ + private function removeFiles(PhingFile $d, &$files, &$dirs) { + if (count($files) > 0) { + $this->log("Deleting " . count($files) . " files from " . $d->__toString()); + for ($j=0,$_j=count($files); $j < $_j; $j++) { + $f = new PhingFile($d, $files[$j]); + $this->log("Deleting " . $f->getAbsolutePath(), $this->verbosity); + try { + $f->delete(); + } catch (Exception $e) { + $message = "Unable to delete file " . $f->__toString() . ": " . $e->getMessage(); + if($this->failonerror) { + throw new BuildException($message); + } else { + $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + + } + } + + if (count($dirs) > 0 && $this->includeEmpty) { + $dirCount = 0; + for ($j=count($dirs)-1; $j>=0; --$j) { + $dir = new PhingFile($d, $dirs[$j]); + $dirFiles = $dir->listDir(); + if ($dirFiles === null || count($dirFiles) === 0) { + $this->log("Deleting " . $dir->__toString(), $this->verbosity); + try { + $dir->delete(); + $dirCount++; + } catch (Exception $e) { + $message="Unable to delete directory " . $dir->__toString(); + if($this->failonerror) { + throw new BuildException($message); + } else { + $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN); + } + } + } + } + if ($dirCount > 0) { + $this->log("Deleted $dirCount director" . ($dirCount==1 ? "y" : "ies") . " from " . $d->__toString()); + } + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/EchoTask.php b/buildscripts/phing/classes/phing/tasks/system/EchoTask.php new file mode 100755 index 00000000..edc098c4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/EchoTask.php @@ -0,0 +1,152 @@ +<?php +/* + * $Id: c5ebadd12256533d9ca4d6fb6cabd50415bdddbf $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/Task.php'; + +/** + * Echos a message to the logging system or to a file + * + * @author Michiel Rook <mrook@php.net> + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.tasks.system + */ + +class EchoTask extends Task { + + protected $msg = ""; + + protected $file = ""; + + protected $append = false; + + protected $level = "info"; + + protected $filesets = array(); + + function main() { + switch ($this->level) + { + case "error": $loglevel = Project::MSG_ERR; break; + case "warning": $loglevel = Project::MSG_WARN; break; + case "info": $loglevel = Project::MSG_INFO; break; + case "verbose": $loglevel = Project::MSG_VERBOSE; break; + case "debug": $loglevel = Project::MSG_DEBUG; break; + } + + if (count($this->filesets)) { + if (trim(substr($this->msg, -1)) != '') { + $this->msg .= "\n"; + } + $this->msg .= $this->getFilesetsMsg(); + } + + if (empty($this->file)) + { + $this->log($this->msg, $loglevel); + } + else + { + if ($this->append) + { + $handle = fopen($this->file, "a"); + } + else + { + $handle = fopen($this->file, "w"); + } + + fwrite($handle, $this->msg); + + fclose($handle); + } + } + + /** + * Merges all filesets into a string to be echoed out + * + * @return string String to echo + */ + protected function getFilesetsMsg() + { + $project = $this->getProject(); + $msg = ''; + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($project); + $fromDir = $fs->getDir($project); + $srcFiles = $ds->getIncludedFiles(); + $msg .= 'Directory: ' . $fromDir . ' => ' + . realpath($fromDir) . "\n"; + foreach ($srcFiles as $file) { + $relPath = $fromDir . DIRECTORY_SEPARATOR . $file; + $msg .= $relPath . "\n"; + } + } + + return $msg; + } + + /** setter for file */ + function setFile($file) + { + $this->file = (string) $file; + } + + /** setter for level */ + function setLevel($level) + { + $this->level = (string) $level; + } + + /** setter for append */ + function setAppend($append) + { + $this->append = $append; + } + + /** setter for message */ + function setMsg($msg) { + $this->setMessage($msg); + } + + /** alias setter */ + function setMessage($msg) { + $this->msg = (string) $msg; + } + + /** Supporting the <echo>Message</echo> syntax. */ + function addText($msg) + { + $this->msg = (string) $msg; + } + + /** + * Adds a fileset to echo the files of + * + * @param FileSet $fs Set of files to echo + * + * @return void + */ + public function addFileSet(FileSet $fs) + { + $this->filesets[] = $fs; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/ExecTask.php b/buildscripts/phing/classes/phing/tasks/system/ExecTask.php new file mode 100755 index 00000000..f5a57948 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ExecTask.php @@ -0,0 +1,516 @@ +<?php + +/** + * $Id: 3101d1403eeb4fe4fe8bae729b7d918b303de1cf $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Executes a command on the shell. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @author Christian Weiske <cweiske@cweiske.de> + * @version $Id: 3101d1403eeb4fe4fe8bae729b7d918b303de1cf $ + * @package phing.tasks.system + */ +class ExecTask extends Task +{ + + /** + * Command to execute. + * @var string + */ + protected $command; + + /** + * Commandline managing object + * + * @var Commandline + */ + protected $commandline; + + /** + * Working directory. + * @var PhingFile + */ + protected $dir; + + /** + * Operating system. + * @var string + */ + protected $os; + + /** + * Whether to escape shell command using escapeshellcmd(). + * @var boolean + */ + protected $escape = false; + + /** + * Where to direct output. + * @var File + */ + protected $output; + + /** + * Whether to use PHP's passthru() function instead of exec() + * @var boolean + */ + protected $passthru = false; + + /** + * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE + * @var boolean + */ + protected $logOutput = false; + + /** + * Logging level for status messages + * @var integer + */ + protected $logLevel = Project::MSG_VERBOSE; + + /** + * Where to direct error output. + * @var File + */ + protected $error; + + /** + * If spawn is set then [unix] programs will redirect stdout and add '&'. + * @var boolean + */ + protected $spawn = false; + + /** + * Property name to set with return value from exec call. + * + * @var string + */ + protected $returnProperty; + + /** + * Property name to set with output value from exec call. + * + * @var string + */ + protected $outputProperty; + + /** + * Whether to check the return code. + * @var boolean + */ + protected $checkreturn = false; + + + + public function __construct() + { + $this->commandline = new Commandline(); + } + + /** + * Main method: wraps execute() command. + * + * @return void + */ + public function main() + { + if (!$this->isApplicable()) { + return; + } + + $this->prepare(); + $this->buildCommand(); + list($return, $output) = $this->executeCommand(); + $this->cleanup($return, $output); + } + + /** + * Checks whether the command shall be executed + * + * @return boolean False if the exec command shall not be run + */ + protected function isApplicable() + { + if ($this->os === null) { + return true; + } + + $myos = Phing::getProperty('os.name'); + $this->log('Myos = ' . $myos, Project::MSG_VERBOSE); + + if (strpos($this->os, $myos) !== false) { + // this command will be executed only on the specified OS + // OS matches + return true; + } + + $this->log( + sprintf( + 'Operating system %s not found in %s', + $myos, $this->os + ), + Project::MSG_VERBOSE + ); + return false; + } + + /** + * Prepares the command building and execution, i.e. + * changes to the specified directory. + * + * @return void + */ + protected function prepare() + { + if ($this->dir === null) { + return; + } + + // expand any symbolic links first + if (!$this->dir->getCanonicalFile()->isDirectory()) { + throw new BuildException( + "'" . (string) $this->dir . "' is not a valid directory" + ); + } + $this->currdir = getcwd(); + @chdir($this->dir->getPath()); + } + + /** + * Builds the full command to execute and stores it in $command. + * + * @return void + * @uses $command + */ + protected function buildCommand() + { + if ($this->command === null && $this->commandline->getExecutable() === null) { + throw new BuildException( + 'ExecTask: Please provide "command" OR "executable"' + ); + } else if ($this->command === null) { + $this->command = Commandline::toString($this->commandline->getCommandline(), $this->escape); + } else if ($this->commandline->getExecutable() === null) { + //we need to escape the command only if it's specified directly + // commandline takes care of "executable" already + if ($this->escape == true) { + $this->command = escapeshellcmd($this->command); + } + } else { + throw new BuildException( + 'ExecTask: Either use "command" OR "executable"' + ); + } + + if ($this->error !== null) { + $this->command .= ' 2> ' . $this->error->getPath(); + $this->log( + "Writing error output to: " . $this->error->getPath(), + $this->logLevel + ); + } + + if ($this->output !== null) { + $this->command .= ' 1> ' . $this->output->getPath(); + $this->log( + "Writing standard output to: " . $this->output->getPath(), + $this->logLevel + ); + } elseif ($this->spawn) { + $this->command .= ' 1>/dev/null'; + $this->log("Sending output to /dev/null", $this->logLevel); + } + + // If neither output nor error are being written to file + // then we'll redirect error to stdout so that we can dump + // it to screen below. + + if ($this->output === null && $this->error === null) { + $this->command .= ' 2>&1'; + } + + // we ignore the spawn boolean for windows + if ($this->spawn) { + $this->command .= ' &'; + } + } + + /** + * Executes the command and returns return code and output. + * + * @return array array(return code, array with output) + */ + protected function executeCommand() + { + $this->log("Executing command: " . $this->command, $this->logLevel); + + $output = array(); + $return = null; + + if ($this->passthru) { + passthru($this->command, $return); + } else { + exec($this->command, $output, $return); + } + + return array($return, $output); + } + + /** + * Runs all tasks after command execution: + * - change working directory back + * - log output + * - verify return value + * + * @param integer $return Return code + * @param array $output Array with command output + * + * @return void + */ + protected function cleanup($return, $output) + { + if ($this->dir !== null) { + @chdir($this->currdir); + } + + $outloglevel = $this->logOutput ? Project::MSG_INFO : Project::MSG_VERBOSE; + foreach ($output as $line) { + $this->log($line, $outloglevel); + } + + if ($this->returnProperty) { + $this->project->setProperty($this->returnProperty, $return); + } + + if ($this->outputProperty) { + $this->project->setProperty( + $this->outputProperty, implode("\n", $output) + ); + } + + if ($return != 0 && $this->checkreturn) { + throw new BuildException("Task exited with code $return"); + } + } + + + /** + * The command to use. + * + * @param mixed $command String or string-compatible (e.g. w/ __toString()). + * + * @return void + */ + public function setCommand($command) + { + $this->command = "" . $command; + } + + /** + * The executable to use. + * + * @param mixed $executable String or string-compatible (e.g. w/ __toString()). + * + * @return void + */ + public function setExecutable($executable) + { + $this->commandline->setExecutable((string)$executable); + } + + /** + * Whether to use escapeshellcmd() to escape command. + * + * @param boolean $escape If the command shall be escaped or not + * + * @return void + */ + public function setEscape($escape) + { + $this->escape = (bool) $escape; + } + + /** + * Specify the working directory for executing this command. + * + * @param PhingFile $dir Working directory + * + * @return void + */ + public function setDir(PhingFile $dir) + { + $this->dir = $dir; + } + + /** + * Specify OS (or muliple OS) that must match in order to execute this command. + * + * @param string $os Operating system string (e.g. "Linux") + * + * @return void + */ + public function setOs($os) + { + $this->os = (string) $os; + } + + /** + * File to which output should be written. + * + * @param PhingFile $f Output log file + * + * @return void + */ + public function setOutput(PhingFile $f) + { + $this->output = $f; + } + + /** + * File to which error output should be written. + * + * @param PhingFile $f Error log file + * + * @return void + */ + public function setError(PhingFile $f) + { + $this->error = $f; + } + + /** + * Whether to use PHP's passthru() function instead of exec() + * + * @param boolean $passthru If passthru shall be used + * + * @return void + */ + public function setPassthru($passthru) + { + $this->passthru = (bool) $passthru; + } + + /** + * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE + * + * @param boolean $logOutput If output shall be logged visibly + * + * @return void + */ + public function setLogoutput($logOutput) + { + $this->logOutput = (bool) $logOutput; + } + + /** + * Whether to suppress all output and run in the background. + * + * @param boolean $spawn If the command is to be run in the background + * + * @return void + */ + public function setSpawn($spawn) + { + $this->spawn = (bool) $spawn; + } + + /** + * Whether to check the return code. + * + * @param boolean $checkreturn If the return code shall be checked + * + * @return void + */ + public function setCheckreturn($checkreturn) + { + $this->checkreturn = (bool) $checkreturn; + } + + /** + * The name of property to set to return value from exec() call. + * + * @param string $prop Property name + * + * @return void + */ + public function setReturnProperty($prop) + { + $this->returnProperty = $prop; + } + + /** + * The name of property to set to output value from exec() call. + * + * @param string $prop Property name + * + * @return void + */ + public function setOutputProperty($prop) + { + $this->outputProperty = $prop; + } + + /** + * Set level of log messages generated (default = verbose) + * + * @param string $level Log level + * + * @return void + */ + public function setLevel($level) + { + switch ($level) { + case 'error': + $this->logLevel = Project::MSG_ERR; + break; + case 'warning': + $this->logLevel = Project::MSG_WARN; + break; + case 'info': + $this->logLevel = Project::MSG_INFO; + break; + case 'verbose': + $this->logLevel = Project::MSG_VERBOSE; + break; + case 'debug': + $this->logLevel = Project::MSG_DEBUG; + break; + default: + throw new BuildException( + sprintf('Unknown log level "%s"', $level) + ); + } + } + + /** + * Creates a nested <arg> tag. + * + * @return CommandlineArgument Argument object + */ + public function createArg() + { + return $this->commandline->createArgument(); + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/system/FailTask.php b/buildscripts/phing/classes/phing/tasks/system/FailTask.php new file mode 100755 index 00000000..92306842 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/FailTask.php @@ -0,0 +1,118 @@ +<?php +/* + * $Id: 260a7f3caa499a33f0d3982f9a5cc9db222475a6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Exits the active build, giving an additional message + * if available. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Nico Seessle <nico@seessle.de> (Ant) + * @version $Id$ + * @package phing.tasks.system + */ +class FailTask extends Task { + + private $message; + private $ifCondition; + private $unlessCondition; + + /** + * A message giving further information on why the build exited. + * + * @param string $value message to output + */ + public function setMsg($value) { + $this->setMessage($value); + } + + /** + * A message giving further information on why the build exited. + * + * @param value message to output + */ + public function setMessage($value) { + $this->message = $value; + } + + /** + * Only fail if a property of the given name exists in the current project. + * @param c property name + */ + public function setIf($c) { + $this->ifCondition = $c; + } + + /** + * Only fail if a property of the given name does not + * exist in the current project. + * @param c property name + */ + public function setUnless($c) { + $this->unlessCondition = $c; + } + + /** + * @throws BuildException + */ + public function main() { + if ($this->testIfCondition() && $this->testUnlessCondition()) { + if ($this->message !== null) { + throw new BuildException($this->message); + } else { + throw new BuildException("No message"); + } + } + } + + /** + * Set a multiline message. + */ + public function addText($msg) { + if ($this->message === null) { + $this->message = ""; + } + $this->message .= $this->project->replaceProperties($msg); + } + + /** + * @return boolean + */ + private function testIfCondition() { + if ($this->ifCondition === null || $this->ifCondition === "") { + return true; + } + + return $this->project->getProperty($this->ifCondition) !== null; + } + + /** + * @return boolean + */ + private function testUnlessCondition() { + if ($this->unlessCondition === null || $this->unlessCondition === "") { + return true; + } + return $this->project->getProperty($this->unlessCondition) === null; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/ForeachTask.php b/buildscripts/phing/classes/phing/tasks/system/ForeachTask.php new file mode 100755 index 00000000..e9b556d3 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ForeachTask.php @@ -0,0 +1,320 @@ +<?php +/* + * $Id: 7acb78ab792426fa7d4edec53496cff8da5923eb $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/FileSystem.php'; +include_once 'phing/mappers/FileNameMapper.php'; +include_once 'phing/tasks/system/PhingTask.php'; + +/** + * <foreach> task + * + * Task definition for the foreach task. This task takes a list with + * delimited values, and executes a target with set param. + * + * Usage: + * <foreach list="values" target="targ" param="name" delimiter="|" /> + * + * Attributes: + * list --> The list of values to process, with the delimiter character, + * indicated by the "delimiter" attribute, separating each value. + * target --> The target to call for each token, passing the token as the + * parameter with the name indicated by the "param" attribute. + * param --> The name of the parameter to pass the tokens in as to the + * target. + * delimiter --> The delimiter string that separates the values in the "list" + * parameter. The default is ",". + * + * @author Jason Hines <jason@greenhell.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class ForeachTask extends Task { + + /** Delimter-separated list of values to process. */ + private $list; + + /** Name of parameter to pass to callee */ + private $param; + + /** Name of absolute path parameter to pass to callee */ + private $absparam; + + /** Delimiter that separates items in $list */ + private $delimiter = ','; + + /** + * PhingCallTask that will be invoked w/ calleeTarget. + * @var PhingCallTask + */ + private $callee; + + /** Array of filesets */ + private $filesets = array(); + + /** Instance of mapper **/ + private $mapperElement; + + /** + * Array of filelists + * @var array + */ + private $filelists = array(); + + /** + * Target to execute. + * @var string + */ + private $calleeTarget; + + /** + * Total number of files processed + * @var integer + */ + private $total_files = 0; + + /** + * Total number of directories processed + * @var integer + */ + private $total_dirs = 0; + + + function init() { + $this->callee = $this->project->createTask("phingcall"); + $this->callee->setOwningTarget($this->getOwningTarget()); + $this->callee->setTaskName($this->getTaskName()); + $this->callee->setLocation($this->getLocation()); + $this->callee->init(); + } + + /** + * This method does the work. + * @return void + */ + function main() { + if ($this->list === null && count($this->filesets) == 0 && count($this->filelists) == 0) { + throw new BuildException("Need either list, nested fileset or nested filelist to iterate through"); + } + if ($this->param === null) { + throw new BuildException("You must supply a property name to set on each iteration in param"); + } + if ($this->calleeTarget === null) { + throw new BuildException("You must supply a target to perform"); + } + + $callee = $this->callee; + $callee->setTarget($this->calleeTarget); + $callee->setInheritAll(true); + $callee->setInheritRefs(true); + $mapper = null; + + if ($this->mapperElement !== null) { + $mapper = $this->mapperElement->getImplementation(); + } + + if (trim($this->list)) { + $arr = explode($this->delimiter, $this->list); + + foreach ($arr as $value) { + $value = trim($value); + $premapped = ''; + if ($mapper !== null) { + $premapped = $value; + $value = $mapper->main($value); + if ($value === null) { + continue; + } + $value = array_shift($value); + } + $this->log("Setting param '$this->param' to value '$value'" . ($premapped ? " (mapped from '$premapped')" : ''), Project::MSG_VERBOSE); + $prop = $callee->createProperty(); + $prop->setOverride(true); + $prop->setName($this->param); + $prop->setValue($value); + $callee->main(); + } + } + + // filelists + foreach ($this->filelists as $fl) { + $srcFiles = $fl->getFiles($this->project); + + $this->process($callee, $fl->getDir($this->project), $srcFiles, array()); + } + + // filesets + foreach ($this->filesets as $fs) { + $ds = $fs->getDirectoryScanner($this->project); + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + $this->process($callee, $fs->getDir($this->project), $srcFiles, $srcDirs); + } + + $this->log("Processed {$this->total_dirs} directories and {$this->total_files} files", Project::MSG_VERBOSE); + } + + /** + * Processes a list of files & directories + * + * @param Task $callee + * @param PhingFile $fromDir + * @param array $srcFiles + * @param array $srcDirs + */ + protected function process(Task $callee, PhingFile $fromDir, $srcFiles, $srcDirs) + { + $mapper = null; + + if ($this->mapperElement !== null) { + $mapper = $this->mapperElement->getImplementation(); + } + + $filecount = count($srcFiles); + $this->total_files += $filecount; + + for ($j = 0; $j < $filecount; $j++) { + $value = $srcFiles[$j]; + $premapped = ""; + + if ($this->absparam) { + $prop = $callee->createProperty(); + $prop->setOverride(true); + $prop->setName($this->absparam); + $prop->setValue($fromDir . FileSystem::getFileSystem()->getSeparator() . $value); + } + + if ($mapper !== null) { + $premapped = $value; + $value = $mapper->main($value); + if ($value === null) { + continue; + } + $value = array_shift($value); + } + + if ($this->param) { + $this->log("Setting param '$this->param' to value '$value'" . ($premapped ? " (mapped from '$premapped')" : ''), Project::MSG_VERBOSE); + $prop = $callee->createProperty(); + $prop->setOverride(true); + $prop->setName($this->param); + $prop->setValue($value); + } + + $callee->main(); + } + + $dircount = count($srcDirs); + $this->total_dirs += $dircount; + + for ($j = 0; $j < $dircount; $j++) { + $value = $srcDirs[$j]; + $premapped = ""; + + if ($this->absparam) { + $prop = $callee->createProperty(); + $prop->setOverride(true); + $prop->setName($this->absparam); + $prop->setValue($fromDir . FileSystem::getFileSystem()->getSeparator() . $value); + } + + if ($mapper !== null) { + $premapped = $value; + $value = $mapper->main($value); + if ($value === null) { + continue; + } + $value = array_shift($value); + } + + if ($this->param) { + $this->log("Setting param '$this->param' to value '$value'" . ($premapped ? " (mapped from '$premapped')" : ''), Project::MSG_VERBOSE); + $prop = $callee->createProperty(); + $prop->setOverride(true); + $prop->setName($this->param); + $prop->setValue($value); + } + + $callee->main(); + } + } + + function setList($list) { + $this->list = (string) $list; + } + + function setTarget($target) { + $this->calleeTarget = (string) $target; + } + + function setParam($param) { + $this->param = (string) $param; + } + + function setAbsparam($absparam) { + $this->absparam = (string) $absparam; + } + + function setDelimiter($delimiter) { + $this->delimiter = (string) $delimiter; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + */ + function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Nested creator, creates one Mapper for this task + * + * @access public + * @return object The created Mapper type object + * @throws BuildException + */ + function createMapper() { + if ($this->mapperElement !== null) { + throw new BuildException("Cannot define more than one mapper", $this->location); + } + $this->mapperElement = new Mapper($this->project); + return $this->mapperElement; + } + + /** + * @return Property + */ + function createProperty() { + return $this->callee->createProperty(); + } + + /** + * Supports embedded <filelist> element. + * @return FileList + */ + public function createFileList() { + $num = array_push($this->filelists, new FileList()); + return $this->filelists[$num-1]; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/IfTask.php b/buildscripts/phing/classes/phing/tasks/system/IfTask.php new file mode 100644 index 00000000..cff06ce1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/IfTask.php @@ -0,0 +1,227 @@ +<?php + +/* + * $Id: 4452f066ac71d51d3e2521d39cdee2caf4f7e9cc $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/ConditionBase.php'; +require_once 'phing/tasks/system/SequentialTask.php'; + +/** + * Perform some tasks based on whether a given condition holds true or + * not. + * + * <p>This task is heavily based on the Condition framework that can + * be found in Ant 1.4 and later, therefore it cannot be used in + * conjunction with versions of Ant prior to 1.4.</p> + * + * <p>This task doesn't have any attributes, the condition to test is + * specified by a nested element - see the documentation of your + * <code><condition></code> task (see + * <a href="http://jakarta.apache.org/ant/manual/CoreTasks/condition.html">the + * online documentation</a> for example) for a complete list of nested + * elements.</p> + * + * <p>Just like the <code><condition></code> task, only a single + * condition can be specified - you combine them using + * <code><and></code> or <code><or></code> conditions.</p> + * + * <p>In addition to the condition, you can specify three different + * child elements, <code><elseif></code>, <code><then></code> and + * <code><else></code>. All three subelements are optional. + * + * Both <code><then></code> and <code><else></code> must not be + * used more than once inside the if task. Both are + * containers for Ant tasks, just like Ant's + * <code><parallel></code> and <code><sequential></code> + * tasks - in fact they are implemented using the same class as Ant's + * <code><sequential></code> task.</p> + * + * The <code><elseif></code> behaves exactly like an <code><if></code> + * except that it cannot contain the <code><else></code> element + * inside of it. You may specify as may of these as you like, and the + * order they are specified is the order they are evaluated in. If the + * condition on the <code><if></code> is false, then the first + * <code><elseif></code> who's conditional evaluates to true + * will be executed. The <code><else></code> will be executed + * only if the <code><if></code> and all <code><elseif></code> + * conditions are false. + * + * <p>Use the following task to define the <code><if></code> + * task before you use it the first time:</p> + * + * <pre><code> + * <taskdef name="if" classname="net.sf.antcontrib.logic.IfTask" /> + * </code></pre> + * + * <h3>Crude Example</h3> + * + * <code> + * <if> + * <equals arg1="${foo}" arg2="bar" /> + * <then> + * <echo message="The value of property foo is bar" /> + * </then> + * <else> + * <echo message="The value of property foo is not bar" /> + * </else> + * </if> + * </code> + * + * <code> + * <if> + * <equals arg1="${foo}" arg2="bar" /> + * <then> + * <echo message="The value of property foo is 'bar'" /> + * </then> + * + * <elseif> + * <equals arg1="${foo}" arg2="foo" /> + * <then> + * <echo message="The value of property foo is 'foo'" /> + * </then> + * </elseif> + * + * <else> + * <echo message="The value of property foo is not 'foo' or 'bar'" /> + * </else> + * </if> + * </code> + * + * @author <a href="mailto:stefan.bodewig@freenet.de">Stefan Bodewig</a> + * @package phing.tasks.system + */ +class IfTask extends ConditionBase { + + + private $thenTasks = null; + private $elseIfTasks = array(); + private $elseTasks = null; + + /*** + * A nested Else if task + */ + public function addElseIf(ElseIfTask $ei) + { + $this->elseIfTasks[] = $ei; + } + + /** + * A nested <then> element - a container of tasks that will + * be run if the condition holds true. + * + * <p>Not required.</p> + */ + public function addThen(SequentialTask $t) { + if ($this->thenTasks != null) { + throw new BuildException("You must not nest more than one <then> into <if>"); + } + $this->thenTasks = $t; + } + + /** + * A nested <else> element - a container of tasks that will + * be run if the condition doesn't hold true. + * + * <p>Not required.</p> + */ + public function addElse(SequentialTask $e) { + if ($this->elseTasks != null) { + throw new BuildException("You must not nest more than one <else> into <if>"); + } + $this->elseTasks = $e; + } + + public function main() { + + if ($this->countConditions() > 1) { + throw new BuildException("You must not nest more than one condition into <if>"); + } + if ($this->countConditions() < 1) { + throw new BuildException("You must nest a condition into <if>"); + } + $conditions = $this->getConditions(); + $c = $conditions[0]; + + if ($c->evaluate()) { + if ($this->thenTasks != null) { + $this->thenTasks->main(); + } + } else { + $done = false; + $sz = count($this->elseIfTasks); + for($i=0; $i < $sz && !$done; $i++) { + $ei = $this->elseIfTasks[$i]; + if ($ei->evaluate()) { + $done = true; + $ei->main(); + } + } + + if (!$done && $this->elseTasks != null) { + $this->elseTasks->main(); + } + } + } +} + +/** + * "Inner" class for IfTask. + * This class has same basic structure as the IfTask, although of course it doesn't support <else> tags. + * + * @package phing.tasks.system + */ +class ElseIfTask extends ConditionBase { + + private $thenTasks = null; + + public function addThen(SequentialTask $t) { + if ($this->thenTasks != null) { + throw new BuildException("You must not nest more than one <then> into <elseif>"); + } + $this->thenTasks = $t; + } + + /** + * @return boolean + */ + public function evaluate() { + + if ($this->countConditions() > 1) { + throw new BuildException("You must not nest more than one condition into <elseif>"); + } + if ($this->countConditions() < 1) { + throw new BuildException("You must nest a condition into <elseif>"); + } + + $conditions = $this->getConditions(); + $c = $conditions[0]; + + return $c->evaluate(); + } + + /** + * + */ + public function main() { + if ($this->thenTasks != null) { + $this->thenTasks->main(); + } + } + } diff --git a/buildscripts/phing/classes/phing/tasks/system/ImportTask.php b/buildscripts/phing/classes/phing/tasks/system/ImportTask.php new file mode 100755 index 00000000..760380eb --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ImportTask.php @@ -0,0 +1,136 @@ +<?php +/* + * $Id: 88eafadfcd59c0ce28f122926b7d5706b807a8e3 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/system/io/FileSystem.php'; +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/parser/ProjectConfigurator.php'; + +/** + * Imports another build file into the current project. + * + * Targets and properties of the imported file can be overrridden + * by targets and properties of the same name declared in the importing file. + * + * The imported file will have a new synthetic property of + * "phing.file.<projectname>" declared which gives the full path to the + * imported file. Additionally each target in the imported file will be + * declared twice: once with the normal name and once with "<projectname>." + * prepended. The "<projectname>.<targetname>" synthetic targets allow the + * importing file a mechanism to call the imported files targets as + * dependencies or via the <phing> or <phingcall> task mechanisms. + * + * @author Bryan Davis <bpd@keynetics.com> + * @version $Id: 88eafadfcd59c0ce28f122926b7d5706b807a8e3 $ + * @package phing.tasks.system + */ +class ImportTask extends Task { + + /** + * @var FileSystem + */ + protected $fs; + + /** + * @var PhingFile + */ + protected $file = null; + + /** + * @var bool + */ + protected $optional = false; + + /** + * Initialize task. + * @return void + */ + public function init () { + $this->fs = FileSystem::getFileSystem(); + } //end init + + + /** + * Set the file to import. + * @param string $f Path to file + * @return void + */ + public function setFile ($f) { + $this->file = $f; + } + + /** + * Is this include optional? + * @param bool $opt If true, do not stop the build if the file does not + * exist + * @return void + */ + public function setOptional ($opt) { + $this->optional = $opt; + } + + /** + * Parse a Phing build file and copy the properties, tasks, data types and + * targets it defines into the current project. + * + * @return void + */ + public function main () { + if (!isset($this->file)) { + throw new BuildException("Missing attribute 'file'"); + } + + $file = new PhingFile($this->file); + if (!$file->isAbsolute()) { + $file = new PhingFile($this->project->getBasedir(), $this->file); + } + if (!$file->exists()) { + $msg = "Unable to find build file: {$file->getPath()}"; + if ($this->optional) { + $this->log($msg . '... skipped'); + return; + } else { + throw new BuildException($msg); + } + } + + $ctx = $this->project->getReference("phing.parsing.context"); + $cfg = $ctx->getConfigurator(); + if (null !== $cfg && $cfg->isParsing()) { + // because there isn't a top level implicit target in phing like there is + // in Ant 1.6, we will be called as soon as our xml is parsed. This isn't + // really what we want to have happen. Instead we will register ourself + // with the parse context to be called at the end of the current file's + // parse phase. + $cfg->delayTaskUntilParseEnd($this); + + } else { + // Import xml file into current project scope + // Since this is delayed until after the importing file has been + // processed, the properties and targets of this new file may not take + // effect if they have alreday been defined in the outer scope. + $this->log("Importing configuration from {$file->getName()}", Project::MSG_VERBOSE); + ProjectConfigurator::configureProject($this->project, $file); + $this->log("Configuration imported.", Project::MSG_VERBOSE); + } + } //end main + +} //end ImportTask diff --git a/buildscripts/phing/classes/phing/tasks/system/IncludePathTask.php b/buildscripts/phing/classes/phing/tasks/system/IncludePathTask.php new file mode 100755 index 00000000..807a3cad --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/IncludePathTask.php @@ -0,0 +1,115 @@ +<?php + +/* + * $Id: eaa0d6c5c7fb908d419cd6e22c7eccbf01506da7 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/types/Path.php'; + +/** + * Adds a normalized path to the PHP include_path. + * + * This provides a way to alter the include_path without editing any global php.ini settings + * or PHP_CLASSPATH environment variable. + * + * <code> + * <includepath classpath="new/path/here"/> + * </code> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class IncludePathTask extends Task { + + /** + * Classname of task to register. + * This can be a dot-path -- relative to a location on PHP include_path. + * E.g. path.to.MyClass -> path/to/MyClass.php + * @var string + */ + private $classname; + + /** + * Path to add to PHP include_path to aid in finding specified class. + * @var Path + */ + private $classpath; + + /** + * Refid to already defined classpath + */ + private $classpathId; + + /** + * Set the classpath to be used when searching for component being defined + * + * @param Path $classpath An Path object containing the classpath. + */ + public function setClasspath(Path $classpath) { + if ($this->classpath === null) { + $this->classpath = $classpath; + } else { + $this->classpath->append($classpath); + } + } + + /** + * Create the classpath to be used when searching for component being defined + */ + public function createClasspath() { + if ($this->classpath === null) { + $this->classpath = new Path($this->project); + } + return $this->classpath->createPath(); + } + + /** + * Reference to a classpath to use when loading the files. + */ + public function setClasspathRef(Reference $r) { + $this->classpathId = $r->getRefId(); + $this->createClasspath()->setRefid($r); + } + + + /** Main entry point */ + public function main() { + + // Apparently casting to (string) no longer invokes __toString() automatically. + if (is_object($this->classpath)) { + $this->classpath = $this->classpath->__toString(); + } + + if (empty($this->classpath)) { + throw new BuildException("Provided classpath was empty."); + } + + $curr_parts = explode(PATH_SEPARATOR, get_include_path()); + $add_parts = explode(PATH_SEPARATOR, $this->classpath); + $new_parts = array_diff($add_parts, $curr_parts); + + if ($new_parts) { + $this->log("Prepending new include_path components: " . implode(PATH_SEPARATOR, $new_parts), Project::MSG_VERBOSE); + set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts))); + } + + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/InputTask.php b/buildscripts/phing/classes/phing/tasks/system/InputTask.php new file mode 100755 index 00000000..3c7168fd --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/InputTask.php @@ -0,0 +1,150 @@ +<?php +/* + * $Id: ae986f84ca9d952b0e14f2df9f21657213ef5e47 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/input/InputRequest.php'; +include_once 'phing/input/YesNoInputRequest.php'; +include_once 'phing/input/MultipleChoiceInputRequest.php'; + +/** + * Reads input from the InputHandler. + * + * @see Project::getInputHandler() + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Ulrich Schmidt <usch@usch.net> (Ant) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @version $Id: ae986f84ca9d952b0e14f2df9f21657213ef5e47 $ + * @package phing.tasks.system + */ +class InputTask extends Task { + + private $validargs; + private $message = ""; // required + private $propertyName; // required + private $defaultValue; + private $promptChar; + + /** + * Defines valid input parameters as comma separated strings. If set, input + * task will reject any input not defined as accepted and requires the user + * to reenter it. Validargs are case sensitive. If you want 'a' and 'A' to + * be accepted you need to define both values as accepted arguments. + * + * @param validargs A comma separated String defining valid input args. + */ + public function setValidargs ($validargs) { + $this->validargs = $validargs; + } + + /** + * Defines the name of a property to be set from input. + * + * @param string $name Name for the property to be set from input + */ + public function setPropertyName($name) { + $this->propertyName = $name; + } + + /** + * Sets the Message which gets displayed to the user during the build run. + * @param message The message to be displayed. + */ + public function setMessage ($message) { + $this->message = $message; + } + + /** + * Set a multiline message. + */ + public function addText($msg) { + $this->message .= $this->project->replaceProperties($msg); + } + + /** + * Add a default value. + * @param string $v + */ + public function setDefaultValue($v) { + $this->defaultValue = $v; + } + + /** + * Set the character/string to use for the prompt. + * @param string $c + */ + public function setPromptChar($c) { + $this->promptChar = $c; + } + + /** + * Actual method executed by phing. + * @throws BuildException + */ + public function main() { + + if ($this->propertyName === null) { + throw new BuildException("You must specify a value for propertyName attribute."); + } + + if ($this->message === "") { + throw new BuildException("You must specify a message for input task."); + } + + if ($this->validargs !== null) { + $accept = preg_split('/[\s,]+/', $this->validargs); + + // is it a boolean (yes/no) inputrequest? + $yesno = false; + if (count($accept) == 2) { + $yesno = true; + foreach($accept as $ans) { + if(!StringHelper::isBoolean($ans)) { + $yesno = false; + break; + } + } + } + if ($yesno) $request = new YesNoInputRequest($this->message, $accept); + else $request = new MultipleChoiceInputRequest($this->message, $accept); + } else { + $request = new InputRequest($this->message); + } + + // default default is curr prop value + $request->setDefaultValue($this->project->getProperty($this->propertyName)); + + $request->setPromptChar($this->promptChar); + + // unless overridden... + if ($this->defaultValue !== null) { + $request->setDefaultValue($this->defaultValue); + } + + $this->project->getInputHandler()->handleInput($request); + + $value = $request->getInput(); + + if ($value !== null) { + $this->project->setUserProperty($this->propertyName, $value); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/LoadFileTask.php b/buildscripts/phing/classes/phing/tasks/system/LoadFileTask.php new file mode 100644 index 00000000..ff7b7d3b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/LoadFileTask.php @@ -0,0 +1,119 @@ +<?php +/* + * $Id: d4796a6eb57300eca59f8b6f79e3dbcb08d1e346 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ +require_once 'phing/Task.php'; + +/** + * LoadFileTask + * + * Loads a (text) file and stores the contents in a property. + * Supports filterchains. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: d4796a6eb57300eca59f8b6f79e3dbcb08d1e346 $ + * @package phing.tasks.ext + */ +class LoadFileTask extends Task +{ + /** + * File to read + * @var PhingFile file + */ + private $file; + + /** + * Property to be set + * @var string $property + */ + private $property; + + /** + * Array of FilterChain objects + * @var FilterChain[] + */ + private $filterChains = array(); + + /** + * Set file to read + * @param PhingFile $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Convenience setter to maintain Ant compatibility (@see setFile()) + * @param PhingFile $file + */ + public function setSrcFile($srcFile) + { + $this->file = $srcFile; + } + + /** + * Set name of property to be set + * @param $property + * @return void + */ + public function setProperty($property) + { + $this->property = $property; + } + + /** + * Creates a filterchain + * + * @return object The created filterchain object + */ + function createFilterChain() { + $num = array_push($this->filterChains, new FilterChain($this->project)); + return $this->filterChains[$num-1]; + } + + /** + * Main method + * + * @return void + * @throws BuildException + */ + public function main() + { + if (empty($this->file)) { + throw new BuildException("Attribute 'file' required", $this->getLocation()); + } + + if (empty($this->property)) { + throw new BuildException("Attribute 'property' required", $this->getLocation()); + } + + // read file (through filterchains) + $contents = ""; + + $reader = FileUtils::getChainedReader(new FileReader($this->file), $this->filterChains, $this->project); + while(-1 !== ($buffer = $reader->read())) { + $contents .= $buffer; + } + $reader->close(); + + // publish as property + $this->project->setProperty($this->property, $contents); + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/system/MatchingTask.php b/buildscripts/phing/classes/phing/tasks/system/MatchingTask.php new file mode 100755 index 00000000..b04be2e1 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/MatchingTask.php @@ -0,0 +1,361 @@ +<?php +/* + * $Id: 1e1f6274f400b90c2344c8cdb5d3711030b8f44a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/types/selectors/SelectorContainer.php'; +include_once 'phing/types/FileSet.php'; +include_once 'phing/types/PatternSet.php'; +include_once 'phing/util/DirectoryScanner.php'; + +/** + * This is an abstract task that should be used by all those tasks that + * require to include or exclude files based on pattern matching. + * + * This is very closely based on the ANT class of the same name. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Arnout J. Kuiper <ajkuiper@wxs.nl> (Ant) + * @author Stefano Mazzocchi <stefano@apache.org> (Ant) + * @author Sam Ruby <rubys@us.ibm.com> (Ant) + * @author Jon S. Stevens <jon@clearink.com> (Ant + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @version $Id: 1e1f6274f400b90c2344c8cdb5d3711030b8f44a $ + * @package phing.tasks.system + */ +abstract class MatchingTask extends Task implements SelectorContainer { + + /** @var boolean */ + protected $useDefaultExcludes = true; + + /** @var FileSet */ + protected $fileset; + + /** + * Create instance; set fileset to new FileSet. + */ + public function __construct() { + $this->fileset = new FileSet(); + } + + /** + * @see ProjectComponent::setProject() + */ + public function setProject($project) { + parent::setProject($project); + $this->fileset->setProject($project); + } + + /** + * add a name entry on the include list + * @return PatternSetNameEntry + */ + public function createInclude() { + return $this->fileset->createInclude(); + } + + /** + * add a name entry on the include files list + * @return PatternSetNameEntry + */ + public function createIncludesFile() { + return $this->fileset->createIncludesFile(); + } + + /** + * add a name entry on the exclude list + * @return PatternSetNameEntry + */ + public function createExclude() { + return $this->fileset->createExclude(); + } + + /** + * add a name entry on the include files list + * @return PatternSetNameEntry + */ + public function createExcludesFile() { + return $this->fileset->createExcludesFile(); + } + + /** + * add a set of patterns + * @return PatternSet + */ + public function createPatternSet() { + return $this->fileset->createPatternSet(); + } + + /** + * Sets the set of include patterns. Patterns may be separated by a comma + * or a space. + * + * @param string $includes the string containing the include patterns + * @return void + */ + public function setIncludes($includes) { + $this->fileset->setIncludes($includes); + } + + /** + * Sets the set of exclude patterns. Patterns may be separated by a comma + * or a space. + * + * @param string $excludes the string containing the exclude patterns + */ + public function setExcludes($excludes) { + $this->fileset->setExcludes($excludes); + } + + + /** + * Sets whether default exclusions should be used or not. + * + * @param boolean $useDefaultExcludes "true"|"on"|"yes" when default exclusions + * should be used, "false"|"off"|"no" when they + * shouldn't be used. + */ + public function setDefaultexcludes($useDefaultExcludes) { + $this->useDefaultExcludes = (boolean) $useDefaultExcludes; + } + + /** + * Returns the directory scanner needed to access the files to process. + * @return DirectoryScanner + */ + protected function getDirectoryScanner(PhingFile $baseDir) { + $this->fileset->setDir($baseDir); + $this->fileset->setDefaultexcludes($this->useDefaultExcludes); + return $this->fileset->getDirectoryScanner($this->project); + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param PhingFile $includesfile A string containing the filename to fetch + * the include patterns from. + * @return void + */ + public function setIncludesfile(PhingFile $includesfile) { + $this->fileset->setIncludesfile(includesfile); + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param PhingFile $excludesfile A string containing the filename to fetch + * the include patterns from. + * @return void + */ + public function setExcludesfile(PhingFile $excludesfile) { + $this->fileset->setExcludesfile($excludesfile); + } + + /** + * Sets case sensitivity of the file system + * + * @param boolean $isCaseSensitive "true"|"on"|"yes" if file system is case + * sensitive, "false"|"off"|"no" when not. + * @return void + */ + public function setCaseSensitive($isCaseSensitive) { + $this->fileset->setCaseSensitive($isCaseSensitive); + } + + /** + * Sets whether or not symbolic links should be followed. + * + * @param boolean $followSymlinks whether or not symbolic links should be followed + * @return void + */ + public function setFollowSymlinks($followSymlinks) { + $this->fileset->setFollowSymlinks($followSymlinks); + } + + /** + * Indicates whether there are any selectors here. + * + * @return boolean Whether any selectors are in this container + */ + public function hasSelectors() { + return $this->fileset->hasSelectors(); + } + + /** + * Gives the count of the number of selectors in this container + * + * @return int The number of selectors in this container + */ + public function selectorCount() { + return $this->fileset->selectorCount(); + } + + /** + * Returns the set of selectors as an array. + * + * @return array FileSelector[] An array of selectors in this container + */ + public function getSelectors(Project $p) { + return $this->fileset->getSelectors($p); + } + + /** + * Returns an enumerator for accessing the set of selectors. + * + * @return an enumerator that goes through each of the selectors + */ + public function selectorElements() { + return $this->fileset->selectorElements(); + } + + /** + * Add a new selector into this container. + * + * @param FileSelector $selector the new selector to add + * @return void + */ + public function appendSelector(FileSelector $selector) { + $this->fileset->appendSelector($selector); + } + + /* Methods below all add specific selectors */ + + /** + * add a "Select" selector entry on the selector list + * @return SelectSelector + */ + public function createSelector() { + return $this->fileset->createSelector(); + } + + /** + * add an "And" selector entry on the selector list + * @return AndSelector + */ + public function createAnd() { + return $this->fileset->createAnd(); + } + + /** + * add an "Or" selector entry on the selector list + * @return void + */ + public function createOr() { + return $this->fileset->createOr(); + } + + /** + * add a "Not" selector entry on the selector list + * @return NotSelector + */ + public function createNot() { + return $this->fileset->createNot(); + } + + /** + * add a "None" selector entry on the selector list + * @return NoneSelector + */ + public function createNone() { + return $this->fileset->createNone(); + } + + /** + * add a majority selector entry on the selector list + * @return MajoritySelector + */ + public function createMajority() { + return $this->fileset->createMajority(); + } + + /** + * add a selector date entry on the selector list + * @return DateSelector + */ + public function createDate() { + return $this->fileset->addDate(); + } + + /** + * add a selector size entry on the selector list + * @return SizeSelector + */ + public function createSize() { + return $this->fileset->createSize(); + } + + /** + * add a selector filename entry on the selector list + * @return FilenameSelector + */ + public function createFilename() { + return $this->fileset->createFilename(); + } + + /** + * add an extended selector entry on the selector list + * @return ExtendSelector + */ + public function createCustom() { + return $this->fileset->createCustom(); + } + + /** + * add a contains selector entry on the selector list + * @return ContainsSelector + */ + public function createContains() { + return $this->fileset->createContains(); + } + + /** + * add a present selector entry on the selector list + * @return PresentSelector + */ + public function createPresent() { + return $this->fileset->createPresent(); + } + + /** + * add a depth selector entry on the selector list + * @return DepthSelector + */ + public function createDepth() { + return $this->fileset->createDepth(); + } + + /** + * add a depends selector entry on the selector list + * @return DependSelector + */ + public function createDepend() { + return $this->fileset->createDepend(); + } + + /** + * Accessor for the implict fileset. + * + * @return FileSet + */ + protected final function getImplicitFileSet() { + return $this->fileset; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/MkdirTask.php b/buildscripts/phing/classes/phing/tasks/system/MkdirTask.php new file mode 100755 index 00000000..934b991f --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/MkdirTask.php @@ -0,0 +1,79 @@ +<?php +/* + * $Id: e7e3dbd896d8d46cd8694fca10d1d9a7b3ce54fc $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/system/io/PhingFile.php'; + +/** + * Task to create a directory. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id$ + * @package phing.tasks.system + */ +class MkdirTask extends Task { + + /** directory to create*/ + private $dir; + + /** + * Mode to create directory with + * @var integer + */ + private $mode = 0755; + + /** + * create the directory and all parents + * + * @throws BuildException if dir is somehow invalid, or creation failed. + */ + function main() { + if ($this->dir === null) { + throw new BuildException("dir attribute is required", $this->location); + } + if ($this->dir->isFile()) { + throw new BuildException("Unable to create directory as a file already exists with that name: " . $this->dir->getAbsolutePath()); + } + if (!$this->dir->exists()) { + $result = $this->dir->mkdirs($this->mode); + if (!$result) { + $msg = "Directory " . $this->dir->getAbsolutePath() . " creation was not successful for an unknown reason"; + throw new BuildException($msg, $this->location); + } + $this->log("Created dir: " . $this->dir->getAbsolutePath()); + } + } + + /** the directory to create; required. */ + function setDir(PhingFile $dir) { + $this->dir = $dir; + } + + /** + * Sets mode to create directory with + * @param mixed $mode + */ + function setMode($mode) + { + $this->mode = base_convert((int) $mode, 8, 10); + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/MoveTask.php b/buildscripts/phing/classes/phing/tasks/system/MoveTask.php new file mode 100755 index 00000000..e7041464 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/MoveTask.php @@ -0,0 +1,202 @@ +<?php +/* + * $Id: 01e6d627d455729bfed47d603188d8c56f54d9e5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/CopyTask.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/IOException.php'; + +/** + * Moves a file or directory to a new file or directory. + * + * By default, the destination file is overwritten if it + * already exists. When overwrite is turned off, then files + * are only moved if the source file is newer than the + * destination file, or when the destination file does not + * exist. + * + * Source files and directories are only deleted when the file or + * directory has been copied to the destination successfully. + * + * @version $Id$ + * @package phing.tasks.system + */ +class MoveTask extends CopyTask { + + function __construct() { + parent::__construct(); + $this->forceOverwrite = true; + } + + /** + * Validates attributes coming in from XML + * + * @access private + * @return void + * @throws BuildException + */ + protected function validateAttributes() { + if ($this->file !== null && $this->file->isDirectory()) { + if (($this->destFile !== null && $this->destDir !== null) + || ($this->destFile === null && $this->destDir === null)) { + throw new BuildException("One and only one of tofile and todir must be set."); + } + + if ($this->destFile === null) + { + $this->destFile = new PhingFile($this->destDir, $this->file->getName()); + } + + if ($this->destDir === null) + { + $this->destDir = $this->destFile->getParentFile(); + } + + $this->completeDirMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath(); + + $this->file = null; + } else { + parent::validateAttributes(); + } + } + + protected function doWork() { + if (count($this->completeDirMap) > 0) + { + foreach ($this->completeDirMap as $from => $to) + { + $f = new PhingFile($from); + $d = new PhingFile($to); + + $moved = false; + try { // try to rename + $this->log("Attempting to rename $from to $to", $this->verbosity); + $this->fileUtils->copyFile($f, $d, $this->forceOverwrite, $this->preserveLMT, $this->filterChains, $this->getProject(), $this->mode); + $f->delete(true); + $moved = true; + } catch (IOException $ioe) { + $moved = false; + $this->logError("Failed to rename $from to $to: " . $ioe->getMessage()); + } + } + } + + $copyMapSize = count($this->fileCopyMap); + if ($copyMapSize > 0) { + // files to move + $this->log("Moving $copyMapSize files to " . $this->destDir->getAbsolutePath()); + + foreach($this->fileCopyMap as $from => $to) { + if ($from == $to) { + $this->log("Skipping self-move of $from", $this->verbosity); + continue; + } + + $f = new PhingFile($from); + $d = new PhingFile($to); + + try { // try to move + $this->log("Moving $from to $to", $this->verbosity); + + $this->fileUtils->copyFile($f, $d, $this->forceOverwrite, $this->preserveLMT, $this->filterChains, $this->getProject(), $this->mode); + + $f->delete(); + } catch (IOException $ioe) { + $this->logError("Failed to move $from to $to: " . $ioe->getMessage(), $this->location); + } + } // foreach fileCopyMap + } // if copyMapSize + + // handle empty dirs if appropriate + if ($this->includeEmpty) { + $e = array_keys($this->dirCopyMap); + $count = 0; + foreach ($e as $dir) { + $d = new PhingFile((string) $dir); + if (!$d->exists()) { + if (!$d->mkdirs()) { + $this->logError("Unable to create directory " . $d->getAbsolutePath()); + } else { + $count++; + } + } + } + if ($count > 0) { + $this->log("moved $count empty director" . ($count == 1 ? "y" : "ies") . " to " . $this->destDir->getAbsolutePath()); + } + } + + if (count($this->filesets) > 0) { + // process filesets + foreach($this->filesets as $fs) { + $dir = $fs->getDir($this->project); + if ($this->okToDelete($dir)) { + $this->deleteDir($dir); + } + } + } + } + + /** Its only ok to delete a dir tree if there are no files in it. */ + private function okToDelete($d) { + $list = $d->listDir(); + if ($list === null) { + return false; // maybe io error? + } + + foreach($list as $s) { + $f = new PhingFile($d, $s); + if ($f->isDirectory()) { + if (!$this->okToDelete($f)) { + return false; + } + } else { + // found a file + return false; + } + } + return true; + } + + /** Go and delete the directory tree. */ + private function deleteDir($d) { + + $list = $d->listDir(); + if ($list === null) { + return; // on an io error list() can return null + } + + foreach($list as $fname) { + $f = new PhingFile($d, $fname); + if ($f->isDirectory()) { + $this->deleteDir($f); + } else { + throw new BuildException("UNEXPECTED ERROR - The file " . $f->getAbsolutePath() . " should not exist!"); + } + } + + $this->log("Deleting directory " . $d->getPath(), $this->verbosity); + try { + $d->delete(); + } catch (Exception $e) { + $this->logError("Unable to delete directory " . $d->__toString() . ": " . $e->getMessage()); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/PhingCallTask.php b/buildscripts/phing/classes/phing/tasks/system/PhingCallTask.php new file mode 100755 index 00000000..2aec4db5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/PhingCallTask.php @@ -0,0 +1,161 @@ +<?php +/* + * $Id: 5d02fa25dc18b56093884ae756b1720aafb42937 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Call another target in the same project. + * + * <samp> + * <target name="foo"> + * <phingcall target="bar"> + * <property name="property1" value="aaaaa" /> + * <property name="foo" value="baz" /> + * </phingcall> + * </target> + * + * <target name="bar" depends="init"> + * <echo message="prop is ${property1} ${foo}" /> + * </target> + * </samp> + * + * This only works as expected if neither property1 nor foo are defined in the project itself. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: 5d02fa25dc18b56093884ae756b1720aafb42937 $ + * @access public + * @package phing.tasks.system + */ +class PhingCallTask extends Task { + + /** + * The called Phing task. + * + * @var PhingTask + */ + private $callee; + + /** + * The target to call. + * + * @var string + */ + private $subTarget; + + /** + * Whether to inherit all properties from current project. + * + * @var boolean + */ + private $inheritAll = true; + + /** + * Whether to inherit refs from current project. + * + * @var boolean + */ + private $inheritRefs = false; + + /** + * If true, pass all properties to the new Phing project. + * Defaults to true. Future use. + * @param boolean new value + */ + function setInheritAll($inherit) { + $this->inheritAll = (boolean) $inherit; + } + + /** + * If true, pass all references to the new Phing project. + * Defaults to false. Future use. + * + * @param boolean new value + */ + function setInheritRefs($inheritRefs) { + $this->inheritRefs = (boolean) $inheritRefs; + } + + /** + * Alias for createProperty + * @see createProperty() + */ + function createParam() { + if ($this->callee === null) { + $this->init(); + } + return $this->callee->createProperty(); + } + + /** + * Property to pass to the invoked target. + */ + function createProperty() { + if ($this->callee === null) { + $this->init(); + } + return $this->callee->createProperty(); + } + + /** + * Target to execute, required. + */ + function setTarget($target) { + $this->subTarget = (string) $target; + } + + /** + * init this task by creating new instance of the phing task and + * configuring it's by calling its own init method. + */ + function init() { + $this->callee = $this->project->createTask("phing"); + $this->callee->setOwningTarget($this->getOwningTarget()); + $this->callee->setTaskName($this->getTaskName()); + $this->callee->setHaltOnFailure(true); + $this->callee->setLocation($this->getLocation()); + $this->callee->init(); + } + + /** + * hand off the work to the phing task of ours, after setting it up + * @throws BuildException on validation failure or if the target didn't + * execute + */ + function main() { + + $this->log("Running PhingCallTask for target '" . $this->subTarget . "'", Project::MSG_DEBUG); + if ($this->callee === null) { + $this->init(); + } + + if ($this->subTarget === null) { + throw new BuildException("Attribute target is required.", $this->getLocation()); + } + + $this->callee->setPhingfile($this->project->getProperty("phing.file")); + $this->callee->setTarget($this->subTarget); + $this->callee->setInheritAll($this->inheritAll); + $this->callee->setInheritRefs($this->inheritRefs); + $this->callee->main(); + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/PhingTask.php b/buildscripts/phing/classes/phing/tasks/system/PhingTask.php new file mode 100755 index 00000000..6ac0b5ac --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/PhingTask.php @@ -0,0 +1,627 @@ +<?php + +/* + * $Id: 2eec26f6ebaaeceb4eca76644de88bde7515f5dc $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/Task.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/types/Reference.php'; +include_once 'phing/tasks/system/PropertyTask.php'; + +/** + * Task that invokes phing on another build file. + * + * Use this task, for example, if you have nested buildfiles in your project. Unlike + * AntTask, PhingTask can even support filesets: + * + * <pre> + * <phing> + * <fileset dir="${srcdir}"> + * <include name="** /build.xml" /> <!-- space added after ** is there because of PHP comment syntax --> + * <exclude name="build.xml" /> + * </fileset> + * </phing> + * </pre> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: 2eec26f6ebaaeceb4eca76644de88bde7515f5dc $ + * @package phing.tasks.system + */ +class PhingTask extends Task { + + /** the basedir where is executed the build file */ + private $dir; + + /** build.xml (can be absolute) in this case dir will be ignored */ + private $phingFile; + + /** the target to call if any */ + protected $newTarget; + + /** should we inherit properties from the parent ? */ + private $inheritAll = true; + + /** should we inherit references from the parent ? */ + private $inheritRefs = false; + + /** the properties to pass to the new project */ + private $properties = array(); + + /** the references to pass to the new project */ + private $references = array(); + + /** The filesets that contain the files PhingTask is to be run on. */ + private $filesets = array(); + + /** the temporary project created to run the build file */ + private $newProject; + + /** Fail the build process when the called build fails? */ + private $haltOnFailure = false; + + /** + * If true, abort the build process if there is a problem with or in the target build file. + * Defaults to false. + * + * @param boolean new value + */ + public function setHaltOnFailure($hof) { + $this->haltOnFailure = (boolean) $hof; + } + + /** + * Creates a Project instance for the project to call. + * @return void + */ + public function init() { + $this->newProject = new Project(); + $tdf = $this->project->getTaskDefinitions(); + $this->newProject->addTaskDefinition("property", $tdf["property"]); + } + + /** + * Called in execute or createProperty if newProject is null. + * + * <p>This can happen if the same instance of this task is run + * twice as newProject is set to null at the end of execute (to + * save memory and help the GC).</p> + * + * <p>Sets all properties that have been defined as nested + * property elements.</p> + */ + private function reinit() { + $this->init(); + $count = count($this->properties); + for ($i = 0; $i < $count; $i++) { + $p = $this->properties[$i]; + $newP = $this->newProject->createTask("property"); + $newP->setName($p->getName()); + if ($p->getValue() !== null) { + $newP->setValue($p->getValue()); + } + if ($p->getFile() !== null) { + $newP->setFile($p->getFile()); + } + if ($p->getPrefix() !== null) { + $newP->setPrefix($p->getPrefix()); + } + if ($p->getRefid() !== null) { + $newP->setRefid($p->getRefid()); + } + if ($p->getEnvironment() !== null) { + $newP->setEnvironment($p->getEnvironment()); + } + if ($p->getUserProperty() !== null) { + $newP->setUserProperty($p->getUserProperty()); + } + if ($p->getOverride() !== null) { + $newP->setOverride($p->getOverride()); + } + $this->properties[$i] = $newP; + } + } + + /** + * Main entry point for the task. + * + * @return void + */ + public function main() { + + // Call Phing on the file set with the attribute "phingfile" + if ($this->phingFile !== null or $this->dir !== null) { + $this->processFile(); + } + + // if no filesets are given stop here; else process filesets + if (!empty($this->filesets)) { + // preserve old settings + $savedDir = $this->dir; + $savedPhingFile = $this->phingFile; + $savedTarget = $this->newTarget; + + // set no specific target for files in filesets + // [HL] I'm commenting this out; I don't know why this should not be supported! + // $this->newTarget = null; + + foreach($this->filesets as $fs) { + + $ds = $fs->getDirectoryScanner($this->project); + + $fromDir = $fs->getDir($this->project); + $srcFiles = $ds->getIncludedFiles(); + + foreach($srcFiles as $fname) { + $f = new PhingFile($ds->getbasedir(), $fname); + $f = $f->getAbsoluteFile(); + $this->phingFile = $f->getAbsolutePath(); + $this->dir = $f->getParentFile(); + $this->processFile(); // run Phing! + } + } + + // side effect free programming ;-) + $this->dir = $savedDir; + $this->phingFile = $savedPhingFile; + $this->newTarget = $savedTarget; + + // [HL] change back to correct dir + if ($this->dir !== null) { + chdir($this->dir->getAbsolutePath()); + } + } + + // Remove any dangling references to help the GC + foreach ($this->properties as $property) { + $property->setFallback(null); + } + } + + /** + * Execute phing file. + * + * @return void + */ + private function processFile() { + + $buildFailed = false; + $savedDir = $this->dir; + $savedPhingFile = $this->phingFile; + $savedTarget = $this->newTarget; + + $savedBasedirAbsPath = null; // this is used to save the basedir *if* we change it + + try { + + if ($this->newProject === null) { + $this->reinit(); + } + + $this->initializeProject(); + + if ($this->dir !== null) { + + $dirAbsPath = $this->dir->getAbsolutePath(); + + // BE CAREFUL! -- when the basedir is changed for a project, + // all calls to getAbsolutePath() on a relative-path dir will + // be made relative to the project's basedir! This means + // that subsequent calls to $this->dir->getAbsolutePath() will be WRONG! + + // We need to save the current project's basedir first. + $savedBasedirAbsPath = $this->getProject()->getBasedir()->getAbsolutePath(); + + $this->newProject->setBasedir($this->dir); + + // Now we must reset $this->dir so that it continues to resolve to the same + // path. + $this->dir = new PhingFile($dirAbsPath); + + if ($savedDir !== null) { // has been set explicitly + $this->newProject->setInheritedProperty("project.basedir", $this->dir->getAbsolutePath()); + } + + } else { + + // Since we're not changing the basedir here (for file resolution), + // we don't need to worry about any side-effects in this scanrio. + $this->dir = $this->getProject()->getBasedir(); + } + + $this->overrideProperties(); + if ($this->phingFile === null) { + $this->phingFile = "build.xml"; + } + + $fu = new FileUtils(); + $file = $fu->resolveFile($this->dir, $this->phingFile); + $this->phingFile = $file->getAbsolutePath(); + + $this->log("Calling Buildfile '" . $this->phingFile . "' with target '" . $this->newTarget . "'"); + + $this->newProject->setUserProperty("phing.file", $this->phingFile); + + ProjectConfigurator::configureProject($this->newProject, new PhingFile($this->phingFile)); + + if ($this->newTarget === null) { + $this->newTarget = $this->newProject->getDefaultTarget(); + } + + // Are we trying to call the target in which we are defined? + if ($this->newProject->getBaseDir() == $this->project->getBaseDir() && + $this->newProject->getProperty("phing.file") == $this->project->getProperty("phing.file") && + $this->getOwningTarget() !== null && + $this->newTarget == $this->getOwningTarget()->getName()) { + + throw new BuildException("phing task calling its own parent target"); + } + + $this->addReferences(); + $this->newProject->executeTarget($this->newTarget); + + } catch (Exception $e) { + $buildFailed = true; + $this->log($e->getMessage(), Project::MSG_ERR); + if (Phing::getMsgOutputLevel() <= Project::MSG_DEBUG) { + $lines = explode("\n", $e->getTraceAsString()); + foreach($lines as $line) { + $this->log($line, Project::MSG_DEBUG); + } + } + // important!!! continue on to perform cleanup tasks. + } + + + // reset environment values to prevent side-effects. + + $this->newProject = null; + $pkeys = array_keys($this->properties); + foreach($pkeys as $k) { + $this->properties[$k]->setProject(null); + } + + $this->dir = $savedDir; + $this->phingFile = $savedPhingFile; + $this->newTarget = $savedTarget; + + // If the basedir for any project was changed, we need to set that back here. + if ($savedBasedirAbsPath !== null) { + chdir($savedBasedirAbsPath); + } + + if ($this->haltOnFailure && $buildFailed) { + throw new BuildException("Execution of the target buildfile failed. Aborting."); + } + } + + /** + * Configure the Project, i.e. make intance, attach build listeners + * (copy from father project), add Task and Datatype definitions, + * copy properties and references from old project if these options + * are set via the attributes of the XML tag. + * + * Developer note: + * This function replaces the old methods "init", "_reinit" and + * "_initializeProject". + * + * @access protected + */ + private function initializeProject() { + + $this->newProject->setInputHandler($this->project->getInputHandler()); + + foreach($this->project->getBuildListeners() as $listener) { + $this->newProject->addBuildListener($listener); + } + + /* Copy things from old project. Datatypes and Tasks are always + * copied, properties and references only if specified so/not + * specified otherwise in the XML definition. + */ + // Add Datatype definitions + foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) { + $this->newProject->addDataTypeDefinition($typeName, $typeClass); + } + + // Add Task definitions + foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) { + if ($taskClass == "propertytask") { + // we have already added this taskdef in init() + continue; + } + $this->newProject->addTaskDefinition($taskName, $taskClass); + } + + // set user-defined properties + $this->project->copyUserProperties($this->newProject); + + if (!$this->inheritAll) { + // set System built-in properties separately, + // b/c we won't inherit them. + $this->newProject->setSystemProperties(); + + } else { + // set all properties from calling project + $properties = $this->project->getProperties(); + foreach ($properties as $name => $value) { + if ($name == "basedir" || $name == "phing.file" || $name == "phing.version") { + // basedir and phing.file get special treatment in main() + continue; + } + // don't re-set user properties, avoid the warning message + if ($this->newProject->getProperty($name) === null){ + // no user property + $this->newProject->setNewProperty($name, $value); + } + } + + } + + } + + /** + * Override the properties in the new project with the one + * explicitly defined as nested elements here. + * @return void + * @throws BuildException + */ + private function overrideProperties() { + foreach(array_keys($this->properties) as $i) { + $p = $this->properties[$i]; + $p->setProject($this->newProject); + $p->main(); + } + $this->project->copyInheritedProperties($this->newProject); + } + + /** + * Add the references explicitly defined as nested elements to the + * new project. Also copy over all references that don't override + * existing references in the new project if inheritrefs has been + * requested. + * + * @return void + * @throws BuildException + */ + private function addReferences() { + + // parent project references + $projReferences = $this->project->getReferences(); + + $newReferences = $this->newProject->getReferences(); + + $subprojRefKeys = array(); + + if (count($this->references) > 0) { + for ($i=0, $count=count($this->references); $i < $count; $i++) { + $ref = $this->references[$i]; + $refid = $ref->getRefId(); + + if ($refid === null) { + throw new BuildException("the refid attribute is required" + . " for reference elements"); + } + if (!isset($projReferences[$refid])) { + $this->log("Parent project doesn't contain any reference '" + . $refid . "'", + Project::MSG_WARN); + continue; + } + + $subprojRefKeys[] = $refid; + //thisReferences.remove(refid); + $toRefid = $ref->getToRefid(); + if ($toRefid === null) { + $toRefid = $refid; + } + $this->copyReference($refid, $toRefid); + } + } + + // Now add all references that are not defined in the + // subproject, if inheritRefs is true + if ($this->inheritRefs) { + + // get the keys that are were not used by the subproject + $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys); + + foreach($unusedRefKeys as $key) { + if (isset($newReferences[$key])) { + continue; + } + $this->copyReference($key, $key); + } + } + } + + /** + * Try to clone and reconfigure the object referenced by oldkey in + * the parent project and add it to the new project with the key + * newkey. + * + * <p>If we cannot clone it, copy the referenced object itself and + * keep our fingers crossed.</p> + * + * @param string $oldKey + * @param string $newKey + * @return void + */ + private function copyReference($oldKey, $newKey) { + $orig = $this->project->getReference($oldKey); + if ($orig === null) { + $this->log("No object referenced by " . $oldKey . ". Can't copy to " + .$newKey, + PROJECT_SG_WARN); + return; + } + + $copy = clone $orig; + + if ($copy instanceof ProjectComponent) { + $copy->setProject($this->newProject); + } elseif (in_array('setProject', get_class_methods(get_class($copy)))) { + $copy->setProject($this->newProject); + } elseif ($copy instanceof Project) { + // don't copy the old "Project" itself + } else { + $msg = "Error setting new project instance for " + . "reference with id " . $oldKey; + throw new BuildException($msg); + } + + $this->newProject->addReference($newKey, $copy); + } + + /** + * If true, pass all properties to the new phing project. + * Defaults to true. + * + * @access public + */ + function setInheritAll($value) { + $this->inheritAll = (boolean) $value; + } + + /** + * If true, pass all references to the new phing project. + * Defaults to false. + * + * @access public + */ + function setInheritRefs($value) { + $this->inheritRefs = (boolean)$value; + } + + /** + * The directory to use as a base directory for the new phing project. + * Defaults to the current project's basedir, unless inheritall + * has been set to false, in which case it doesn't have a default + * value. This will override the basedir setting of the called project. + * + * @access public + */ + function setDir($d) { + if ( is_string($d) ) + $this->dir = new PhingFile($d); + else + $this->dir = $d; + } + + /** + * The build file to use. + * Defaults to "build.xml". This file is expected to be a filename relative + * to the dir attribute given. + * + * @access public + */ + function setPhingfile($s) { + // it is a string and not a file to handle relative/absolute + // otherwise a relative file will be resolved based on the current + // basedir. + $this->phingFile = $s; + } + + /** + * Alias function for setPhingfile + * + * @access public + */ + function setBuildfile($s) { + $this->setPhingFile($s); + } + + /** + * The target of the new Phing project to execute. + * Defaults to the new project's default target. + * + * @access public + */ + function setTarget($s) { + $this->newTarget = $s; + } + + /** + * Support for filesets; This method returns a reference to an instance + * of a FileSet object. + * + * @return FileSet + */ + function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Property to pass to the new project. + * The property is passed as a 'user property' + * + * @access public + */ + function createProperty() { + $p = new PropertyTask(); + $p->setFallback($this->newProject); + $p->setUserProperty(true); + $this->properties[] = $p; + return $p; + } + + /** + * Reference element identifying a data type to carry + * over to the new project. + * + * @access public + */ + function createReference() { + $num = array_push($this->references, new PhingReference()); + return $this->references[$num-1]; + } + +} + +/** + * Helper class that implements the nested <reference> + * element of <phing> and <phingcall>. + * + * @package phing.tasks.system + */ +class PhingReference extends Reference { + + private $targetid = null; + + /** + * Set the id that this reference to be stored under in the + * new project. + * + * @param targetid the id under which this reference will be passed to + * the new project */ + public function setToRefid($targetid) { + $this->targetid = $targetid; + } + + /** + * Get the id under which this reference will be stored in the new + * project + * + * @return the id of the reference in the new project. + */ + public function getToRefid() { + return $this->targetid; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/PhpEvalTask.php b/buildscripts/phing/classes/phing/tasks/system/PhpEvalTask.php new file mode 100755 index 00000000..99316f7b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/PhpEvalTask.php @@ -0,0 +1,192 @@ +<?php +/* + * $Id: 8c46403ac685f362e310ffcceff5d4193bf09ef0 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Executes PHP function or evaluates expression and sets return value to a property. + * + * WARNING: + * This task can, of course, be abused with devastating effects. E.g. do not + * modify internal Phing classes unless you know what you are doing. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + * + * @todo Add support for evaluating expressions + */ +class PhpEvalTask extends Task { + + protected $expression; // Expression to evaluate + protected $function; // Function to execute + protected $class; // Class containing function to execute + protected $returnProperty = null; // name of property to set to return value + protected $params = array(); // parameters for function calls + + protected $logLevel = Project::MSG_INFO; + + /** + * Set level of log messages generated (default = info) + * @param string $level + */ + public function setLevel($level) + { + switch ($level) + { + case "error": $this->logLevel = Project::MSG_ERR; break; + case "warning": $this->logLevel = Project::MSG_WARN; break; + case "info": $this->logLevel = Project::MSG_INFO; break; + case "verbose": $this->logLevel = Project::MSG_VERBOSE; break; + case "debug": $this->logLevel = Project::MSG_DEBUG; break; + } + } + + /** Main entry point. */ + function main() { + + if ($this->function === null && $this->expression === null) { + throw new BuildException("You must specify a function to execute or PHP expression to evalute.", $this->location); + } + + if ($this->function !== null && $this->expression !== null) { + throw new BuildException("You can specify function or expression, but not both.", $this->location); + } + + if ($this->expression !== null && !empty($this->params)) { + throw new BuildException("You cannot use nested <param> tags when evaluationg a PHP expression.", $this->location); + } + + if ($this->function !== null) { + $this->callFunction(); + } elseif ($this->expression !== null) { + $this->evalExpression(); + } + } + + /** + * Calls function and returns results. + * @return mixed + */ + protected function callFunction() { + + if ($this->class !== null) { + // import the classname & unqualify it, if necessary + $this->class = Phing::import($this->class); + + $user_func = array($this->class, $this->function); + $h_func = $this->class . '::' . $this->function; // human-readable (for log) + } else { + $user_func = $this->function; + $h_func = $user_func; // human-readable (for log) + } + + // put parameters into simple array + $params = array(); + foreach($this->params as $p) { + $params[] = $p->getValue(); + } + + $this->log("Calling PHP function: " . $h_func . "()", $this->logLevel); + foreach($params as $p) { + $this->log(" param: " . $p, Project::MSG_VERBOSE); + } + + $return = call_user_func_array($user_func, $params); + + if ($this->returnProperty !== null) { + $this->project->setProperty($this->returnProperty, $return); + } + } + + /** + * Evaluates expression and returns resulting value. + * @return mixed + */ + protected function evalExpression() { + $this->log("Evaluating PHP expression: " . $this->expression, $this->logLevel); + if (!StringHelper::endsWith(';', trim($this->expression))) { + $this->expression .= ';'; + } + + if ($this->returnProperty !== null) { + $retval = null; + eval('$retval = ' . $this->expression); + $this->project->setProperty($this->returnProperty, $retval); + } else { + eval($this->expression); + } + } + + /** Set function to execute */ + public function setFunction($f) { + $this->function = $f; + } + + /** Set [static] class which contains function to execute */ + public function setClass($c) { + $this->class = $c; + } + + /** Sets property name to set with return value of function or expression.*/ + public function setReturnProperty($r) { + $this->returnProperty = $r; + } + + /** Set PHP expression to evaluate. */ + public function addText($expression) { + $this->expression = $expression; + } + + /** Set PHP expression to evaluate. */ + public function setExpression($expression) { + $this->expression = $expression; + } + + /** Add a nested <param> tag. */ + public function createParam() { + $p = new FunctionParam(); + $this->params[] = $p; + return $p; + } +} + +/** + * Supports the <param> nested tag for PhpTask. + * + * @package phing.tasks.system + */ +class FunctionParam { + + private $val; + + public function setValue($v) { + $this->val = $v; + } + + public function addText($v) { + $this->val = $v; + } + + public function getValue() { + return $this->val; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/system/PropertyPromptTask.php b/buildscripts/phing/classes/phing/tasks/system/PropertyPromptTask.php new file mode 100755 index 00000000..7a337764 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/PropertyPromptTask.php @@ -0,0 +1,234 @@ +<?php +/* + * $Id: d2de9371732599b179facb97ef937b2cfbfbead2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/system/io/ConsoleReader.php'; + +/** + * Deprecated task that uses console to prompt user for property values. + * + * This class is very slightly simpler than the InputTask, but lacks the ability + * to use a non-console input handler. You should, therefore, use InputTask. This + * class can serve as a reference, but will be removed in the future. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Anthony J. Young-Garner <ajyoung@alum.mit.edu> (Ant) + * @version $Id$ + * @package phing.tasks.system + * @deprecated - in favor of the more capable InputTask + */ +class PropertyPromptTask extends Task { + + /** + * The property name to set with the output. + * @var string + */ + private $propertyName; // required + + /** + * The default value to use if no input is entered. + * @var string + */ + private $defaultValue; + + /** + * The entered value. + * @var string + */ + private $proposedValue; + + /** + * The text to use for the prompt. + * @var string + */ + private $promptText; + + /** + * The character to put after the text. + * @var string + */ + private $promptCharacter; + + /** + * + */ + private $useExistingValue; + + /** + * Run the PropertyPrompt task. + * @throws BuildException + */ + public function main() { + + $this->proposedValue = $this->project->getProperty($this->propertyName); + $currentValue = $this->defaultValue; + + if ($currentValue == "" && $this->proposedValue !== null) { + $currentValue = $this->proposedValue; + } + + if ($this->useExistingValue !== true || $this->proposedValue === null) { + + $this->log("Prompting user for " . $this->propertyName . ". " . $this->getDefaultMessage(), Project::MSG_VERBOSE); + + print "\n" . $this->promptText . " [" . $currentValue . "] " . $this->promptCharacter . " "; + + /** future version should probably have hooks for validation of user input.*/ + $reader = new ConsoleReader(); + + try { + $this->proposedValue = $reader->readLine(); + } catch (IOException $e) { + $this->log("Prompt failed. Using default. (Failure reason: " . $e->getMessage().")"); + $this->proposedValue = $this->defaultValue; + } + + if ($this->proposedValue === "") { + $this->log("No value specified, using default.", Project::MSG_VERBOSE); + $this->proposedValue = $this->defaultValue; + } + + if (isset($this->proposedValue)) { + $this->project->setProperty($this->propertyName, $this->proposedValue); + } + + } + } + + /** + * Returns a string to be inserted in the log message + * indicating whether a default response was specified + * in the build file. + */ + private function getDefaultMessage() { + if ($this->defaultValue == "") { + return "No default response specified."; + } else return "Default response is " . $this->defaultValue . "."; + } + + /** + * Returns defaultValue specified + * in this task for the Property + * being set. + * @return string + */ + public function getDefaultValue() { + return $this->defaultValue; + } + + /** + * Returns the terminating character used to + * punctuate the prompt text. + * @return string + */ + public function getPromptCharacter() { + return $this->promptCharacter; + } + + /** + * Returns text of the prompt. + * @return java.lang.String + */ + public function getPromptText() { + return $this->promptText; + } + + /** + * Returns name of the Ant Project Property + * being set by this task. + * @return string + */ + public function getPropertyName() { + return $this->propertyName; + } + /** + * Initializes this task. + */ + public function init() { + parent::init(); + $this->defaultValue = ""; + $this->promptCharacter = "?"; + $this->useExistingValue = false; + } + + /** + * Insert the method's description here. + * Creation date: (12/10/2001 8:16:16 AM) + * @return boolean + */ + public function isUseExistingValue() { + return $this->useExistingValue; + } + + /** + * Sets defaultValue for the Property + * being set by this task. + * @param string $newDefaultvalue + */ + public function setDefaultvalue($newDefaultvalue) { + $this->defaultValue = $newDefaultvalue; + } + + /** + * Sets the terminating character used to + * punctuate the prompt text (default is "?"). + * @param string $newPromptcharacter + */ + public function setPromptCharacter($newPromptcharacter) { + $this->promptCharacter = $newPromptcharacter; + } + + /** + * Sets text of the prompt. + * @param string $newPrompttext + */ + public function setPromptText($newPrompttext) { + $this->promptText = $newPrompttext; + } + + /** + * Specifies the Phing Project Property + * being set by this task. + * @param newPropertyname java.lang.String + */ + public function setPropertyName($newPropertyname) { + $this->propertyName = $newPropertyname; + } + + /** + * + * @param boolean $newUseExistingValue + */ + public function setUseExistingValue($newUseExistingValue) { + $this->useExistingValue = $newUseExistingValue; + } + + /** + * Sets the prompt text that will be presented to the user. + * @param string $prompt + * @return void + */ + public function addText($prompt) { + $this->setPromptText($prompt); + } + + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/PropertyTask.php b/buildscripts/phing/classes/phing/tasks/system/PropertyTask.php new file mode 100755 index 00000000..0d8854c8 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/PropertyTask.php @@ -0,0 +1,438 @@ +<?php + +/* + * $Id: e6d7123b6331d5032ad1e67967cf54ef2aae3f7f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/Task.php'; +include_once 'phing/system/util/Properties.php'; + +/** + * Task for setting properties in buildfiles. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: e6d7123b6331d5032ad1e67967cf54ef2aae3f7f $ + * @package phing.tasks.system + */ +class PropertyTask extends Task { + + /** name of the property */ + protected $name; + + /** value of the property */ + protected $value; + + protected $reference; + protected $env; // Environment + protected $file; + protected $ref; + protected $prefix; + protected $fallback; + + /** Whether to force overwrite of existing property. */ + protected $override = false; + + /** Whether property should be treated as "user" property. */ + protected $userProperty = false; + + /** + * Sets a the name of current property component + */ + function setName($name) { + $this->name = (string) $name; + } + + /** Get property component name. */ + function getName() { + return $this->name; + } + + /** + * Sets a the value of current property component. + * @param mixed Value of name, all scalars allowed + */ + function setValue($value) { + $this->value = (string) $value; + } + + /** + * Sets value of property to CDATA tag contents. + * @param string $values + * @since 2.2.0 + */ + public function addText($value) { + $this->setValue($value); + } + + /** Get the value of current property component. */ + function getValue() { + return $this->value; + } + + /** Set a file to use as the source for properties. */ + function setFile($file) { + if (is_string($file)) { + $file = new PhingFile($file); + } + $this->file = $file; + } + + /** Get the PhingFile that is being used as property source. */ + function getFile() { + return $this->file; + } + + function setRefid(Reference $ref) { + $this->reference = $ref; + } + + function getRefid() { + return $this->reference; + } + + /** + * Prefix to apply to properties loaded using <code>file</code>. + * A "." is appended to the prefix if not specified. + * @param string $prefix prefix string + * @return void + * @since 2.0 + */ + public function setPrefix($prefix) { + $this->prefix = $prefix; + if (!StringHelper::endsWith(".", $prefix)) { + $this->prefix .= "."; + } + } + + /** + * @return string + * @since 2.0 + */ + public function getPrefix() { + return $this->prefix; + } + + /** + * the prefix to use when retrieving environment variables. + * Thus if you specify environment="myenv" + * you will be able to access OS-specific + * environment variables via property names "myenv.PATH" or + * "myenv.TERM". + * <p> + * Note that if you supply a property name with a final + * "." it will not be doubled. ie environment="myenv." will still + * allow access of environment variables through "myenv.PATH" and + * "myenv.TERM". This functionality is currently only implemented + * on select platforms. Feel free to send patches to increase the number of platforms + * this functionality is supported on ;).<br> + * Note also that properties are case sensitive, even if the + * environment variables on your operating system are not, e.g. it + * will be ${env.Path} not ${env.PATH} on Windows 2000. + * @param env prefix + */ + function setEnvironment($env) { + $this->env = (string) $env; + } + + function getEnvironment() { + return $this->env; + } + + /** + * Set whether this is a user property (ro). + * This is deprecated in Ant 1.5, but the userProperty attribute + * of the class is still being set via constructor, so Phing will + * allow this method to function. + * @param boolean $v + */ + function setUserProperty($v) { + $this->userProperty = (boolean) $v; + } + + function getUserProperty() { + return $this->userProperty; + } + + function setOverride($v) { + $this->override = (boolean) $v; + } + + function getOverride() { + return $this->override; + } + + function toString() { + return (string) $this->value; + } + + /** + * @param Project $p + */ + function setFallback($p) { + $this->fallback = $p; + } + + function getFallback() { + return $this->fallback; + } + /** + * set the property in the project to the value. + * if the task was give a file or env attribute + * here is where it is loaded + */ + function main() { + if ($this->name !== null) { + if ($this->value === null && $this->ref === null) { + throw new BuildException("You must specify value or refid with the name attribute", $this->getLocation()); + } + } else { + if ($this->file === null && $this->env === null ) { + throw new BuildException("You must specify file or environment when not using the name attribute", $this->getLocation()); + } + } + + if ($this->file === null && $this->prefix !== null) { + throw new BuildException("Prefix is only valid when loading from a file.", $this->getLocation()); + } + + if (($this->name !== null) && ($this->value !== null)) { + $this->addProperty($this->name, $this->value); + } + + if ($this->file !== null) { + $this->loadFile($this->file); + } + + if ( $this->env !== null ) { + $this->loadEnvironment($this->env); + } + + if (($this->name !== null) && ($this->ref !== null)) { + // get the refereced property + try { + $this->addProperty($this->name, $this->reference->getReferencedObject($this->project)->toString()); + } catch (BuildException $be) { + if ($this->fallback !== null) { + $this->addProperty($this->name, $this->reference->getReferencedObject($this->fallback)->toString()); + } else { + throw $be; + } + } + } + } + + /** + * load the environment values + * @param string $prefix prefix to place before them + */ + protected function loadEnvironment($prefix) { + + $props = new Properties(); + if ( substr($prefix, strlen($prefix)-1) == '.' ) { + $prefix .= "."; + } + $this->log("Loading Environment $prefix", Project::MSG_VERBOSE); + foreach($_ENV as $key => $value) { + $props->setProperty($prefix . '.' . $key, $value); + } + $this->addProperties($props); + } + + /** + * iterate through a set of properties, + * resolve them then assign them + */ + protected function addProperties($props) { + $this->resolveAllProperties($props); + foreach($props->keys() as $name) { + $value = $props->getProperty($name); + $v = $this->project->replaceProperties($value); + if ($this->prefix !== null) { + $name = $this->prefix . $name; + } + $this->addProperty($name, $v); + } + } + + /** + * add a name value pair to the project property set + * @param string $name name of property + * @param string $value value to set + */ + protected function addProperty($name, $value) { + if ($this->userProperty) { + if ($this->project->getUserProperty($name) === null || $this->override) { + $this->project->setInheritedProperty($name, $value); + } else { + $this->log("Override ignored for " . $name, Project::MSG_VERBOSE); + } + } else { + if ($this->override) { + $this->project->setProperty($name, $value); + } else { + $this->project->setNewProperty($name, $value); + } + } + } + + /** + * load properties from a file. + * @param PhingFile $file + */ + protected function loadFile(PhingFile $file) { + $props = new Properties(); + $this->log("Loading ". $file->getAbsolutePath(), Project::MSG_INFO); + try { // try to load file + if ($file->exists()) { + $props->load($file); + $this->addProperties($props); + } else { + $this->log("Unable to find property file: ". $file->getAbsolutePath() ."... skipped", Project::MSG_WARN); + } + } catch (IOException $ioe) { + throw new BuildException("Could not load properties from file.", $ioe); + } + } + + /** + * Given a Properties object, this method goes through and resolves + * any references to properties within the object. + * + * @param Properties $props The collection of Properties that need to be resolved. + * @return void + */ + protected function resolveAllProperties(Properties $props) { + + $keys = $props->keys(); + + while(count($keys)) { + + // There may be a nice regex/callback way to handle this + // replacement, but at the moment it is pretty complex, and + // would probably be a lot uglier to work into a preg_replace_callback() + // system. The biggest problem is the fact that a resolution may require + // multiple passes. + + $name = array_shift($keys); + $value = $props->getProperty($name); + $resolved = false; + + while(!$resolved) { + + $fragments = array(); + $propertyRefs = array(); + + // [HL] this was ::parsePropertyString($this->value ...) ... this seems wrong + self::parsePropertyString($value, $fragments, $propertyRefs); + + $resolved = true; + if (count($propertyRefs) !== 0) { + + $sb = ""; + + $i = $fragments; + $j = $propertyRefs; + while(count($i)) { + $fragment = array_shift($i); + if ($fragment === null) { + $propertyName = array_shift($j); + + if ($propertyName === $name) { + // Should we maybe just log this as an error & move on? + // $this->log("Property ".$name." was circularly defined.", Project::MSG_ERR); + throw new BuildException("Property ".$name." was circularly defined."); + } + + $fragment = $this->getProject()->getProperty($propertyName); + if ($fragment === null) { + if ($props->containsKey($propertyName)) { + $fragment = $props->getProperty($propertyName); + $resolved = false; // parse again (could have been replaced w/ another var) + } else { + $fragment = "\${".$propertyName."}"; + } + } + } + $sb .= $fragment; + } + + $this->log("Resolved Property \"$value\" to \"$sb\"", Project::MSG_DEBUG); + $value = $sb; + $props->setProperty($name, $value); + + } // if (count($propertyRefs)) + + } // while (!$resolved) + + } // while (count($keys) + } + + + /** + * This method will parse a string containing ${value} style + * property values into two lists. The first list is a collection + * of text fragments, while the other is a set of string property names + * null entries in the first list indicate a property reference from the + * second list. + * + * This is slower than regex, but useful for this class, which has to handle + * multiple parsing passes for properties. + * + * @param string $value The string to be scanned for property references + * @param array &$fragments The found fragments + * @param array &$propertyRefs The found refs + */ + protected function parsePropertyString($value, &$fragments, &$propertyRefs) { + + $prev = 0; + $pos = 0; + + while (($pos = strpos($value, '$', $prev)) !== false) { + + if ($pos > $prev) { + array_push($fragments, StringHelper::substring($value, $prev, $pos-1)); + } + if ($pos === (strlen($value) - 1)) { + array_push($fragments, '$'); + $prev = $pos + 1; + } elseif ($value{$pos+1} !== '{' ) { + + // the string positions were changed to value-1 to correct + // a fatal error coming from function substring() + array_push($fragments, StringHelper::substring($value, $pos, $pos + 1)); + $prev = $pos + 2; + } else { + $endName = strpos($value, '}', $pos); + if ($endName === false) { + throw new BuildException("Syntax error in property: $value"); + } + $propertyName = StringHelper::substring($value, $pos + 2, $endName-1); + array_push($fragments, null); + array_push($propertyRefs, $propertyName); + $prev = $endName + 1; + } + } + + if ($prev < strlen($value)) { + array_push($fragments, StringHelper::substring($value, $prev)); + } + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/ReflexiveTask.php b/buildscripts/phing/classes/phing/tasks/system/ReflexiveTask.php new file mode 100755 index 00000000..d49c47f2 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ReflexiveTask.php @@ -0,0 +1,155 @@ +<?php +/* + * $Id: 3dcb1ad6e9fd3b2801c1fe3bcbaf2fcab8ea6018 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * This task is for using filter chains to make changes to files and overwrite the original files. + * + * This task was created to serve the need for "cleanup" tasks -- e.g. a ReplaceRegexp task or strip task + * being used to modify files and then overwrite the modified files. In many (most?) cases you probably + * should just use a copy task to preserve the original source files, but this task supports situations + * where there is no src vs. build directory, and modifying source files is actually desired. + * + * <code> + * <reflexive> + * <fileset dir="."> + * <include pattern="*.html"> + * </fileset> + * <filterchain> + * <replaceregexp> + * <regexp pattern="\n\r" replace="\n"/> + * </replaceregexp> + * </filterchain> + * </reflexive> + * </code> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class ReflexiveTask extends Task { + + /** Single file to process. */ + private $file; + + /** Any filesets that should be processed. */ + private $filesets = array(); + + /** Any filters to be applied before append happens. */ + private $filterChains = array(); + + /** Alias for setFrom() */ + function setFile(PhingFile $f) { + $this->file = $f; + } + + /** Nested creator, adds a set of files (nested fileset attribute). */ + function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Creates a filterchain + * + * @return object The created filterchain object + */ + function createFilterChain() { + $num = array_push($this->filterChains, new FilterChain($this->project)); + return $this->filterChains[$num-1]; + } + + /** Append the file(s). */ + function main() { + + if ($this->file === null && empty($this->filesets)) { + throw new BuildException("You must specify a file or fileset(s) for the <reflexive> task."); + } + + // compile a list of all files to modify, both file attrib and fileset elements + // can be used. + + $files = array(); + + if ($this->file !== null) { + $files[] = $this->file; + } + + if (!empty($this->filesets)) { + $filenames = array(); + foreach($this->filesets as $fs) { + try { + $ds = $fs->getDirectoryScanner($this->project); + $filenames = $ds->getIncludedFiles(); // get included filenames + $dir = $fs->getDir($this->project); + foreach ($filenames as $fname) { + $files[] = new PhingFile($dir, $fname); + } + } catch (BuildException $be) { + $this->log($be->getMessage(), Project::MSG_WARN); + } + } + } + + $this->log("Applying reflexive processing to " . count($files) . " files."); + + // These "slots" allow filters to retrieve information about the currently-being-process files + $slot = $this->getRegisterSlot("currentFile"); + $basenameSlot = $this->getRegisterSlot("currentFile.basename"); + + + foreach($files as $file) { + // set the register slots + + $slot->setValue($file->getPath()); + $basenameSlot->setValue($file->getName()); + + // 1) read contents of file, pulling through any filters + $in = null; + try { + $contents = ""; + $in = FileUtils::getChainedReader(new FileReader($file), $this->filterChains, $this->project); + while(-1 !== ($buffer = $in->read())) { + $contents .= $buffer; + } + $in->close(); + } catch (Exception $e) { + if ($in) $in->close(); + $this->log("Erorr reading file: " . $e->getMessage(), Project::MSG_WARN); + } + + try { + // now create a FileWriter w/ the same file, and write to the file + $out = new FileWriter($file); + $out->write($contents); + $out->close(); + $this->log("Applying reflexive processing to " . $file->getPath(), Project::MSG_VERBOSE); + } catch (Exception $e) { + if ($out) $out->close(); + $this->log("Error writing file back: " . $e->getMessage(), Project::MSG_WARN); + } + + } + + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/ResolvePathTask.php b/buildscripts/phing/classes/phing/tasks/system/ResolvePathTask.php new file mode 100755 index 00000000..bdd707a2 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ResolvePathTask.php @@ -0,0 +1,159 @@ +<?php +/* + * $Id: 9635ed3f6605f5ed64b74e85731dbcd3ad43ce0f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Task for resolving relative paths and setting absolute path in property value. + * + * This task was created to address a need for resolving absolute paths of files / directories. + * In many cases a relative directory (e.g. "./build") is specified, but it needs to be treated + * as an absolute path since other build files (e.g. in subdirs) should all be using the same + * path -- and not treating it as a relative path to their own directory. + * + * <code> + * <property name="relative_path" value="./dirname"/> + * <resolvepath propertyName="absolute_path" file="${relative_path}"/> + * <echo>Resolved [absolute] path: ${absolute_path}</echo> + * </code> + * + * TODO: + * - Possibly integrate this with PackageAsPath, for handling/resolving dot-path paths. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class ResolvePathTask extends Task { + + /** Name of property to set. */ + private $propertyName; + + /** The [possibly] relative file/path that needs to be resolved. */ + private $file; + + /** Base directory used for resolution. */ + private $dir; + + /** + * Log level + */ + private $logLevel = Project::MSG_VERBOSE; + + /** + * Set the name of the property to set. + * @param string $v Property name + * @return void + */ + public function setPropertyName($v) { + $this->propertyName = $v; + } + + /** + * Sets a base dir to use for resolution. + * @param PhingFile $d + */ + function setDir(PhingFile $d) { + $this->dir = $d; + } + + /** + * Sets a path (file or directory) that we want to resolve. + * This is the same as setFile() -- just more generic name so that it's + * clear that you can also use it to set directory. + * @param string $f + * @see setFile() + */ + function setPath($f) { + $this->file = $f; + } + + /** + * Sets a file that we want to resolve. + * @param string $f + */ + function setFile($f) { + $this->file = $f; + } + + /** + * Set level of log messages generated (default = verbose) + * + * @param string $level Log level + * + * @return void + */ + public function setLevel($level) + { + switch ($level) { + case 'error': + $this->logLevel = Project::MSG_ERR; + break; + case 'warning': + $this->logLevel = Project::MSG_WARN; + break; + case 'info': + $this->logLevel = Project::MSG_INFO; + break; + case 'verbose': + $this->logLevel = Project::MSG_VERBOSE; + break; + case 'debug': + $this->logLevel = Project::MSG_DEBUG; + break; + default: + throw new BuildException( + sprintf('Unknown log level "%s"', $level) + ); + } + } + + /** + * Perform the resolution & set property. + */ + public function main() { + + if (!$this->propertyName) { + throw new BuildException("You must specify the propertyName attribute", $this->getLocation()); + } + + // Currently only files are supported + if ($this->file === null) { + throw new BuildException("You must specify a path to resolve", $this->getLocation()); + } + + $fs = FileSystem::getFileSystem(); + + // if dir attribute was specified then we should + // use that as basedir to which file was relative. + // -- unless the file specified is an absolute path + if ($this->dir !== null && !$fs->isAbsolute(new PhingFile($this->file))) { + $resolved = new PhingFile($this->dir->getPath(), $this->file); + } else { + // otherwise just resolve it relative to project basedir + $resolved = $this->project->resolveFile($this->file); + } + + $this->log("Resolved " . $this->file . " to " . $resolved->getAbsolutePath(), $this->logLevel); + $this->project->setProperty($this->propertyName, $resolved->getAbsolutePath()); + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/SequentialTask.php b/buildscripts/phing/classes/phing/tasks/system/SequentialTask.php new file mode 100644 index 00000000..d080deda --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/SequentialTask.php @@ -0,0 +1,58 @@ +<?php + +/* + * $Id: e6be0ff54ade0fb900d101759d8788590e769831 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +require_once 'phing/TaskContainer.php'; + +/** + * Sequential is a container task that contains other Phing Task objects. + * + * The sequential task has no attributes and does not support any nested + * elements apart from Ant tasks. Any valid Ant task may be embedded within the + * sequential task. + * + * @since 2.1.2 + * @package phing.tasks.system + */ +class SequentialTask extends Task implements TaskContainer { + + /** Optional Vector holding the nested tasks */ + protected $nestedTasks = array(); + + /** + * Add a nested task to Sequential. + * @param Task $nestedTask Nested task to execute Sequential + */ + public function addTask(Task $nestedTask) { + $this->nestedTasks[] = $nestedTask; + } + + /** + * Execute all nestedTasks. + * @throws BuildException if one of the nested tasks fails. + */ + public function main() { + foreach($this->nestedTasks as $task) { + $task->perform(); + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/TaskdefTask.php b/buildscripts/phing/classes/phing/tasks/system/TaskdefTask.php new file mode 100755 index 00000000..84552d69 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/TaskdefTask.php @@ -0,0 +1,165 @@ +<?php + +/* + * $Id: df52def0bb44ce1b0909f5e8858e79b2ef88ca0f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/system/io/PhingFile.php'; + +/** + * Register a task for use within a buildfile. + * + * This is for registering your own tasks -- or any non-core Task -- for use within a buildfile. + * If you find that you are using a particular class frequently, you may want to edit the + * phing/tasks/defaults.properties file so that it is included by default. You may also + * want to submit it (if LGPL or compatible license) to be included in Phing distribution. + * + * <pre> + * <taskdef name="mytag" classname="path.to.MyHandlingClass"/> + * . + * . + * <mytag param1="val1" param2="val2"/> + * </pre> + * + * TODO: + * -- possibly refactor since this is almost the same as TypeDefTask + * (right now these are just too simple to really justify creating an abstract class) + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id: df52def0bb44ce1b0909f5e8858e79b2ef88ca0f $ + * @package phing.tasks.system + */ +class TaskdefTask extends Task { + + /** Tag name for task that will be used in XML */ + private $name; + + /** + * Classname of task to register. + * This can be a dot-path -- relative to a location on PHP include_path. + * E.g. path.to.MyClass -> path/to/MyClass.php + * @var string + */ + private $classname; + + /** + * Path to add to PHP include_path to aid in finding specified class. + * @var Path + */ + private $classpath; + + /** + * Refid to already defined classpath + */ + private $classpathId; + + /** + * Name of file to load multiple definitions from. + * @var string + */ + private $typeFile; + + /** + * Set the classpath to be used when searching for component being defined + * + * @param Path $classpath A Path object containing the classpath. + */ + public function setClasspath(Path $classpath) { + if ($this->classpath === null) { + $this->classpath = $classpath; + } else { + $this->classpath->append($classpath); + } + } + + /** + * Create the classpath to be used when searching for component being defined + * + * @return Path + */ + public function createClasspath() { + if ($this->classpath === null) { + $this->classpath = new Path($this->project); + } + return $this->classpath->createPath(); + } + + /** + * Reference to a classpath to use when loading the files. + */ + public function setClasspathRef(Reference $r) { + $this->classpathId = $r->getRefId(); + $this->createClasspath()->setRefid($r); + } + + /** + * Sets the name that will be used in XML buildfile. + * @param string $name + */ + public function setName($name) { + $this->name = $name; + } + + /** + * Sets the class name / dotpath to use. + * @param string $class + */ + public function setClassname($class) { + $this->classname = $class; + } + + /** + * Sets the file of definitionas to use to use. + * @param string $file + */ + public function setFile($file) { + $this->typeFile = $file; + } + + /** Main entry point */ + public function main() { + if ($this->typeFile === null && + ($this->name === null || $this->classname === null)) { + throw new BuildException("You must specify name and class attributes for <taskdef>."); + } + if ($this->typeFile == null) { + $this->log("Task " . $this->name . " will be handled by class " . $this->classname, Project::MSG_VERBOSE); + $this->project->addTaskDefinition($this->name, $this->classname, $this->classpath); + } else { + try { // try to load taskdefs given in file + $props = new Properties(); + $in = new PhingFile((string) $this->typeFile); + + if ($in === null) { + throw new BuildException("Can't load task list {$this->typeFile}"); + } + $props->load($in); + + $enum = $props->propertyNames(); + foreach($enum as $key) { + $value = $props->getProperty($key); + $this->project->addTaskDefinition($key, $value, $this->classpath); + } + } catch (IOException $ioe) { + throw new BuildException("Can't load task list {$this->typeFile}"); + } + } + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/TouchTask.php b/buildscripts/phing/classes/phing/tasks/system/TouchTask.php new file mode 100755 index 00000000..3c45d0d3 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/TouchTask.php @@ -0,0 +1,170 @@ +<?php +/* + * $Id: e581b40ff4e3eac5f62a32b48b4a22285cbc51c1 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/util/DirectoryScanner.php'; +include_once 'phing/types/FileSet.php'; +include_once 'phing/util/FileUtils.php'; +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/system/io/IOException.php'; + +/** + * Touch a file and/or fileset(s); corresponds to the Unix touch command. + * + * If the file to touch doesn't exist, an empty one is created. + * + * @version $Id$ + * @package phing.tasks.system + */ +class TouchTask extends Task { + + private $file; + private $millis = -1; + private $dateTime; + private $filesets = array(); + private $fileUtils; + + function __construct() { + $this->fileUtils = new FileUtils(); + } + + /** + * Sets a single source file to touch. If the file does not exist + * an empty file will be created. + */ + function setFile(PhingFile $file) { + $this->file = $file; + } + + /** + * the new modification time of the file + * in milliseconds since midnight Jan 1 1970. + * Optional, default=now + */ + function setMillis($millis) { + $this->millis = (int) $millis; + } + + /** + * the new modification time of the file + * in the format MM/DD/YYYY HH:MM AM or PM; + * Optional, default=now + */ + function setDatetime($dateTime) { + $this->dateTime = (string) $dateTime; + } + + /** + * Nested creator, adds a set of files (nested fileset attribute). + * @return FileSet + */ + function createFileSet() { + $num = array_push($this->filesets, new FileSet()); + return $this->filesets[$num-1]; + } + + /** + * Execute the touch operation. + */ + function main() { + $savedMillis = $this->millis; + + if ($this->file === null && count($this->filesets) === 0) { + throw new BuildException("Specify at least one source - a file or a fileset."); + } + + if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) { + throw new BuildException("Use a fileset to touch directories."); + } + + try { // try to touch file + if ($this->dateTime !== null) { + $this->setMillis(strtotime($this->dateTime)); + if ($this->millis < 0) { + throw new BuildException("Date of {$this->dateTime} results in negative milliseconds value relative to epoch (January 1, 1970, 00:00:00 GMT)."); + } + } + $this->_touch(); + } catch (Exception $ex) { + throw new BuildException("Error touch()ing file", $ex, $this->location); + } + + $this->millis = $savedMillis; + + } + + /** + * Does the actual work. + */ + function _touch() { + if ($this->file !== null) { + if (!$this->file->exists()) { + $this->log("Creating " . $this->file->__toString(), Project::MSG_INFO); + try { // try to create file + $this->file->createNewFile(); + } catch(IOException $ioe) { + throw new BuildException("Error creating new file " . $this->file->__toString(), $ioe, $this->location); + } + } + } + + $resetMillis = false; + if ($this->millis < 0) { + $resetMillis = true; + $this->millis = Phing::currentTimeMillis(); + } + + if ($this->file !== null) { + $this->touchFile($this->file); + } + + // deal with the filesets + foreach($this->filesets as $fs) { + + $ds = $fs->getDirectoryScanner($this->getProject()); + $fromDir = $fs->getDir($this->getProject()); + + $srcFiles = $ds->getIncludedFiles(); + $srcDirs = $ds->getIncludedDirectories(); + + for ($j=0,$_j=count($srcFiles); $j < $_j; $j++) { + $this->touchFile(new PhingFile($fromDir, (string) $srcFiles[$j])); + } + + for ($j=0,$_j=count($srcDirs); $j < $_j ; $j++) { + $this->touchFile(new PhingFile($fromDir, (string) $srcDirs[$j])); + } + } + + if ($resetMillis) { + $this->millis = -1; + } + } + + private function touchFile($file) { + if ( !$file->canWrite() ) { + throw new BuildException("Can not change modification date of read-only file " . $file->__toString()); + } + $file->setLastModified($this->millis); + } + +} + diff --git a/buildscripts/phing/classes/phing/tasks/system/TryCatchTask.php b/buildscripts/phing/classes/phing/tasks/system/TryCatchTask.php new file mode 100644 index 00000000..b27d829b --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/TryCatchTask.php @@ -0,0 +1,123 @@ +<?php +/* + * $Id: acc9e5d431141ae13ea43c1ed1d3177a4fb60bf9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/Task.php'; + +/** + * A wrapper task that lets you run tasks(s) when another set + * of tasks fails. + * + * Inspired by {@link http://ant-contrib.sourceforge.net/tasks/tasks/trycatch.html} + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: acc9e5d431141ae13ea43c1ed1d3177a4fb60bf9 $ + * @package phing.tasks.system + */ +class TryCatchTask extends Task +{ + protected $propertyName = ""; + + protected $tryContainer = null; + protected $catchContainer = null; + protected $finallyContainer = null; + + /** + * Main method + * + * @throws BuildException + * @return void + */ + public function main() + { + $exc = null; + + if (empty($this->tryContainer)) { + throw new BuildException('A nested <try> element is required'); + } + + try { + $this->tryContainer->perform(); + } catch (BuildException $e) { + if (!empty($this->propertyName)) { + $this->project->setProperty($this->propertyName, $e->getMessage()); + } + + if (!empty($this->referenceName)) { + $this->project->addReference($this->referenceName, $e); + } + + if (!empty($this->catchContainer)) { + $this->catchContainer->perform(); + } else { + $exc = $e; + } + } + + if (!empty($this->finallyContainer)) { + $this->finallyContainer->perform(); + } + + if (!empty($exc)) { + throw $exc; + } + } + + /** + * Sets the name of the property that will + * contain the exception message. + * + * @param string $property + */ + public function setProperty($property) + { + $this->propertyName = (string) $property; + } + + /** + * Add nested <try> element + * + * @param SequentialTask $container + */ + public function addTry(SequentialTask $container) + { + $this->tryContainer = $container; + } + + /** + * Add nested <catch> element + * + * @param SequentialTask $container + */ + public function addCatch(SequentialTask $container) + { + $this->catchContainer = $container; + } + + /** + * Add nested <finally> element + * + * @param SequentialTask $container + */ + public function addFinally(SequentialTask $container) + { + $this->finallyContainer = $container; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/TstampTask.php b/buildscripts/phing/classes/phing/tasks/system/TstampTask.php new file mode 100755 index 00000000..44f2c3f0 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/TstampTask.php @@ -0,0 +1,171 @@ +<?php +/* + * $Id: c0693134153e6095ab198edb5cf204f53bb7ba69 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Sets properties to the current time, or offsets from the current time. + * The default properties are TSTAMP, DSTAMP and TODAY; + * + * Based on Ant's Tstamp task. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id$ + * @package phing.tasks.system + * @since 2.2.0 + */ +class TstampTask extends Task +{ + private $customFormats = array(); + + private $prefix = ""; + + /** + * Set a prefix for the properties. If the prefix does not end with a "." + * one is automatically added. + * @param prefix the prefix to use. + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + + if (!empty($this->prefix)) + { + $this->prefix.= "."; + } + } + + /** + * Adds a custom format + * + * @param TstampCustomFormat custom format + */ + public function addFormat(TstampCustomFormat $cf) + { + $this->customFormats[] = $cf; + } + + /** + * Create the timestamps. Custom ones are done before + * the standard ones. + * + * @throws BuildException + */ + public function main() + { + foreach ($this->customFormats as $cf) + { + $cf->execute($this); + } + + $dstamp = strftime('%Y%m%d'); + $this->prefixProperty('DSTAMP', $dstamp); + + $tstamp = strftime('%H%M'); + $this->prefixProperty('TSTAMP', $tstamp); + + $today = strftime('%B %d %Y'); + $this->prefixProperty('TODAY', $today); + } + + /** + * helper that encapsulates prefix logic and property setting + * policy (i.e. we use setNewProperty instead of setProperty). + */ + public function prefixProperty($name, $value) + { + $this->getProject()->setNewProperty($this->prefix . $name, $value); + } +} + +/** + * @package phing.tasks.system + */ +class TstampCustomFormat +{ + private $propertyName = ""; + private $pattern = ""; + private $locale = ""; + + /** + * The property to receive the date/time string in the given pattern + * + * @param propertyName the name of the property. + */ + public function setProperty($propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * The date/time pattern to be used. The values are as + * defined by the PHP strftime() function. + * + * @param pattern + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + } + + /** + * The locale used to create date/time string. + * + * @param locale + */ + public function setLocale($locale) + { + $this->locale = $locale; + } + + /** + * validate parameter and execute the format. + * + * @param TstampTask reference to task + */ + public function execute(TstampTask $tstamp) + { + if (empty($this->propertyName)) + { + throw new BuildException("property attribute must be provided"); + } + + if (empty($this->pattern)) + { + throw new BuildException("pattern attribute must be provided"); + } + + if (!empty($this->locale)) + { + setlocale(LC_ALL, $this->locale); + } + + $value = strftime($this->pattern); + $tstamp->prefixProperty($this->propertyName, $value); + + if (!empty($this->locale)) + { + // reset locale + setlocale(LC_ALL, NULL); + } + } +} + diff --git a/buildscripts/phing/classes/phing/tasks/system/TypedefTask.php b/buildscripts/phing/classes/phing/tasks/system/TypedefTask.php new file mode 100755 index 00000000..c03e716d --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/TypedefTask.php @@ -0,0 +1,127 @@ +<?php +/* + * $Id: 6122dcb36b79ffe3c3fb430a0b4586d9d145410b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Register a datatype for use within a buildfile. + * + * This is for registering your own datatypes for use within a buildfile. + * + * If you find that you are using a particular class frequently, you may want to edit the + * phing/types/defaults.properties file so that it is included by default. You may also + * want to submit it (if LGPL or compatible license) to be included in Phing distribution. + * + * <pre> + * <typedef name="mytype" classname="path.to.MyHandlingClass"/> + * . + * <sometask ...> + * <mytype param1="val1" param2="val2"/> + * </sometask> + * </pre> + * + * TODO: + * -- possibly refactor since this is almost the same as TaskDefTask + * (right now these are just too simple to really justify creating an abstract class) + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class TypedefTask extends Task { + + /** Tag name for datatype that will be used in XML */ + private $name; + + /** + * Classname of task to register. + * This can be a dot-path -- relative to a location on PHP include_path. + * E.g. path.to.MyClass -> path/to/MyClass.php + * @var string + */ + private $classname; + + /** + * Path to add to PHP include_path to aid in finding specified class. + * @var Path + */ + private $classpath; + + /** Refid to already defined classpath */ + private $classpathId; + + /** + * Set the classpath to be used when searching for component being defined + * + * @param Path $classpath A Path object containing the classpath. + */ + public function setClasspath(Path $classpath) { + if ($this->classpath === null) { + $this->classpath = $classpath; + } else { + $this->classpath->append($classpath); + } + } + + /** + * Create the classpath to be used when searching for component being defined + * + * @return Path + */ + public function createClasspath() { + if ($this->classpath === null) { + $this->classpath = new Path($this->project); + } + return $this->classpath->createPath(); + } + + /** + * Reference to a classpath to use when loading the files. + */ + public function setClasspathRef(Reference $r) { + $this->classpathId = $r->getRefId(); + $this->createClasspath()->setRefid($r); + } + + /** Main entry point */ + public function main() { + if ($this->name === null || $this->classname === null) { + throw new BuildException("You must specify name and class attributes for <typedef>."); + } + $this->project->addDataTypeDefinition($this->name, $this->classname, $this->classpath); + } + + /** + * Sets the name that will be used in XML buildfile. + * @param string $name + */ + public function setName($name) { + $this->name = $name; + } + + /** + * Sets the class name / dotpath to use. + * @param string $class + */ + public function setClassname($class) { + $this->classname = $class; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/UpToDateTask.php b/buildscripts/phing/classes/phing/tasks/system/UpToDateTask.php new file mode 100755 index 00000000..1954fe88 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/UpToDateTask.php @@ -0,0 +1,257 @@ +<?php +/* + * $Id: 5b0af63dfa9acb85374dcdd2d7fd866ce81391d0 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; +include_once 'phing/tasks/system/condition/Condition.php'; +include_once 'phing/tasks/system/PropertyTask.php'; +include_once 'phing/util/DirectoryScanner.php'; +include_once 'phing/util/SourceFileScanner.php'; +include_once 'phing/mappers/MergeMapper.php'; + +/** + * Sets the given property if the specified target has a timestamp + * greater than all of the source files. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author William Ferguson <williamf@mincom.com> (Ant) + * @author Hiroaki Nakamura <hnakamur@mc.neweb.ne.jp> (Ant) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @version $Id$ + * @package phing.tasks.system + */ +class UpToDateTask extends Task implements Condition { + + private $_property; + private $_value; + private $_sourceFile; + private $_targetFile; + private $sourceFileSets = array(); + private $_filelists = array(); + + protected $mapperElement = null; + + /** + * The property to set if the target file is more up-to-date than + * (each of) the source file(s). + * + * @param property the name of the property to set if Target is up-to-date. + */ + public function setProperty($property) { + $this->_property = $property; + } + + /** + * Get property name + * @param property the name of the property to set if Target is up-to-date. + */ + public function getProperty() { + return $this->_property; + } + + /** + * The value to set the named property to if the target file is more + * up-to-date than (each of) the source file(s). Defaults to 'true'. + * + * @param value the value to set the property to if Target is up-to-date + */ + public function setValue($value) { + $this->_value = $value; + } + + /** + * Returns the value, or "true" if a specific value wasn't provided. + */ + private function getValue() { + return ($this->_value !== null) ? $this->_value : "true"; + } + + /** + * The file which must be more up-to-date than (each of) the source file(s) + * if the property is to be set. + * + * @param file the file we are checking against. + */ + public function setTargetFile($file) { + if (is_string($file)) { + $file = new PhingFile($file); + } + $this->_targetFile = $file; + } + + /** + * The file that must be older than the target file + * if the property is to be set. + * + * @param file the file we are checking against the target file. + */ + public function setSrcfile($file) { + if (is_string($file)) { + $file = new PhingFile($file); + } + $this->_sourceFile = $file; + } + + /** + * Nested <srcfiles> element. + * + * @deprecated Deprecated since Phing 2.4.0 + */ + public function createSrcfiles() { + $fs = new FileSet(); + $this->sourceFileSets[] = $fs; + return $fs; + } + + /** + * Nested <fileset> element. + */ + public function addFileset(FileSet $fs) { + $this->sourceFileSets[] = $fs; + } + + /** + * Supports embedded <filelist> element. + * @return FileList + */ + public function createFileList() { + $num = array_push($this->_filelists, new FileList()); + return $this->_filelists[$num-1]; + } + + /** + * Defines the FileNameMapper to use (nested mapper element). + */ + public function createMapper() { + if ($this->mapperElement !== null) { + throw new BuildException("Cannot define more than one mapper", + $this->location); + } + $this->mapperElement = new Mapper($this->getProject()); + return $this->mapperElement; + } + + /** + * Evaluate (all) target and source file(s) to + * see if the target(s) is/are up-to-date. + * @return boolean + */ + public function evaluate() { + if (count($this->sourceFileSets) == 0 && count($this->_filelists) == 0 && $this->_sourceFile === null) { + throw new BuildException("At least one srcfile or a nested " + . "<fileset> or <filelist> element must be set."); + } + + if ((count($this->sourceFileSets) > 0 || count($this->_filelists) > 0) && $this->_sourceFile !== null) { + throw new BuildException("Cannot specify both the srcfile " + . "attribute and a nested <fileset> " + . "or <filelist> element."); + } + + if ($this->_targetFile === null && $this->mapperElement === null) { + throw new BuildException("The targetfile attribute or a nested " + . "mapper element must be set."); + } + + // if the target file is not there, then it can't be up-to-date + if ($this->_targetFile !== null && !$this->_targetFile->exists()) { + return false; + } + + // if the source file isn't there, throw an exception + if ($this->_sourceFile !== null && !$this->_sourceFile->exists()) { + throw new BuildException($this->_sourceFile->getAbsolutePath() + . " not found."); + } + + $upToDate = true; + for($i=0,$size=count($this->sourceFileSets); $i < $size && $upToDate; $i++) { + $fs = $this->sourceFileSets[$i]; + $ds = $fs->getDirectoryScanner($this->project); + $upToDate = $upToDate && $this->scanDir($fs->getDir($this->project), + $ds->getIncludedFiles()); + } + + for($i=0,$size=count($this->_filelists); $i < $size && $upToDate; $i++) { + $fl = $this->_filelists[$i]; + $srcFiles = $fl->getFiles($this->project); + $upToDate = $upToDate && $this->scanDir($fs->getDir($this->project), + $srcFiles); + } + + if ($this->_sourceFile !== null) { + if ($this->mapperElement === null) { + $upToDate = $upToDate && + ($this->_targetFile->lastModified() >= $this->_sourceFile->lastModified()); + } else { + $sfs = new SourceFileScanner($this); + $upToDate = $upToDate && + count($sfs->restrict($this->_sourceFile->getAbsolutePath(), + null, null, + $this->mapperElement->getImplementation())) === 0; + } + } + return $upToDate; + } + + + /** + * Sets property to true if target file(s) have a more recent timestamp + * than (each of) the corresponding source file(s). + * @throws BuildException + */ + public function main() { + if ($this->_property === null) { + throw new BuildException("property attribute is required.", + $this->location); + } + $upToDate = $this->evaluate(); + if ($upToDate) { + $property = $this->project->createTask('property'); + $property->setName($this->getProperty()); + $property->setValue($this->getValue()); + $property->setOverride(true); + $property->main(); // execute + + if ($this->mapperElement === null) { + $this->log("File \"" . $this->_targetFile->getAbsolutePath() + . "\" is up-to-date.", Project::MSG_VERBOSE); + } else { + $this->log("All target files are up-to-date.", + Project::MSG_VERBOSE); + } + } + } + + protected function scanDir(PhingFile $srcDir, $files) { + $sfs = new SourceFileScanner($this); + $mapper = null; + $dir = $srcDir; + if ($this->mapperElement === null) { + $mm = new MergeMapper(); + $mm->setTo($this->_targetFile->getAbsolutePath()); + $mapper = $mm; + $dir = null; + } else { + $mapper = $this->mapperElement->getImplementation(); + } + return (count($sfs->restrict($files, $srcDir, $dir, $mapper)) === 0); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/WaitForTask.php b/buildscripts/phing/classes/phing/tasks/system/WaitForTask.php new file mode 100755 index 00000000..00bcff1e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/WaitForTask.php @@ -0,0 +1,188 @@ +<?php +/* + * $Id: dae67bf2b9c154d4614f30e9ba85c16782550bb3 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Based on Apache Ant Wait For: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Michiel Rook <mrook@php.net> + * @version $Id$ + * @package phing.tasks.system + */ +class WaitForTask extends ConditionBase +{ + const ONE_MILLISECOND = 1; + const ONE_SECOND = 1000; + const ONE_MINUTE = 60000; + const ONE_HOUR = 3600000; + const ONE_DAY = 86400000; + const ONE_WEEK = 604800000; + + const DEFAULT_MAX_WAIT_MILLIS = 180000; + const DEFAULT_CHECK_MILLIS = 500; + + protected $maxWait = self::DEFAULT_MAX_WAIT_MILLIS; + protected $maxWaitMultiplier = self::ONE_MILLISECOND; + + protected $checkEvery = self::DEFAULT_CHECK_MILLIS; + protected $checkEveryMultiplier = self::ONE_MILLISECOND; + + protected $timeoutProperty = null; + + /** + * Set the maximum length of time to wait. + * @param int $maxWait + */ + public function setMaxWait($maxWait) + { + $this->maxWait = (int) $maxWait; + } + + /** + * Set the max wait time unit + * @param string $maxWaitUnit + */ + public function setMaxWaitUnit($maxWaitUnit) + { + $this->maxWaitMultiplier = $this->_convertUnit($maxWaitUnit); + } + + /** + * Set the time between each check + * @param int $checkEvery + */ + public function setCheckEvery($checkEvery) + { + $this->checkEvery = (int) $checkEvery; + } + + /** + * Set the check every time unit + * @param string $checkEveryUnit + */ + public function setCheckEveryUnit($checkEveryUnit) + { + $this->checkEveryMultiplier = $this->_convertUnit($checkEveryUnit); + } + + /** + * Name of the property to set after a timeout. + * @param string $timeoutProperty + */ + public function setTimeoutProperty($timeoutProperty) + { + $this->timeoutProperty = $timeoutProperty; + } + + /** + * Convert the unit to a multipler. + * @param string $unit + */ + protected function _convertUnit($unit) + { + switch ($unit) { + case "week": { + return self::ONE_WEEK; + } + + case "day": { + return self::ONE_DAY; + } + + case "hour": { + return self::ONE_HOUR; + } + + case "minute": { + return self::ONE_MINUTE; + } + + case "second": { + return self::ONE_SECOND; + } + + case "millisecond": { + return self::ONE_MILLISECOND; + } + + default: { + throw new BuildException("Illegal unit '$unit'"); + } + } + } + + /** + * Check repeatedly for the specified conditions until they become + * true or the timeout expires. + * @throws BuildException + */ + public function main() + { + if ($this->countConditions() > 1) { + throw new BuildException("You must not nest more than one condition into <waitfor>"); + } + + if ($this->countConditions() < 1) { + throw new BuildException("You must nest a condition into <waitfor>"); + } + + $cs = $this->getIterator(); + $condition = $cs->current(); + + $maxWaitMillis = $this->maxWait * $this->maxWaitMultiplier; + $checkEveryMillis = $this->checkEvery * $this->checkEveryMultiplier; + + $start = microtime(true) * 1000; + $end = $start + $maxWaitMillis; + + while (microtime(true) * 1000 < $end) { + if ($condition->evaluate()) { + $this->log("waitfor: condition was met", Project::MSG_VERBOSE); + + return; + } + + usleep($checkEveryMillis * 1000); + } + + $this->log("waitfor: timeout", Project::MSG_VERBOSE); + + if ($this->timeoutProperty != null) { + $this->project->setNewProperty($this->timeoutProperty, "true"); + } + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/tasks/system/WarnTask.php b/buildscripts/phing/classes/phing/tasks/system/WarnTask.php new file mode 100755 index 00000000..28f0ad92 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/WarnTask.php @@ -0,0 +1,35 @@ +<?php +/* + * $Id: a2192433abbea9c5fe4b4ddb518cde8c09a643da $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/EchoTask.php'; + +/** + * Simple task to echo a warning message (Project::MSG_WARN) to all output devices. + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system + */ +class WarnTask extends EchoTask { + function main() { + $this->log($this->msg, Project::MSG_WARN); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/XsltTask.php b/buildscripts/phing/classes/phing/tasks/system/XsltTask.php new file mode 100644 index 00000000..7155caf8 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/XsltTask.php @@ -0,0 +1,103 @@ +<?php +/* + * $Id: 8f87e1c7908c06223382baf628d018c3a0f10824 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/CopyTask.php'; +include_once 'phing/system/io/FileReader.php'; +include_once 'phing/system/io/FileWriter.php'; +include_once 'phing/filters/XsltFilter.php'; + +/** + * Implements an XSLT processing filter while copying files. + * + * This is a shortcut for calling the <copy> task with the XSLTFilter used + * in the <filterchains> section. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id: 8f87e1c7908c06223382baf628d018c3a0f10824 $ + * @package phing.tasks.system + */ +class XsltTask extends CopyTask { + + /** XSLTFilter object that we use to handle transformation. */ + private $xsltFilter; + + /** Parameters to pass to XSLT procesor. */ + private $parameters = array(); + + /** + * Setup the filterchains w/ XSLTFilter that we will use while copying the files. + */ + function init() { + $xf = new XsltFilter(); + $chain = $this->createFilterChain($this->getProject()); + $chain->addXsltFilter($xf); + $this->xsltFilter = $xf; + } + + /** + * Set any XSLT Param and invoke CopyTask::main() + * @see CopyTask::main() + */ + function main() { + $this->log("Doing XSLT transformation using stylesheet " . $this->xsltFilter->getStyle(), Project::MSG_VERBOSE); + $this->xsltFilter->setParams($this->parameters); + parent::main(); + } + + /** + * Set the stylesheet to use. + * @param PhingFile $style + */ + function setStyle(PhingFile $style) { + $this->xsltFilter->setStyle($style); + } + + /** + * Whether to resolve entities in the XML document. + * + * @param bool $resolveExternals + * + * @since 2.4 + */ + function setResolveDocumentExternals($resolveExternals) { + $this->xsltFilter->setResolveDocumentExternals((bool)$resolveExternals); + } + + /** + * Whether to resolve entities in the stylesheet. + * + * @param bool $resolveExternals + * + * @since 2.4 + */ + function setResolveStylesheetExternals($resolveExternals) { + $this->xsltFilter->setResolveStylesheetExternals((bool)$resolveExternals); + } + + /** + * Support nested <param> tags useing XSLTParam class. + * @return XSLTParam + */ + function createParam() { + $num = array_push($this->parameters, new XSLTParam()); + return $this->parameters[$num-1]; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/AndCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/AndCondition.php new file mode 100755 index 00000000..41b57a8e --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/AndCondition.php @@ -0,0 +1,46 @@ +<?php +/* + * $Id: 69074307e3d1aae5fbfaa03842f5a8fc14b49625 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/ConditionBase.php'; + +/** + * <and> condition container. + * + * Iterates over all conditions and returns false as soon as one + * evaluates to false. + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.tasks.system.condition + */ +class AndCondition extends ConditionBase implements Condition { + + public function evaluate() { + foreach($this as $c) { // ConditionBase implements IteratorAggregator + if (!$c->evaluate()) { + return false; + } + } + return true; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/Condition.php b/buildscripts/phing/classes/phing/tasks/system/condition/Condition.php new file mode 100755 index 00000000..9c9d90e5 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/Condition.php @@ -0,0 +1,38 @@ +<?php + +/* + * $Id: c971532805c4ac4c3d3cbf05a5c53abe7279b336 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Condition interface specification: + * + * Each condition must implement a method applying to this prototye: + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.tasks.system.condition + */ +interface Condition { + /** + * @return boolean + * @throws BuildException + */ + public function evaluate(); +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/ConditionBase.php b/buildscripts/phing/classes/phing/tasks/system/condition/ConditionBase.php new file mode 100755 index 00000000..e21ce4e4 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/ConditionBase.php @@ -0,0 +1,197 @@ +<?php +/* + * $Id: 8721880badf6aee475a8cb87c88ca3dc4299efb8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; +include_once 'phing/Project.php'; +include_once 'phing/tasks/system/AvailableTask.php'; +include_once 'phing/tasks/system/condition/Condition.php'; + +/** + * Abstract baseclass for the <condition> task as well as several + * conditions - ensures that the types of conditions inside the task + * and the "container" conditions are in sync. + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @package phing.tasks.system.condition + */ +abstract class ConditionBase extends ProjectComponent implements IteratorAggregate { + + public $conditions = array(); // needs to be public for "inner" class access + + function countConditions() { + return count($this->conditions); + } + + /** + * Required for IteratorAggregate + */ + function getIterator() { + return new ConditionEnumeration($this); + } + + function getConditions() { + return $this->conditions; + } + + /** + * @return void + */ + function addAvailable(AvailableTask $a) { + $this->conditions[] = $a; + } + + /** + * @return NotCondition + */ + function createNot() { + include_once 'phing/tasks/system/condition/NotCondition.php'; + $num = array_push($this->conditions, new NotCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return AndCondition + */ + function createAnd() { + include_once 'phing/tasks/system/condition/AndCondition.php'; + $num = array_push($this->conditions, new AndCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return OrCondition + */ + function createOr() { + include_once 'phing/tasks/system/condition/OrCondition.php'; + $num = array_push($this->conditions, new OrCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return EqualsCondition + */ + function createEquals() { + include_once 'phing/tasks/system/condition/EqualsCondition.php'; + $num = array_push($this->conditions, new EqualsCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return OsCondition + */ + function createOs() { + include_once 'phing/tasks/system/condition/OsCondition.php'; + $num = array_push($this->conditions, new OsCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return IsFalseCondition + */ + function createIsFalse() { + include_once 'phing/tasks/system/condition/IsFalseCondition.php'; + $num = array_push($this->conditions, new IsFalseCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return IsTrueCondition + */ + function createIsTrue() { + include_once 'phing/tasks/system/condition/IsTrueCondition.php'; + $num = array_push($this->conditions, new IsTrueCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return ContainsCondition + */ + function createContains() { + include_once 'phing/tasks/system/condition/ContainsCondition.php'; + $num = array_push($this->conditions, new ContainsCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return IsSetCondition + */ + function createIsSet() { + include_once 'phing/tasks/system/condition/IsSetCondition.php'; + $num = array_push($this->conditions, new IsSetCondition()); + return $this->conditions[$num-1]; + } + + /** + * @return ReferenceExistsCondition + */ + function createReferenceExists() { + include_once 'phing/tasks/system/condition/ReferenceExistsCondition.php'; + $num = array_push($this->conditions, new ReferenceExistsCondition()); + return $this->conditions[$num-1]; + } + +} + +/** + * "Inner" class for handling enumerations. + * Uses build-in PHP5 iterator support. + * + * @package phing.tasks.system.condition + */ +class ConditionEnumeration implements Iterator { + + /** Current element number */ + private $num = 0; + + /** "Outer" ConditionBase class. */ + private $outer; + + function __construct(ConditionBase $outer) { + $this->outer = $outer; + } + + public function valid() { + return $this->outer->countConditions() > $this->num; + } + + function current() { + $o = $this->outer->conditions[$this->num]; + if ($o instanceof ProjectComponent) { + $o->setProject($this->outer->getProject()); + } + return $o; + } + + function next() { + $this->num++; + } + + function key() { + return $this->num; + } + + function rewind() { + $this->num = 0; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/ContainsCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/ContainsCondition.php new file mode 100755 index 00000000..d7fb80ac --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/ContainsCondition.php @@ -0,0 +1,76 @@ +<?php + +/* + * $Id: 6cdecc53a715fce4601b1ceb64c8ec95d29c1468 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/Condition.php'; + +/** + * Is one string part of another string? + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @version $Id: 6cdecc53a715fce4601b1ceb64c8ec95d29c1468 $ + * @package phing.tasks.system.condition + */ +class ContainsCondition implements Condition { + + private $string; + private $subString; + private $caseSensitive = true; + + /** + * The string to search in. + * @param string $a1 + */ + public function setString($a1) { + $this->string = $a1; + } + + /** + * The string to search for. + * @param string $a2 + */ + public function setSubstring($a2) { + $this->subString = $a2; + } + + /** + * Whether to search ignoring case or not. + */ + public function setCaseSensitive($b) { + $this->caseSensitive = (boolean) $b; + } + + /** + * Check whether string contains substring. + * @throws BuildException + */ + public function evaluate() { + if ($this->string === null || $this->subString === null) { + throw new BuildException("both string and substring are required " + . "in contains"); + } + + return $this->caseSensitive + ? strpos($this->string, $this->subString) !== false + : strpos(strtolower($this->string), strtolower($this->subString)) !== false; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/EqualsCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/EqualsCondition.php new file mode 100755 index 00000000..4be4e8fe --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/EqualsCondition.php @@ -0,0 +1,78 @@ +<?php +/* + * $Id: faec716501a00cdeb84b8c893b5bbe5c76064dec $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/Condition.php'; + +/** + * A simple string comparator. Compares two strings for eqiality in a + * binary safe manner. Implements the condition interface specification. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id: faec716501a00cdeb84b8c893b5bbe5c76064dec $ + * @access public + * @package phing.tasks.system.condition + */ +class EqualsCondition implements Condition { + + private $arg1; + private $arg2; + private $trim = false; + private $caseSensitive = true; + + public function setArg1($a1) { + $this->arg1 = $a1; + } + + public function setArg2($a2) { + $this->arg2 = $a2; + } + + /** + * Should we want to trim the arguments before comparing them? + * @param boolean $b + */ + public function setTrim($b) { + $this->trim = (boolean) $b; + } + + /** + * Should the comparison be case sensitive? + * @param boolean $b + */ + public function setCaseSensitive($b) { + $this->caseSensitive = (boolean) $b; + } + + public function evaluate() { + if ($this->arg1 === null || $this->arg2 === null) { + throw new BuildException("Both arg1 and arg2 are required in equals."); + } + + if ($this->trim) { + $this->arg1 = trim($this->arg1); + $this->arg2 = trim($this->arg2); + } + + //print("[comparison] Comparing '".$this->arg1."' and '".$this->arg2."'\n"); + return $this->caseSensitive ? $this->arg1 === $this->arg2 : strtolower($this->arg1) === strtolower($this->arg2); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/IsFalseCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/IsFalseCondition.php new file mode 100755 index 00000000..8a1b3dcf --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/IsFalseCondition.php @@ -0,0 +1,60 @@ +<?php +/* + * $Id: f7d355bdf8f7aa539afb572e2fe033ffd3bcd89b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; +require_once 'phing/tasks/system/condition/Condition.php'; + +/** + * Condition that tests whether a given string evals to false. + * + * @author Hans Lellelid (Phing) + * @author Steve Loughran (Ant) + * @version $Id: f7d355bdf8f7aa539afb572e2fe033ffd3bcd89b $ + * @package phing.tasks.system.condition + */ +class IsFalseCondition extends ProjectComponent implements Condition { + + /** + * what we eval + */ + private $value; + + /** + * Set the value to be tested. + * @param boolean $value + */ + public function setValue($value) { + $this->value = $value; + } + + /** + * return the inverted value; + * @throws BuildException if someone forgot to spec a value + */ + public function evaluate() { + if ($this->value === null) { + throw new BuildException("Nothing to test for falsehood"); + } + return !$this->value; + } + +} + diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/IsSetCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/IsSetCondition.php new file mode 100755 index 00000000..24f3f609 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/IsSetCondition.php @@ -0,0 +1,53 @@ +<?php +/* + * $Id: 5c5924da5cff2626af09b4908703a21eec79c777 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; +require_once 'phing/tasks/system/condition/Condition.php'; + +/** + * Condition that tests whether a given property has been set. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @version $Id$ + * @package phing.tasks.system.condition + */ +class IsSetCondition extends ProjectComponent implements Condition { + + private $property; + + public function setProperty($p) { + $this->property = $p; + } + + /** + * Check whether property is set. + * @throws BuildException + */ + public function evaluate() { + if ($this->property === null) { + throw new BuildException("No property specified for isset " + . "condition"); + } + return $this->project->getProperty($this->property) !== null; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/IsTrueCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/IsTrueCondition.php new file mode 100644 index 00000000..8c6d19fc --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/IsTrueCondition.php @@ -0,0 +1,59 @@ +<?php +/* + * $Id: d567f7c477e075a5c06d3f8e07ff8a7412cf31cd $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; +require_once 'phing/tasks/system/condition/Condition.php'; + +/** + * Condition that tests whether a given string evals to true. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Steve Loughran (Ant) + * @package phing.tasks.system.condition + */ +class IsTrueCondition extends ProjectComponent implements Condition { + + /** + * what we eval + */ + private $value; + + /** + * Set the value to be tested. + * @param boolean $value + */ + public function setValue($value) { + $this->value = $value; + } + + /** + * return the inverted value; + * @throws BuildException if someone forgot to spec a value + */ + public function evaluate() { + if ($this->value === null) { + throw new BuildException("Nothing to test for falsehood"); + } + return $this->value; + } + +} + diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/NotCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/NotCondition.php new file mode 100755 index 00000000..081876af --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/NotCondition.php @@ -0,0 +1,48 @@ +<?php +/* + * $Id: d8c985da7c759357135cf717b1f4b52a1bd50cb6 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/ConditionBase.php'; + +/** + * <not> condition. + * + * Evaluates to true if the single condition nested into it is false + * and vice versa. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.tasks.system.condition + */ +class NotCondition extends ConditionBase implements Condition { + + function evaluate() { + if ($this->countConditions() > 1) { + throw new BuildException("You must not nest more than one condition into <not>"); + } + if ($this->countConditions() < 1) { + throw new BuildException("You must nest a condition into <not>"); + } + $conds = $this->getIterator(); + return !$conds->current()->evaluate(); + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/OrCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/OrCondition.php new file mode 100755 index 00000000..d88df271 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/OrCondition.php @@ -0,0 +1,46 @@ +<?php +/* + * $Id: adcd6785a85304a0860cc3dd9a0146d965c1cb0c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/ConditionBase.php'; + +/** + * <or> condition container. + * + * Iterates over all conditions and returns true as soon as one + * evaluates to true. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.tasks.system.condition + */ +class OrCondition extends ConditionBase implements Condition { + + function evaluate() { + foreach($this as $c) { // ConditionBase implements IteratorAggregator + if ($c->evaluate()) { + return true; + } + } + return false; + } +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/OsCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/OsCondition.php new file mode 100755 index 00000000..50a7174f --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/OsCondition.php @@ -0,0 +1,63 @@ +<?php +/* + * $Id: d63246e2d25230f5ba6e45a651497e3ba01abe2c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/tasks/system/condition/ConditionBase.php'; + +/** + * Condition that tests the OS type. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @copyright 2001,2002 THYRELL. All rights reserved + * @version $Id$ + * @access public + * @package phing.tasks.system.condition + */ +class OsCondition implements Condition { + + private $family; + + function setFamily($f) { + $this->family = strtolower($f); + } + + function evaluate() { + $osName = strtolower(Phing::getProperty("os.name")); + + if ($this->family !== null) { + if ($this->family === "windows") { + return StringHelper::startsWith("win", $osName); + } elseif ($this->family === "mac") { + return (strpos($osName, "mac") !== false || strpos($osName, "darwin") !== false); + } elseif ($this->family === ("unix")) { + return ( + StringHelper::endsWith("ix", $osName) || + StringHelper::endsWith("ux", $osName) || + StringHelper::endsWith("bsd", $osName) || + StringHelper::startsWith("sunos", $osName) || + StringHelper::startsWith("darwin", $osName) + ); + } + throw new BuildException("Don't know how to detect os family '" . $this->family . "'"); + } + return false; + } + +} diff --git a/buildscripts/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php b/buildscripts/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php new file mode 100755 index 00000000..08e291e3 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php @@ -0,0 +1,52 @@ +<?php +/* + * $Id: e62ed1e00cc6ed859746760c89bc0f873db4620a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; require_once 'phing/tasks/system/condition/Condition.php'; + +/** + * Condition that tests whether a given reference exists. + * + * @author Matthias Pigulla <mp@webfactory.de> (Phing) + * @version $Id$ + * @package phing.tasks.system.condition */ +class ReferenceExistsCondition extends ProjectComponent implements Condition { + + private $refid; + + public function setRef($id) { + $this->refid = (string) $id; + } + + /** + * Check whether the reference exists. + * @throws BuildException + */ + public function evaluate() { + if ($this->refid === null) { + throw new BuildException("No ref attribute specified for reference-exists " + . "condition"); + } + $refs = $this->project->getReferences(); + return isset($refs[$this->refid]); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/AbstractFileSet.php b/buildscripts/phing/classes/phing/types/AbstractFileSet.php new file mode 100755 index 00000000..60ca4d04 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/AbstractFileSet.php @@ -0,0 +1,587 @@ +<?php +/* + * $Id: b7d8ccab6ed556e74626d880dcc09be20ea8bd58 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/PhingFile.php'; +include_once 'phing/types/DataType.php'; +include_once 'phing/types/PatternSet.php'; +include_once 'phing/types/selectors/BaseSelector.php'; +include_once 'phing/types/selectors/SelectorContainer.php'; +include_once 'phing/types/selectors/BaseSelectorContainer.php'; + +// Load all of the selectors (not really necessary but +// helps reveal parse errors right away) + +include_once 'phing/types/selectors/AndSelector.php'; +include_once 'phing/types/selectors/ContainsSelector.php'; +include_once 'phing/types/selectors/ContainsRegexpSelector.php'; +include_once 'phing/types/selectors/DateSelector.php'; +include_once 'phing/types/selectors/DependSelector.php'; +include_once 'phing/types/selectors/DepthSelector.php'; +include_once 'phing/types/selectors/ExtendSelector.php'; +include_once 'phing/types/selectors/FilenameSelector.php'; +include_once 'phing/types/selectors/MajoritySelector.php'; +include_once 'phing/types/selectors/NoneSelector.php'; +include_once 'phing/types/selectors/NotSelector.php'; +include_once 'phing/types/selectors/OrSelector.php'; +include_once 'phing/types/selectors/PresentSelector.php'; +include_once 'phing/types/selectors/SizeSelector.php'; +include_once 'phing/types/selectors/TypeSelector.php'; + +include_once 'phing/util/DirectoryScanner.php'; + +/** + * The FileSet class provides methods and properties for accessing + * and managing filesets. It extends ProjectComponent and thus inherits + * all methods and properties (not explicitly declared). See ProjectComponent + * for further detail. + * + * TODO: + * - merge this with patternsets: FileSet extends PatternSet !!! + * requires additional mods to the parsing algo + * [HL] .... not sure if that really makes so much sense. I think + * that perhaps they should use common utility class if there really + * is that much shared functionality + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @see ProjectComponent + * @package phing.types + */ +class AbstractFileSet extends DataType implements SelectorContainer { + + // These vars are public for cloning purposes + + /** + * @var boolean + */ + public $useDefaultExcludes = true; + + /** + * Whether to expand/dereference symbolic links, default is false + * @var boolean + */ + protected $expandSymbolicLinks = false; + + /** + * @var PatternSet + */ + public $defaultPatterns; + + public $additionalPatterns = array(); + public $dir; + public $isCaseSensitive = true; + public $selectors = array(); + + function __construct($fileset = null) { + if ($fileset !== null && ($fileset instanceof FileSet)) { + $this->dir = $fileset->dir; + $this->defaultPatterns = $fileset->defaultPatterns; + $this->additionalPatterns = $fileset->additionalPatterns; + $this->useDefaultExcludes = $fileset->useDefaultExcludes; + $this->isCaseSensitive = $fileset->isCaseSensitive; + $this->selectors = $fileset->selectors; + } + $this->defaultPatterns = new PatternSet(); + } + + /** + * Sets whether to expand/dereference symbolic links, default is false + * @var boolean + */ + function setExpandSymbolicLinks($expandSymbolicLinks) + { + $this->expandSymbolicLinks = $expandSymbolicLinks; + } + + /** + * Makes this instance in effect a reference to another PatternSet + * instance. + * You must not set another attribute or nest elements inside + * this element if you make it a reference. + */ + function setRefid(Reference $r) { + if ((isset($this->dir) && !is_null($this->dir)) || $this->defaultPatterns->hasPatterns()) { + throw $this->tooManyAttributes(); + } + if (!empty($this->additionalPatterns)) { + throw $this->noChildrenAllowed(); + } + if (!empty($this->selectors)) { + throw $this->noChildrenAllowed(); + } + parent::setRefid($r); + } + + + function setDir($dir) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($dir instanceof PhingFile) { + $dir = $dir->getPath(); + } + $this->dir = new PhingFile((string) $dir); + } + + + function getDir(Project $p) { + if ($this->isReference()) { + return $this->getRef($p)->getDir($p); + } + return $this->dir; + } + + + function createPatternSet() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + $num = array_push($this->additionalPatterns, new PatternSet()); + return $this->additionalPatterns[$num-1]; + } + + /** + * add a name entry on the include list + */ + function createInclude() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + return $this->defaultPatterns->createInclude(); + } + + /** + * add a name entry on the include files list + */ + function createIncludesFile() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + return $this->defaultPatterns->createIncludesFile(); + } + + /** + * add a name entry on the exclude list + */ + function createExclude() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + return $this->defaultPatterns->createExclude(); + } + + /** + * add a name entry on the include files list + */ + function createExcludesFile() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + return; + } + return $this->defaultPatterns->createExcludesFile(); + } + + /** + * Sets the set of include patterns. Patterns may be separated by a comma + * or a space. + */ + function setIncludes($includes) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->defaultPatterns->setIncludes($includes); + } + + /** + * Sets the set of exclude patterns. Patterns may be separated by a comma + * or a space. + */ + function setExcludes($excludes) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->defaultPatterns->setExcludes($excludes); + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param $incl The file to fetch the include patterns from. + * @throws BE + */ + function setIncludesfile($incl) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->defaultPatterns->setIncludesfile($incl); + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param $excl The file to fetch the exclude patterns from. + * @throws BE + */ + function setExcludesfile($excl) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->defaultPatterns->setExcludesfile($excl); + } + + /** + * Sets whether default exclusions should be used or not. + * + * @param $useDefaultExcludes "true"|"on"|"yes" when default exclusions + * should be used, "false"|"off"|"no" when they + * shouldn't be used. + */ + function setDefaultexcludes($useDefaultExcludes) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->useDefaultExcludes = $useDefaultExcludes; + } + + /** + * Sets case sensitivity of the file system + */ + function setCaseSensitive($isCaseSensitive) { + $this->isCaseSensitive = $isCaseSensitive; + } + + /** returns a reference to the dirscanner object belonging to this fileset */ + function getDirectoryScanner(Project $p) { + if ($this->isReference()) { + $o = $this->getRef($p); + return $o->getDirectoryScanner($p); + } + + if ($this->dir === null) { + throw new BuildException("No directory specified for fileset."); + } + if (!$this->dir->exists()) { + throw new BuildException("Directory ".$this->dir->getAbsolutePath()." not found."); + } + if (!$this->dir->isLink() || !$this->expandSymbolicLinks) { + if (!$this->dir->isDirectory()) { + throw new BuildException($this->dir->getAbsolutePath()." is not a directory."); + } + } + $ds = new DirectoryScanner(); + $ds->setExpandSymbolicLinks($this->expandSymbolicLinks); + $this->setupDirectoryScanner($ds, $p); + $ds->scan(); + return $ds; + } + + /** feed dirscanner with infos defined by this fileset */ + protected function setupDirectoryScanner(DirectoryScanner $ds, Project $p) { + if ($ds === null) { + throw new Exception("DirectoryScanner cannot be null"); + } + // FIXME - pass dir directly wehn dirscanner supports File + $ds->setBasedir($this->dir->getPath()); + + foreach($this->additionalPatterns as $addPattern) { + $this->defaultPatterns->append($addPattern, $p); + } + + $ds->setIncludes($this->defaultPatterns->getIncludePatterns($p)); + $ds->setExcludes($this->defaultPatterns->getExcludePatterns($p)); + + $p->log("FileSet: Setup file scanner in dir " . $this->dir->__toString() . " with " . $this->defaultPatterns->toString(), Project::MSG_DEBUG); + + if ($ds instanceof SelectorScanner) { + $ds->setSelectors($this->getSelectors($p)); + } + + if ($this->useDefaultExcludes) { + $ds->addDefaultExcludes(); + } + $ds->setCaseSensitive($this->isCaseSensitive); + } + + + /** + * Performs the check for circular references and returns the + * referenced FileSet. + */ + function getRef(Project $p) { + if (!$this->checked) { + $stk = array(); + array_push($stk, $this); + $this->dieOnCircularReference($stk, $p); + } + + $o = $this->ref->getReferencedObject($p); + if (!($o instanceof FileSet)) { + $msg = $this->ref->getRefId()." doesn't denote a fileset"; + throw new BuildException($msg); + } else { + return $o; + } + } + + // SelectorContainer methods + + /** + * Indicates whether there are any selectors here. + * + * @return boolean Whether any selectors are in this container + */ + public function hasSelectors() { + if ($this->isReference() && $this->getProject() !== null) { + return $this->getRef($this->getProject())->hasSelectors(); + } + return !empty($this->selectors); + } + + /** + * Indicates whether there are any patterns here. + * + * @return boolean Whether any patterns are in this container. + */ + public function hasPatterns() { + + if ($this->isReference() && $this->getProject() !== null) { + return $this->getRef($this->getProject())->hasPatterns(); + } + + if ($this->defaultPatterns->hasPatterns($this->getProject())) { + return true; + } + + for($i=0,$size=count($this->additionalPatterns); $i < $size; $i++) { + $ps = $this->additionalPatterns[$i]; + if ($ps->hasPatterns($this->getProject())) { + return true; + } + } + + return false; + } + + /** + * Gives the count of the number of selectors in this container + * + * @return int The number of selectors in this container + */ + public function selectorCount() { + if ($this->isReference() && $this->getProject() !== null) { + try { + return $this->getRef($this->getProject())->selectorCount(); + } catch (Exception $e) { + throw $e; + } + } + return count($this->selectors); + } + + /** + * Returns the set of selectors as an array. + * + * @return an array of selectors in this container + */ + public function getSelectors(Project $p) { + if ($this->isReference()) { + return $this->getRef($p)->getSelectors($p); + } else { + // *copy* selectors + $result = array(); + for($i=0,$size=count($this->selectors); $i < $size; $i++) { + $result[] = clone $this->selectors[$i]; + } + return $result; + } + } + + /** + * Returns an array for accessing the set of selectors. + * + * @return array The array of selectors + */ + public function selectorElements() { + if ($this->isReference() && $this->getProject() !== null) { + return $this->getRef($this->getProject())->selectorElements(); + } + return $this->selectors; + } + + /** + * Add a new selector into this container. + * + * @param selector the new selector to add + */ + public function appendSelector(FileSelector $selector) { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + $this->selectors[] = $selector; + } + + /* Methods below all add specific selectors */ + + /** + * add a "Select" selector entry on the selector list + */ + public function createSelector() { + $o = new SelectSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add an "And" selector entry on the selector list + */ + public function createAnd() { + $o = new AndSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add an "Or" selector entry on the selector list + */ + public function createOr() { + $o = new OrSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a "Not" selector entry on the selector list + */ + public function createNot() { + $o = new NotSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a "None" selector entry on the selector list + */ + public function createNone() { + $o = new NoneSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a majority selector entry on the selector list + */ + public function createMajority() { + $o = new MajoritySelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a selector date entry on the selector list + */ + public function createDate() { + $o = new DateSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a selector size entry on the selector list + */ + public function createSize() { + $o = new SizeSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a selector filename entry on the selector list + */ + public function createFilename() { + $o = new FilenameSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add an extended selector entry on the selector list + */ + public function createCustom() { + $o = new ExtendSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a contains selector entry on the selector list + */ + public function createContains() { + $o = new ContainsSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a contains selector entry on the selector list + */ + public function createContainsRegexp() { + $o = new ContainsRegexpSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a present selector entry on the selector list + */ + public function createPresent() { + $o = new PresentSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a depth selector entry on the selector list + */ + public function createDepth() { + $o = new DepthSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a depends selector entry on the selector list + */ + public function createDepend() { + $o = new DependSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a type selector entry on the selector list + */ + public function createType() { + $o = new TypeSelector(); + $this->appendSelector($o); + return $o; + } +} diff --git a/buildscripts/phing/classes/phing/types/Commandline.php b/buildscripts/phing/classes/phing/types/Commandline.php new file mode 100755 index 00000000..d6b36e14 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Commandline.php @@ -0,0 +1,475 @@ +<?php +/* + * $Id: 891b349dcd88b825ae99edf53ed4c7ac2c1c2467 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +/** + * Commandline objects help handling command lines specifying processes to + * execute. + * + * The class can be used to define a command line as nested elements or as a + * helper to define a command line by an application. + * <p> + * <code> + * <someelement><br> + * <acommandline executable="/executable/to/run"><br> + * <argument value="argument 1" /><br> + * <argument line="argument_1 argument_2 argument_3" /><br> + * <argument value="argument 4" /><br> + * </acommandline><br> + * </someelement><br> + * </code> + * The element <code>someelement</code> must provide a method + * <code>createAcommandline</code> which returns an instance of this class. + * + * @author thomas.haas@softwired-inc.com + * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> + * @package phing.types + */ +class Commandline { + + /** + * @var array CommandlineArguments[] + */ + public $arguments = array(); // public so "inner" class can access + + /** + * Full path (if not on %PATH% env var) to executable program. + * @var string + */ + public $executable; // public so "inner" class can access + + const DISCLAIMER = "The ' characters around the executable and arguments are not part of the command."; + + public function __construct($to_process = null) { + if ($to_process !== null) { + $tmp = $this->translateCommandline($to_process); + if ($tmp) { + $this->setExecutable(array_shift($tmp)); // removes first el + foreach($tmp as $arg) { // iterate through remaining elements + $this->createArgument()->setValue($arg); + } + } + } + } + + + /** + * Creates an argument object and adds it to our list of args. + * + * <p>Each commandline object has at most one instance of the + * argument class.</p> + * + * @param boolean $insertAtStart if true, the argument is inserted at the + * beginning of the list of args, otherwise it is appended. + * @return CommandlineArgument + */ + public function createArgument($insertAtStart = false) { + $argument = new CommandlineArgument($this); + if ($insertAtStart) { + array_unshift($this->arguments, $argument); + } else { + array_push($this->arguments, $argument); + } + return $argument; + } + + /** + * Sets the executable to run. + */ + public function setExecutable($executable) { + if (!$executable) { + return; + } + $this->executable = $executable; + $this->executable = strtr($this->executable, '/', DIRECTORY_SEPARATOR); + $this->executable = strtr($this->executable, '\\', DIRECTORY_SEPARATOR); + } + + public function getExecutable() { + return $this->executable; + } + + public function addArguments($line) { + foreach($line as $arg) { + $this->createArgument()->setValue($arg); + } + } + + /** + * Returns the executable and all defined arguments. + * @return array + */ + public function getCommandline() { + $args = $this->getArguments(); + if ($this->executable === null) { + return $args; + } + return array_merge(array($this->executable), $args); + } + + + /** + * Returns all arguments defined by <code>addLine</code>, + * <code>addValue</code> or the argument object. + */ + public function getArguments() { + $result = array(); + foreach($this->arguments as $arg) { + $parts = $arg->getParts(); + if ($parts !== null) { + foreach($parts as $part) { + $result[] = $part; + } + } + } + return $result; + } + + public function __toString() { + return self::toString($this->getCommandline()); + } + + /** + * Put quotes around the given String if necessary. + * + * <p>If the argument doesn't include spaces or quotes, return it + * as is. If it contains double quotes, use single quotes - else + * surround the argument by double quotes.</p> + * + * @exception BuildException if the argument contains both, single + * and double quotes. + */ + public static function quoteArgument($argument, $escape = false) { + if ($escape) { + return escapeshellarg($argument); + } elseif (strpos($argument, "\"") !== false) { + if (strpos($argument, "'") !== false) { + throw new BuildException("Can't handle single and double quotes in same argument"); + } else { + return escapeshellarg($argument); + } + } elseif (strpos($argument, "'") !== false || strpos($argument, " ") !== false) { + return escapeshellarg($argument); + //return '\"' . $argument . '\"'; + } else { + return $argument; + } + } + + /** + * Quotes the parts of the given array in way that makes them + * usable as command line arguments. + */ + public static function toString($lines, $escape = false) { + // empty path return empty string + if (!$lines) { + return ""; + } + + // path containing one or more elements + $result = ""; + for ($i = 0, $len=count($lines); $i < $len; $i++) { + if ($i > 0) { + $result .= ' '; + } + $result .= self::quoteArgument($lines[$i], $escape); + } + return $result; + } + + /** + * + * @param string $to_process + * @return array + */ + public static function translateCommandline($to_process) { + + if (!$to_process) { + return array(); + } + + // parse with a simple finite state machine + + $normal = 0; + $inQuote = 1; + $inDoubleQuote = 2; + + $state = $normal; + $args = array(); + $current = ""; + $lastTokenHasBeenQuoted = false; + + $tok = strtok($to_process, ""); + $tokens = preg_split('/(["\' ])/', $to_process, -1, PREG_SPLIT_DELIM_CAPTURE); + while(($nextTok = array_shift($tokens)) !== null) { + switch ($state) { + case $inQuote: + if ("'" == $nextTok) { + $lastTokenHasBeenQuoted = true; + $state = $normal; + } else { + $current .= $nextTok; + } + break; + case $inDoubleQuote: + if ("\"" == $nextTok) { + $lastTokenHasBeenQuoted = true; + $state = $normal; + } else { + $current .= $nextTok; + } + break; + default: + if ("'" == $nextTok) { + $state = $inQuote; + } elseif ("\"" == $nextTok) { + $state = $inDoubleQuote; + } elseif (" " == $nextTok) { + if ($lastTokenHasBeenQuoted || strlen($current) != 0) { + $args[] = $current; + $current = ""; + } + } else { + $current .= $nextTok; + } + $lastTokenHasBeenQuoted = false; + break; + } + } + + if ($lastTokenHasBeenQuoted || strlen($current) != 0) { + $args[] = $current; + } + + if ($state == $inQuote || $state == $inDoubleQuote) { + throw new BuildException("unbalanced quotes in " . $to_process); + } + + return $args; + } + + /** + * @return int Number of components in current commandline. + */ + public function size() { + return count($this->getCommandline()); + } + + public function __copy() { + $c = new Commandline(); + $c->setExecutable($this->executable); + $c->addArguments($this->getArguments()); + return $c; + } + + /** + * Clear out the whole command line. */ + public function clear() { + $this->executable = null; + $this->arguments->removeAllElements(); + } + + /** + * Clear out the arguments but leave the executable in place for + * another operation. + */ + public function clearArgs() { + $this->arguments = array(); + } + + /** + * Return a marker. + * + * <p>This marker can be used to locate a position on the + * commandline - to insert something for example - when all + * parameters have been set.</p> + * @return CommandlineMarker + */ + public function createMarker() { + return new CommandlineMarker($this, count($this->arguments)); + } + + /** + * Returns a String that describes the command and arguments + * suitable for verbose output before a call to + * <code>Runtime.exec(String[])</code>. + * + * <p>This method assumes that the first entry in the array is the + * executable to run.</p> + * @param array $args CommandlineArgument[] to use + * @return string + */ + public function describeCommand($args = null) { + + if ($args === null) { + $args = $this->getCommandline(); + } + + if (!$args) { + return ""; + } + + $buf = "Executing '"; + $buf .= $args[0]; + $buf .= "'"; + if (count($args) > 0) { + $buf .= " with "; + $buf .= $this->describeArguments($args, 1); + } else { + $buf .= self::DISCLAIMER; + } + return $buf; + } + + /** + * Returns a String that describes the arguments suitable for + * verbose output before a call to + * <code>Runtime.exec(String[])</code> + * @param $args arguments to use (default is to use current class args) + * @param $offset ignore entries before this index + * @return string + */ + protected function describeArguments($args = null, $offset = 0) { + if ($args === null) { + $args = $this->getArguments(); + } + + if ($args === null || count($args) <= $offset) { + return ""; + } + + $buf = "argument"; + if (count($args) > $offset) { + $buf .= "s"; + } + $buf .= ":" . PHP_EOL; + for ($i = $offset, $alen=count($args); $i < $alen; $i++) { + $buf .= "'" . $args[$i] . "'" . PHP_EOL; + } + $buf .= self::DISCLAIMER; + return $buf; + } +} + + +/** + * "Inner" class used for nested xml command line definitions. + * + * @package phing.types + */ +class CommandlineArgument { + + private $parts = array(); + private $outer; + + public function __construct(Commandline $outer) { + $this->outer = $outer; + } + + /** + * Sets a single commandline argument. + * + * @param string $value a single commandline argument. + */ + public function setValue($value) { + $this->parts = array($value); + } + + /** + * Line to split into several commandline arguments. + * + * @param line line to split into several commandline arguments + */ + public function setLine($line) { + if ($line === null) { + return; + } + $this->parts = $this->outer->translateCommandline($line); + } + + /** + * Sets a single commandline argument and treats it like a + * PATH - ensures the right separator for the local platform + * is used. + * + * @param value a single commandline argument. + */ + public function setPath($value) { + $this->parts = array( (string) $value ); + } + + /** + * Sets a single commandline argument to the absolute filename + * of the given file. + * + * @param value a single commandline argument. + */ + public function setFile(PhingFile $value) { + $this->parts = array($value->getAbsolutePath()); + } + + /** + * Returns the parts this Argument consists of. + * @return array string[] + */ + public function getParts() { + return $this->parts; + } +} + +/** + * Class to keep track of the position of an Argument. + * + * <p>This class is there to support the srcfile and targetfile + * elements of <execon> and <transform> - don't know + * whether there might be additional use cases.</p> --SB + * + * @package phing.types + */ +class CommandlineMarker { + + private $position; + private $realPos = -1; + private $outer; + + public function __construct(Comandline $outer, $position) { + $this->outer = $outer; + $this->position = $position; + } + + /** + * Return the number of arguments that preceeded this marker. + * + * <p>The name of the executable - if set - is counted as the + * very first argument.</p> + */ + public function getPosition() { + if ($this->realPos == -1) { + $realPos = ($this->outer->executable === null ? 0 : 1); + for ($i = 0; $i < $position; $i++) { + $arg = $this->arguments[$i]; + $realPos += count($arg->getParts()); + } + } + return $this->realPos; + } +} + diff --git a/buildscripts/phing/classes/phing/types/DataType.php b/buildscripts/phing/classes/phing/types/DataType.php new file mode 100644 index 00000000..8aa3b487 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/DataType.php @@ -0,0 +1,182 @@ +<?php +/* + * $Id: 3b15630144c62dda1f447d8eed1e5eda7ac8a466 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/ProjectComponent.php'; +include_once 'phing/BuildException.php'; + +/** + * Base class for those classes that can appear inside the build file + * as stand alone data types. + * + * This class handles the common description attribute and provides + * a default implementation for reference handling and checking for + * circular references that is appropriate for types that can not be + * nested inside elements of the same type (i.e. patternset but not path) + * + * @package phing.types + */ +class DataType extends ProjectComponent { + + /** The descriptin the user has set. */ + public $description = null; + + /** Value to the refid attribute. Type of Reference*/ + public $ref = null; + + /** + * Are we sure we don't hold circular references? + * + * Subclasses are responsible for setting this value to false + * if we'd need to investigate this condition (usually because a + * child element has been added that is a subclass of DataType). + * @var boolean + */ + protected $checked = true; + + /** + * Sets a description of the current data type. It will be useful + * in commenting what we are doing. + */ + function setDescription($desc) { + $this->description = (string) $desc; + } + + /** Return the description for the current data type. */ + function getDescription() { + return $this->description; + } + + /** Has the refid attribute of this element been set? */ + function isReference() { + return ($this->ref !== null); + } + + /** + * Set the value of the refid attribute. + * + * Subclasses may need to check whether any other attributes + * have been set as well or child elements have been created and + * thus override this method. if they do they must call parent::setRefid() + * + * @param Reference $r + * @return void + */ + function setRefid(Reference $r) { + $this->ref = $r; + $this->checked = false; + } + + /** + * Check to see whether any DataType we hold references to is + * included in the Stack (which holds all DataType instances that + * directly or indirectly reference this instance, including this + * instance itself). + * + * If one is included, throw a BuildException created by circularReference + * + * This implementation is appropriate only for a DataType that + * cannot hold other DataTypes as children. + * + * The general contract of this method is that it shouldn't do + * anything if checked is true and set it to true on exit. + */ + function dieOnCircularReference(&$stk, Project $p) { + if ($this->checked || !$this->isReference()) { + return; + } + + $o = $this->ref->getReferencedObject($p); + + if ($o instanceof DataType) { + + // TESTME - make sure that in_array() works just as well here + // + // check if reference is in stack + //$contains = false; + //for ($i=0, $size=count($stk); $i < $size; $i++) { + // if ($stk[$i] === $o) { + // $contains = true; + // break; + // } + //} + + if (in_array($o, $stk, true)) { + // throw build exception + throw $this->circularReference(); + } else { + array_push($stk, $o); + $o->dieOnCircularReference($stk, $p); + array_pop($stk); + } + } + $this->checked = true; + } + + /** Performs the check for circular references and returns the referenced object. */ + function getCheckedRef($requiredClass, $dataTypeName) { + + if (!$this->checked) { + // should be in stack + $stk = array(); + $stk[] = $this; + $this->dieOnCircularReference($stk, $this->getProject()); + } + + $o = $this->ref->getReferencedObject($this->getProject()); + if (!($o instanceof $requiredClass) ) { + throw new BuildException($this->ref->getRefId()." doesn't denote a " . $dataTypeName); + } else { + return $o; + } + } + + /** + * Creates an exception that indicates that refid has to be the + * only attribute if it is set. + */ + function tooManyAttributes() { + return new BuildException( "You must not specify more than one attribute when using refid" ); + } + + /** + * Creates an exception that indicates that this XML element must + * not have child elements if the refid attribute is set. + */ + function noChildrenAllowed() { + return new BuildException("You must not specify nested elements when using refid"); + } + + /** + * Creates an exception that indicates the user has generated a + * loop of data types referencing each other. + */ + function circularReference() { + return new BuildException("This data type contains a circular reference."); + } + + /** + * Template method being called when the data type has been + * parsed completely. + * @return void + */ + function parsingComplete() {} +} + diff --git a/buildscripts/phing/classes/phing/types/Description.php b/buildscripts/phing/classes/phing/types/Description.php new file mode 100644 index 00000000..85b2c529 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Description.php @@ -0,0 +1,53 @@ +<?php + +/* + * $Id: bb303eb742207be6958c8107e21ec7c964c09dee $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Description is used to provide a project-wide description element + * (that is, a description that applies to a buildfile as a whole). + * If present, the <description> element is printed out before the + * target descriptions. + * + * Description has no attributes, only text. There can only be one + * project description per project. A second description element will + * overwrite the first. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Craeg Strong <cstrong@arielpartners.com> (Ant) + * @package phing.types + */ +class Description extends DataType { + + /** + * Adds descriptive text to the project. + * + * @return void + */ + public function addText($text) { + $currentDescription = $this->project->getDescription(); + if ($currentDescription === null) { + $this->project->setDescription($text); + } else { + $this->project->setDescription($currentDescription . $text); + } + } + +} diff --git a/buildscripts/phing/classes/phing/types/DirSet.php b/buildscripts/phing/classes/phing/types/DirSet.php new file mode 100644 index 00000000..527127bb --- /dev/null +++ b/buildscripts/phing/classes/phing/types/DirSet.php @@ -0,0 +1,49 @@ +<?php + +/* + * $Id: 1ab2ec4f1b43daee6cf4c247b4b18b78d87052bc $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/types/AbstractFileSet.php'; + +/** + * Subclass as hint for supporting tasks that the included directories + * instead of files should be used. + * + * @package phing.types + */ +class DirSet extends AbstractFileSet { + + public function __construct($dirset = null) { + parent::__construct($dirset); + } + + /** + * Return a DirSet that has the same basedir and same patternsets + * as this one. + */ + public function __clone() { + if ($this->isReference()) { + return new DirSet($this->getRef($this->getProject())); + } else { + return new DirSet($this); + } + } + +} diff --git a/buildscripts/phing/classes/phing/types/Excludes.php b/buildscripts/phing/classes/phing/types/Excludes.php new file mode 100644 index 00000000..2d45b593 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Excludes.php @@ -0,0 +1,208 @@ +<?php +/* + * $Id: 0c22f261d1984da235c6cf4993d1275c225a33ba $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/types/DataType.php'; +include_once 'phing/types/FileSet.php'; +require_once 'phing/types/ExcludesNameEntry.php'; + +/** + * Datatype which handles excluded files, classes and methods. + * + * @package phing.types + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: 0c22f261d1984da235c6cf4993d1275c225a33ba $ + * @since 2.4.6 + */ +class Excludes extends DataType +{ + /** + * The directory scanner for getting the excluded files + * + * @var DirectoryScanner + */ + private $_directoryScanner = null; + + /** + * Holds the excluded file patterns + * + * @var array + */ + private $_files = array(); + + /** + * Holds the excluded classes + * + * @var array + */ + private $_classes = array(); + + /** + * Holds the excluded methods + * + * @var array + */ + private $_methods = array(); + + /** + * ctor + * + * @param Project $project + */ + public function __construct(Project $project) + { + $this->_directoryScanner = new DirectoryScanner(); + $this->_directoryScanner->setBasedir($project->getBasedir()); + } + + /** + * Add a name entry on the exclude file list + * + * @return ExcludesNameEntry Reference to object + */ + public function createFile() + { + return $this->_addFile($this->_files); + } + + /** + * Add a name entry on the exclude class list + * + * @return ExcludesNameEntry Reference to object + */ + public function createClass() + { + return $this->_addClass($this->_classes); + } + + /** + * Add a name entry on the exclude method list + * + * @return ExcludesNameEntry Reference to object + */ + public function createMethod() + { + return $this->_addMethod($this->_methods); + } + + /** + * Adds a file to the exclusion list + * + * @param FileSet $fileSet The FileSet into which the nameentry should be added + * + * @return ExcludesNameEntry Reference to the created ExcludesNameEntry instance + */ + private function _addFile(&$fileList) + { + $file = new ExcludesNameEntry(); + $fileList[] = $file; + + return $file; + } + + /** + * Adds a class to the exclusion list + * + * @return ExcludesNameEntry Reference to the created ExcludesNameEntry instance + */ + private function _addClass(&$classList) + { + $excludedClass = new ExcludesNameEntry(); + $classList[] = $excludedClass; + + return $excludedClass; + } + + /** + * Adds a method to the exclusion list + * + * @return ExcludesNameEntry Reference to the created ExcludesNameEntry instance + */ + private function _addMethod(&$methodList) + { + $excludedMethod = new ExcludesNameEntry(); + $methodList[] = $excludedMethod; + + return $excludedMethod; + } + + /** + * Returns the excluded files + * + * @return array + */ + public function getExcludedFiles() + { + $includes = array(); + + foreach ($this->_files as $file) { + $includes[] = $file->getName(); + } + + $this->_directoryScanner->setIncludes($includes); + $this->_directoryScanner->scan(); + + $files = $this->_directoryScanner->getIncludedFiles(); + $dir = $this->_directoryScanner->getBasedir(); + $fileList = array(); + + foreach ($files as $file) { + $fileList[] = $dir . DIRECTORY_SEPARATOR . $file; + } + + return $fileList; + } + + /** + * Returns the excluded class names + * + * @return array + */ + public function getExcludedClasses() + { + $excludedClasses = array(); + + foreach ($this->_classes as $excludedClass) { + $excludedClasses[] = $excludedClass->getName(); + } + + return $excludedClasses; + } + + /** + * Returns the excluded method names + * + * @return array + */ + public function getExcludedMethods() + { + $excludedMethods = array(); + + foreach ($this->_methods as $excludedMethod) { + $classAndMethod = explode('::', $excludedMethod->getName()); + $className = $classAndMethod[0]; + $methodName = $classAndMethod[1]; + + $excludedMethods[$className][] = $methodName; + } + + return $excludedMethods; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/types/ExcludesNameEntry.php b/buildscripts/phing/classes/phing/types/ExcludesNameEntry.php new file mode 100644 index 00000000..1aa6aae5 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/ExcludesNameEntry.php @@ -0,0 +1,81 @@ +<?php +/* + * $Id: a1098700b4f205baacfd5adf5434e5a4803a6a3f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Class for holding nested excludes elements (file, class, method). + * + * @package phing.types + * @author Benjamin Schultz <bschultz@proqrent.de> + * @version $Id: a1098700b4f205baacfd5adf5434e5a4803a6a3f $ + * @since 2.4.6 + */ +class ExcludesNameEntry +{ + /** + * Holds the name of a file, class or method or a file pattern + * + * @var string + */ + private $_name; + + /** + * An alias for the setName() method. + * Set the name of a file pattern. + * + * @see setName() + * + * @param string $pattern The file pattern + */ + public function addText($pattern) + { + $this->setName($pattern); + } + + /** + * Set the name of a file, class or method + * + * @param string $name + */ + public function setName($name) + { + $this->_name = (string) $name; + } + + /** + * Get the name of a file, class or method or the file pattern + * + * @return string The name of a file, class or method or the file pattern + */ + public function getName() + { + return $this->_name; + } + + /** + * Gets a string representation of this name or pattern. + * + * @return string + */ + public function toString() + { + return $this->_name; + } +}
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/types/FileList.php b/buildscripts/phing/classes/phing/types/FileList.php new file mode 100755 index 00000000..bc7c6b18 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/FileList.php @@ -0,0 +1,224 @@ +<?php +/* + * $Id: aab0ffb8e2266923f6cdaf165741dc38931a2aed $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/DataType.php'; +include_once 'phing/system/io/PhingFile.php'; + +/** + * FileList represents an explicitly named list of files. FileLists + * are useful when you want to capture a list of files regardless of + * whether they currently exist. + * + * <filelist + * id="docfiles" + * dir="${phing.docs.dir}" + * files="chapters/Installation.html,chapters/Setup.html"/> + * + * OR + * + * <filelist + * dir="${doc.src.dir}" + * listfile="${phing.docs.dir}/PhingGuide.book"/> + * + * (or a mixture of files="" and listfile="" can be used) + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Id$ + * @package phing.types + */ +class FileList extends DataType { + + // public for "cloning" purposes + + /** Array containing all filenames. */ + public $filenames = array(); + + /** Base directory for this file list. */ + public $dir; + + /** PhingFile that contains a list of files (one per line). */ + public $listfile; + + /** + * Construct a new FileList. + * @param array $filelist; + */ + function __construct($filelist = null) { + if ($filelist !== null) { + $this->dir = $filelist->dir; + $this->filenames = $filelist->filenames; + $this->listfile = $filelist->listfile; + } + } + + /** + * Makes this instance in effect a reference to another FileList + * instance. + */ + function setRefid(Reference $r) { + if ($this->dir !== null || count($this->filenames) !== 0) { + throw $this->tooManyAttributes(); + } + parent::setRefid($r); + } + + /** + * Base directory for files in list. + * @param PhingFile $dir + */ + function setDir(PhingFile $dir) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if (!($dir instanceof PhingFile)) { + $dir = new PhingFile($dir); + } + $this->dir = $dir; + } + + /** + * Get the basedir for files in list. + * @return PhingFile + */ + function getDir(Project $p) { + if ($this->isReference()) { + $ref = $this->getRef($p); + return $ref->getDir($p); + } + return $this->dir; + } + + /** + * Set the array of files in list. + * @param array $filenames + */ + function setFiles($filenames) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if (!empty($filenames)) { + $tok = strtok($filenames, ", \t\n\r"); + while ($tok !== false) { + $fname = trim($tok); + if ($fname !== "") { + $this->filenames[] = $tok; + } + $tok = strtok(", \t\n\r"); + } + } + } + + /** + * Sets a source "list" file that contains filenames to add -- one per line. + * @param string $file + */ + function setListFile($file) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if (!($file instanceof PhingFile)) { + $file = new PhingFile($file); + } + $this->listfile = $file; + } + + /** + * Get the source "list" file that contains file names. + * @param Project $p + * @return PhingFile + */ + function getListFile(Project $p) { + if ($this->isReference()) { + $ref = $this->getRef($p); + return $ref->getListFile($p); + } + return $this->listfile; + } + + /** + * Returns the list of files represented by this FileList. + * @param Project $p + * @return array + */ + function getFiles(Project $p) { + + if ($this->isReference()) { + $ret = $this->getRef($p); + $ret = $ret->getFiles($p); + return $ret; + } + + if ($this->listfile !== null) { + $this->readListFile($p); + } + + return $this->filenames; + } + + + /** + * Performs the check for circular references and returns the + * referenced FileSet. + * @param Project $p + */ + function getRef(Project $p) { + if (!$this->checked) { + $stk = array(); + array_push($stk, $this); + $this->dieOnCircularReference($stk, $p); + } + + $o = $this->ref->getReferencedObject($p); + if (!($o instanceof FileList)) { + throw new BuildException($this->ref->getRefId()." doesn't denote a filelist"); + } else { + return $o; + } + } + + /** + * Reads file names from a file and adds them to the files array. + * @param Project $p + */ + private function readListFile(Project $p) { + $listReader = null; + try { + // Get a FileReader + $listReader = new BufferedReader(new FileReader($this->listfile)); + + $line = $listReader->readLine(); + while ($line !== null) { + if (!empty($line)) { + $line = $p->replaceProperties($line); + $this->filenames[] = trim($line); + } + $line = $listReader->readLine(); + } + } catch (Exception $e) { + if ($listReader) $listReader->close(); + throw new BuildException("An error occured while reading from list file " . $this->listfile->__toString() . ": " . $e->getMessage()); + } + + $listReader->close(); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/FileSet.php b/buildscripts/phing/classes/phing/types/FileSet.php new file mode 100644 index 00000000..229e4419 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/FileSet.php @@ -0,0 +1,56 @@ +<?php + +/* + * $Id: 1fbeb217059bb26788059dd46affde1428d5a52e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/AbstractFileSet.php'; + +/** + * Moved out of MatchingTask to make it a standalone object that could + * be referenced (by scripts for example). + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Arnout J. Kuiper <ajkuiper@wxs.nl> (Ant) + * @author Stefano Mazzocchi <stefano@apache.org> (Ant) + * @author Sam Ruby <rubys@us.ibm.com> (Ant) + * @author Jon S. Stevens <jon@clearink.com> (Ant) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @author Magesh Umasankar (Ant) + * @package phing.types + */ +class FileSet extends AbstractFileSet { + + function __construct($fileset = null) { + parent::__construct($fileset); + } + + /** + * Return a FileSet that has the same basedir and same patternsets + * as this one. + */ + public function __clone() { + if ($this->isReference()) { + return new FileSet($this->getRef($this->getProject())); + } else { + return new FileSet($this); + } + } + +} diff --git a/buildscripts/phing/classes/phing/types/FilterChain.php b/buildscripts/phing/classes/phing/types/FilterChain.php new file mode 100755 index 00000000..e6575c33 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/FilterChain.php @@ -0,0 +1,191 @@ +<?php +/* + * $Id: ec2362b430c0a863a77968e9003b09c4b9a78d7e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/types/DataType.php'; +include_once 'phing/filters/HeadFilter.php'; +include_once 'phing/filters/IconvFilter.php'; +include_once 'phing/filters/TailFilter.php'; +include_once 'phing/filters/LineContains.php'; +include_once 'phing/filters/LineContainsRegexp.php'; +include_once 'phing/filters/ExpandProperties.php'; +include_once 'phing/filters/PrefixLines.php'; +include_once 'phing/filters/ReplaceRegexp.php'; +include_once 'phing/filters/ReplaceTokens.php'; +include_once 'phing/filters/ReplaceTokensWithFile.php'; +include_once 'phing/filters/StripPhpComments.php'; +include_once 'phing/filters/StripLineBreaks.php'; +include_once 'phing/filters/StripLineComments.php'; +include_once 'phing/filters/StripWhitespace.php'; +include_once 'phing/filters/TabToSpaces.php'; +include_once 'phing/filters/TidyFilter.php'; +include_once 'phing/filters/TranslateGettext.php'; +include_once 'phing/filters/XincludeFilter.php'; +include_once 'phing/filters/XsltFilter.php'; + +/** + * FilterChain may contain a chained set of filter readers. + * + * @author Yannick Lecaillez <yl@seasonfive.com> + * @version $Id$ + * @package phing.types + */ +class FilterChain extends DataType { + + private $filterReaders = array(); + + function __construct($project = null) { + if ($project) + { + $this->project = $project; + } + } + + function getFilterReaders() { + return $this->filterReaders; + } + + function addExpandProperties(ExpandProperties $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addGettext(TranslateGettext $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addHeadFilter(HeadFilter $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addIconvFilter(IconvFilter $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addTailFilter(TailFilter $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addLineContains(LineContains $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addLineContainsRegExp(LineContainsRegExp $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addPrefixLines(PrefixLines $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addReplaceTokens(ReplaceTokens $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addReplaceTokensWithFile(ReplaceTokensWithFile $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addReplaceRegexp(ReplaceRegexp $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addStripPhpComments(StripPhpComments $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addStripLineBreaks(StripLineBreaks $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addStripLineComments(StripLineComments $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addStripWhitespace(StripWhitespace $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addTidyFilter(TidyFilter $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addTabToSpaces(TabToSpaces $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addXincludeFilter(XincludeFilter $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addXsltFilter(XsltFilter $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + function addFilterReader(PhingFilterReader $o) { + $o->setProject($this->project); + $this->filterReaders[] = $o; + } + + /* + * Makes this instance in effect a reference to another FilterChain + * instance. + * + * <p>You must not set another attribute or nest elements inside + * this element if you make it a reference.</p> + * + * @param $r the reference to which this instance is associated + * @throws BuildException if this instance already has been configured. + */ + function setRefid(Reference $r) { + + if ( count($this->filterReaders) !== 0 ) { + throw $this->tooManyAttributes(); + } + + // change this to get the objects from the other reference + $o = $r->getReferencedObject($this->getProject()); + if ( $o instanceof FilterChain ) { + $this->filterReaders = $o->getFilterReaders(); + } else { + throw new BuildException($r->getRefId()." doesn't refer to a FilterChain"); + } + parent::setRefid($r); + } + +} diff --git a/buildscripts/phing/classes/phing/types/IterableFileSet.php b/buildscripts/phing/classes/phing/types/IterableFileSet.php new file mode 100644 index 00000000..bc800bbe --- /dev/null +++ b/buildscripts/phing/classes/phing/types/IterableFileSet.php @@ -0,0 +1,56 @@ +<?php +/* + * $Id: a607a54aa4e434c01a1f36600274fb31041549e2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * FileSet adapter to SPL's Iterator. + * + * @package phing.types + * @author Alexey Shockov <alexey@shockov.com> + * @since 2.4.0 + * @internal + */ +class IterableFileSet + extends FileSet + implements IteratorAggregate +{ + /** + * @return Iterator + */ + public function getIterator() + { + return new ArrayIterator($this->getFiles()); + } + /** + * @return array + */ + private function getFiles() + { + $directoryScanner = $this->getDirectoryScanner($this->getProject()); + $files = $directoryScanner->getIncludedFiles(); + + $baseDirectory = $directoryScanner->getBasedir(); + foreach ($files as $index => $file) { + $files[$index] = realpath($baseDirectory.'/'.$file); + } + + return $files; + } +} diff --git a/buildscripts/phing/classes/phing/types/Mapper.php b/buildscripts/phing/classes/phing/types/Mapper.php new file mode 100644 index 00000000..03042c64 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Mapper.php @@ -0,0 +1,207 @@ +<?php +/* + * $Id: bab760877500c0bb1ed4d6879c7e31c60c3fd307 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/types/DataType.php'; +include_once 'phing/types/Path.php'; + +/** + * Filename Mapper maps source file name(s) to target file name(s). + * + * Built-in mappers can be accessed by specifying they "type" attribute: + * <code> + * <mapper type="glob" from="*.php" to="*.php.bak"/> + * </code> + * Custom mappers can be specified by providing a dot-path to a include_path-relative + * class: + * <code> + * <mapper classname="myapp.mappers.DevToProdMapper" from="*.php" to="*.php"/> + * <!-- maps all PHP files from development server to production server, for example --> + * </code> + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.types + */ +class Mapper extends DataType { + + protected $type; + protected $classname; + protected $from; + protected $to; + protected $classpath; + protected $classpathId; + + + function __construct(Project $project) { + $this->project = $project; + } + + /** + * Set the classpath to be used when searching for component being defined + * + * @param Path $classpath An Path object containing the classpath. + */ + public function setClasspath(Path $classpath) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($this->classpath === null) { + $this->classpath = $classpath; + } else { + $this->classpath->append($classpath); + } + } + + /** + * Create the classpath to be used when searching for component being defined + */ + public function createClasspath() { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($this->classpath === null) { + $this->classpath = new Path($this->project); + } + return $this->classpath->createPath(); + } + + /** + * Reference to a classpath to use when loading the files. + */ + public function setClasspathRef(Reference $r) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->classpathId = $r->getRefId(); + $this->createClasspath()->setRefid($r); + } + + /** Set the type of FileNameMapper to use. */ + function setType($type) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->type = $type; + } + + /** Set the class name of the FileNameMapper to use. */ + function setClassname($classname) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->classname = $classname; + } + + /** + * Set the argument to FileNameMapper.setFrom + */ + function setFrom($from) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->from = $from; + } + + /** + * Set the argument to FileNameMapper.setTo + */ + function setTo($to) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->to = $to; + } + + /** + * Make this Mapper instance a reference to another Mapper. + * + * You must not set any other attribute if you make it a reference. + */ + function setRefid(Reference $r) { + if ($this->type !== null || $this->from !== null || $this->to !== null) { + throw DataType::tooManyAttributes(); + } + parent::setRefid($r); + } + + /** Factory, returns inmplementation of file name mapper as new instance */ + function getImplementation() { + if ($this->isReference()) { + $tmp = $this->getRef(); + return $tmp->getImplementation(); + } + + if ($this->type === null && $this->classname === null) { + throw new BuildException("either type or classname attribute must be set for <mapper>"); + } + + if ($this->type !== null) { + switch($this->type) { + case 'identity': + $this->classname = 'phing.mappers.IdentityMapper'; + break; + case 'flatten': + $this->classname = 'phing.mappers.FlattenMapper'; + break; + case 'glob': + $this->classname = 'phing.mappers.GlobMapper'; + break; + case 'regexp': + case 'regex': + $this->classname = 'phing.mappers.RegexpMapper'; + break; + case 'merge': + $this->classname = 'phing.mappers.MergeMapper'; + break; + default: + throw new BuildException("Mapper type {$this->type} not known"); + break; + } + } + + // get the implementing class + $cls = Phing::import($this->classname, $this->classpath); + + $m = new $cls; + $m->setFrom($this->from); + $m->setTo($this->to); + + return $m; + } + + /** Performs the check for circular references and returns the referenced Mapper. */ + private function getRef() { + if (!$this->checked) { + $stk = array(); + $stk[] = $this; + $this->dieOnCircularReference($stk, $this->project); + } + + $o = $this->ref->getReferencedObject($this->project); + if (!($o instanceof Mapper)) { + $msg = $this->ref->getRefId()." doesn't denote a mapper"; + throw new BuildException($msg); + } else { + return $o; + } + } +} + + diff --git a/buildscripts/phing/classes/phing/types/Parameter.php b/buildscripts/phing/classes/phing/types/Parameter.php new file mode 100644 index 00000000..cbba153c --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Parameter.php @@ -0,0 +1,99 @@ +<?php +/* + * $Id: 725ec7b7d585219471bb8793da0210c7ab1452c0 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/types/DataType.php'; + +/** + * A parameter is composed of a name, type and value. Nested + * Parameters are also possible, but the using task/type has + * to support them + * + * @author Manuel Holtgrewe + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @package phing.types + */ +class Parameter extends DataType { + + /** Parameter name */ + protected $name; + + /** Paramter type */ + protected $type; + + /** Parameter value */ + protected $value; + + /** Nested parameters */ + protected $parameters = array(); + + function setName($name) { + $this->name = (string) $name; + } + + function setType($type) { + $this->type = (string) $type; + } + + /** + * Sets value to dynamic register slot. + * @param RegisterSlot $value + */ + public function setListeningValue(RegisterSlot $value) { + $this->value = $value; + } + + function setValue($value) { + $this->value = (string) $value; + } + + function getName() { + return $this->name; + } + + function getType() { + return $this->type; + } + + function getValue() { + if ($this->value instanceof RegisterSlot) { + return $this->value->getValue(); + } else { + return $this->value; + } + } + + /** + * @return Parameter + */ + function createParam() { + $num = array_push($this->parameters, new Parameter()); + return $this->parameters[$num-1]; + } + + /** + * @return array Nested parameters. + */ + function getParams() { + return $this->parameters; + } +} + + diff --git a/buildscripts/phing/classes/phing/types/Parameterizable.php b/buildscripts/phing/classes/phing/types/Parameterizable.php new file mode 100644 index 00000000..7efe790a --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Parameterizable.php @@ -0,0 +1,32 @@ +<?php + +/* + * $Id: e0d41388dad173e2dd61527524d6c3958683171b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Parameterizable objects take genric key value pairs. + * + * @author Hans Lellelid, hans@xmpl.org (Phing) + * @author Magesh Umasankar (Ant) + * @package phing.types + */ +interface Parameterizable { + function setParameters($parameters); +} diff --git a/buildscripts/phing/classes/phing/types/Path.php b/buildscripts/phing/classes/phing/types/Path.php new file mode 100644 index 00000000..b8525628 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Path.php @@ -0,0 +1,452 @@ +<?php +/* + * $Id: 762715ec83a12704f4ab528e507c28396c159083 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/DataType.php'; +include_once 'phing/util/PathTokenizer.php'; +include_once 'phing/types/FileSet.php'; + +/** + * This object represents a path as used by include_path or PATH + * environment variable. + * + * This class has been adopted from the Java Ant equivalent. The ability have + * path structures in Phing is important; however, because of how PHP classes interact + * the ability to specify CLASSPATHs makes less sense than Java.Rather than providing + * CLASSPATH for any tasks that take classes as parameters, perhaps a better + * solution in PHP is to have an IncludePath task, which prepends paths to PHP's include_path + * INI variable. This gets around the problem that simply using a path to load the initial + * PHP class is not enough (in most cases the loaded class may assume that it is on the global + * PHP include_path, and will try to load dependent classes accordingly). The other option is + * to provide a way for this class to add paths to the include path, if desired -- or to create + * an IncludePath subclass. Once added, though, when would a path be removed from the include path? + * + * <p> + * <code> + * <sometask><br> + * <somepath><br> + * <pathelement location="/path/to/file" /><br> + * <pathelement path="/path/to/class2;/path/to/class3" /><br> + * <pathelement location="/path/to/file3" /><br> + * </somepath><br> + * </sometask><br> + * </code> + * <p> + * The object implemention <code>sometask</code> must provide a method called + * <code>createSomepath</code> which returns an instance of <code>Path</code>. + * Nested path definitions are handled by the Path object and must be labeled + * <code>pathelement</code>.<p> + * + * The path element takes a parameter <code>path</code> which will be parsed + * and split into single elements. It will usually be used + * to define a path from an environment variable. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Thomas.Haas@softwired-inc.com (Ant) + * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant) + * @package phing.types + */ +class Path extends DataType { + + private $elements = array(); + + /** + * Constructor for internally instantiated objects sets project. + * @param Project $project + * @param string $path (for use by IntrospectionHelper) + */ + public function __construct($project = null, $path = null) { + if ($project !== null) { + $this->setProject($project); + } + if ($path !== null) { + $this->createPathElement()->setPath($path); + } + } + + /** + * Adds a element definition to the path. + * @param $location the location of the element to add (must not be + * <code>null</code> nor empty. + * @throws BuildException + */ + public function setDir(PhingFile $location) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->createPathElement()->setDir($location); + } + + /** + * Parses a path definition and creates single PathElements. + * @param path the path definition. + * @throws BuildException + */ + public function setPath($path) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + $this->createPathElement()->setPath($path); + } + + /** + * Makes this instance in effect a reference to another Path instance. + * + * <p>You must not set another attribute or nest elements inside + * this element if you make it a reference.</p> + * @throws BuildException + */ + public function setRefid(Reference $r) { + if (!empty($this->elements)) { + throw $this->tooManyAttributes(); + } + $this->elements[] = $r; + parent::setRefid($r); + } + + /** + * Creates the nested <code><pathelement></code> element. + * @throws BuildException + */ + public function createPathElement() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + $pe = new PathElement($this); + $this->elements[] = $pe; + return $pe; + } + + /** + * Adds a nested <code><fileset></code> element. + * @throws BuildException + */ + public function addFileset(FileSet $fs) { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + $this->elements[] = $fs; + $this->checked = false; + } + + /** + * Adds a nested <code><dirset></code> element. + * @throws BuildException + */ + public function addDirset(DirSet $dset) { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + $this->elements[] = $dset; + $this->checked = false; + } + + /** + * Creates a nested <code><path></code> element. + * @throws BuildException + */ + public function createPath() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + $p = new Path($this->project); + $this->elements[] = $p; + $this->checked = false; + return $p; + } + + /** + * Append the contents of the other Path instance to this. + */ + public function append(Path $other) { + if ($other === null) { + return; + } + $l = $other->listPaths(); + foreach($l as $path) { + if (!in_array($path, $this->elements, true)) { + $this->elements[] = $path; + } + } + } + + /** + * Adds the components on the given path which exist to this + * Path. Components that don't exist, aren't added. + * + * @param Path $source - Source path whose components are examined for existence. + */ + public function addExisting(Path $source) { + $list = $source->listPaths(); + foreach($list as $el) { + $f = null; + if ($this->project !== null) { + $f = $this->project->resolveFile($el); + } else { + $f = new PhingFile($el); + } + + if ($f->exists()) { + $this->setDir($f); + } else { + $this->log("dropping " . $f->__toString() . " from path as it doesn't exist", + Project::MSG_VERBOSE); + } + } + } + + /** + * Returns all path elements defined by this and nested path objects. + * @return array List of path elements. + */ + public function listPaths() { + if (!$this->checked) { + // make sure we don't have a circular reference here + $stk = array(); + array_push($stk, $this); + $this->dieOnCircularReference($stk, $this->project); + } + + $result = array(); + for ($i = 0, $elSize=count($this->elements); $i < $elSize; $i++) { + $o = $this->elements[$i]; + if ($o instanceof Reference) { + $o = $o->getReferencedObject($this->project); + // we only support references to paths right now + if (!($o instanceof Path)) { + $msg = $r->getRefId() . " doesn't denote a path"; + throw new BuildException($msg); + } + } + + if (is_string($o)) { + $result[] = $o; + } elseif ($o instanceof PathElement) { + $parts = $o->getParts(); + if ($parts === null) { + throw new BuildException("You must either set location or" + . " path on <pathelement>"); + } + foreach($parts as $part) { + $result[] = $part; + } + } elseif ($o instanceof Path) { + $p = $o; + if ($p->getProject() === null) { + $p->setProject($this->getProject()); + } + $parts = $p->listPaths(); + foreach($parts as $part) { + $result[] = $part; + } + } elseif ($o instanceof DirSet) { + $dset = $o; + $ds = $dset->getDirectoryScanner($this->project); + $dirstrs = $ds->getIncludedDirectories(); + $dir = $dset->getDir($this->project); + foreach($dirstrs as $dstr) { + $d = new PhingFile($dir, $dstr); + $result[] = $d->getAbsolutePath(); + } + } elseif ($o instanceof FileList) { + $fl = $o; + $dirstrs = $fl->getFiles($this->project); + $dir = $fl->getDir($this->project); + foreach($dirstrs as $dstr) { + $d = new PhingFile($dir, $dstr); + $result[] = $d->getAbsolutePath(); + } + } + } + + return array_unique($result); + } + + + /** + * Returns a textual representation of the path, which can be used as + * CLASSPATH or PATH environment variable definition. + * @return string A textual representation of the path. + */ + public function __toString() { + + $list = $this->listPaths(); + + // empty path return empty string + if (empty($list)) { + return ""; + } + + return implode(PATH_SEPARATOR, $list); + } + + /** + * Splits a PATH (with : or ; as separators) into its parts. + * @param Project $project + * @param string $source + */ + public static function translatePath(Project $project, $source) { + $result = array(); + if ($source == null) { + return ""; + } + + $tok = new PathTokenizer($source); + while ($tok->hasMoreTokens()) { + $pathElement = $tok->nextToken(); + try { + $element = self::resolveFile($project, $pathElement); + for ($i = 0, $_i=strlen($element); $i < $_i; $i++) { + self::translateFileSep($element, $i); + } + $result[] = $element; + } catch (BuildException $e) { + $this->project->log("Dropping path element " . $pathElement + . " as it is not valid relative to the project", + Project::MSG_VERBOSE); + } + } + + return $result; + } + + /** + * Returns its argument with all file separator characters + * replaced so that they match the local OS conventions. + */ + public static function translateFile($source) { + if ($source == null) { + return ""; + } + + $result = $source; + for ($i = 0, $_i=strlen($source); $i < $_i; $i++) { + self::translateFileSep($result, $i); + } + + return $result; + } + + /** + * Translates all occurrences of / or \ to correct separator of the + * current platform and returns whether it had to do any + * replacements. + */ + protected static function translateFileSep(&$buffer, $pos) { + if ($buffer{$pos} == '/' || $buffer{$pos} == '\\') { + $buffer{$pos} = DIRECTORY_SEPARATOR; + return true; + } + return false; + } + + /** + * How many parts does this Path instance consist of. + * DEV NOTE: expensive call! list is generated, counted, and then + * discareded. + * @return int + */ + public function size() { + return count($this->listPaths()); + } + + /** + * Return a Path that holds the same elements as this instance. + */ + public function __clone() { + $p = new Path($this->project); + $p->append($this); + return $p; + } + + /** + * Overrides the version of DataType to recurse on all DataType + * child elements that may have been added. + * @throws BuildException + */ + public function dieOnCircularReference(&$stk, Project $p) { + + if ($this->checked) { + return; + } + + // elements can contain strings, FileSets, Reference, etc. + foreach($this->elements as $o) { + + if ($o instanceof Reference) { + $o = $o->getReferencedObject($p); + } + + if ($o instanceof DataType) { + if (in_array($o, $stk, true)) { + throw $this->circularReference(); + } else { + array_push($stk, $o); + $o->dieOnCircularReference($stk, $p); + array_pop($stk); + } + } + } + + $this->checked = true; + } + + /** + * Resolve a filename with Project's help - if we know one that is. + * + * <p>Assume the filename is absolute if project is null.</p> + */ + private static function resolveFile(Project $project, $relativeName) { + if ($project !== null) { + $f = $project->resolveFile($relativeName); + return $f->getAbsolutePath(); + } + return $relativeName; + } + +} + + +/** + * Helper class, holds the nested <code><pathelement></code> values. + * + * @package phing.types + */ +class PathElement { + + private $parts = array(); + private $outer; + + public function __construct(Path $outer) { + $this->outer = $outer; + } + + public function setDir(PhingFile $loc) { + $this->parts = array(Path::translateFile($loc->getAbsolutePath())); + } + + public function setPath($path) { + $this->parts = Path::translatePath($this->outer->getProject(), $path); + } + + public function getParts() { + return $this->parts; + } +} diff --git a/buildscripts/phing/classes/phing/types/PatternSet.php b/buildscripts/phing/classes/phing/types/PatternSet.php new file mode 100755 index 00000000..32967d62 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/PatternSet.php @@ -0,0 +1,493 @@ +<?php +/* + * $Id: af3bd3709d79ad0299bf6ec0130a8a244ee325ab $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/io/FileReader.php'; +include_once 'phing/types/DataType.php'; + +/** + * The patternset storage component. Carries all necessary data and methods + * for the patternset stuff. + * + * @author Andreas Aderhold, andi@binarycloud.com + * @version $Id: af3bd3709d79ad0299bf6ec0130a8a244ee325ab $ + * @package phing.types + */ +class PatternSet extends DataType { + + private $includeList = array(); + private $excludeList = array(); + private $includesFileList = array(); + private $excludesFileList = array(); + + /** + * Makes this instance in effect a reference to another PatternSet + * instance. + * You must not set another attribute or nest elements inside + * this element if you make it a reference. + */ + function setRefid(Reference $r) { + if (!empty($this->includeList) || !empty($this->excludeList)) { + throw $this->tooManyAttributes(); + } + parent::setRefid($r); + } + + + /** + * Add a name entry on the include list + * + * @return PatternSetNameEntry Reference to object + * @throws BuildException + */ + function createInclude() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + return $this->addPatternToList($this->includeList); + } + + + /** + * Add a name entry on the include files list + * + * @return PatternSetNameEntry Reference to object + * @throws BuildException + */ + function createIncludesFile() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + return $this->addPatternToList($this->includesFileList); + } + + /** + * Add a name entry on the exclude list + * + * @return PatternSetNameEntry Reference to object + * @throws BuildException + */ + function createExclude() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + return $this->addPatternToList($this->excludeList); + } + + /** + * add a name entry on the exclude files list + * + * @return PatternSetNameEntry Reference to object + * @throws BuildException + */ + function createExcludesFile() { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + return; + } + return $this->addPatternToList($this->excludesFileList); + } + + + /** + * Sets the set of include patterns. Patterns may be separated by a comma + * or a space. + * + * @param string the string containing the include patterns + * @return void + * @throws BuildException + */ + function setIncludes($includes) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($includes !== null && strlen($includes) > 0) { + $tok = strtok($includes, ", "); + while ($tok !== false) { + $o = $this->createInclude(); + $o->setName($tok); + $tok = strtok(", "); + } + } + } + + + /** + * Sets the set of exclude patterns. Patterns may be separated by a comma + * or a space. + * + * @param string the string containing the exclude patterns + * @return void + * @throws BuildException + */ + function setExcludes($excludes) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($excludes !== null && strlen($excludes) > 0) { + $tok = strtok($excludes, ", "); + while ($tok !== false) { + $o = $this->createExclude(); + $o->setName($tok); + $tok = strtok(", "); + } + } + } + + /** + * add a name entry to the given list + * + * @param array List onto which the nameentry should be added + * @return PatternSetNameEntry Reference to the created PsetNameEntry instance + */ + private function addPatternToList(&$list) { + $num = array_push($list, new PatternSetNameEntry()); + return $list[$num-1]; + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param includesFile The file to fetch the include patterns from. + */ + function setIncludesFile($includesFile) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($includesFile instanceof File) { + $includesFile = $includesFile->getPath(); + } + $o = $this->createIncludesFile(); + $o->setName($includesFile); + } + + /** + * Sets the name of the file containing the excludes patterns. + * + * @param excludesFile The file to fetch the exclude patterns from. + */ + function setExcludesFile($excludesFile) { + if ($this->isReference()) { + throw $this->tooManyAttributes(); + } + if ($excludesFile instanceof File) { + $excludesFile = $excludesFile->getPath(); + } + $o = $this->createExcludesFile(); + $o->setName($excludesFile); + } + + + /** + * Reads path matching patterns from a file and adds them to the + * includes or excludes list + */ + private function readPatterns(PhingFile $patternfile, &$patternlist, Project $p) { + $patternReader = null; + try { + // Get a FileReader + $patternReader = new BufferedReader(new FileReader($patternfile)); + + // Create one NameEntry in the appropriate pattern list for each + // line in the file. + $line = $patternReader->readLine(); + while ($line !== null) { + if (!empty($line)) { + $line = $p->replaceProperties($line); + $this->addPatternToList($patternlist)->setName($line); + } + $line = $patternReader->readLine(); + } + + } catch (IOException $ioe) { + $msg = "An error occured while reading from pattern file: " . $patternfile->__toString(); + if($patternReader) $patternReader->close(); + throw new BuildException($msg, $ioe); + } + + $patternReader->close(); + } + + + /** Adds the patterns of the other instance to this set. */ + function append($other, $p) { + if ($this->isReference()) { + throw new BuildException("Cannot append to a reference"); + } + + $incl = $other->getIncludePatterns($p); + if ($incl !== null) { + foreach($incl as $incl_name) { + $o = $this->createInclude(); + $o->setName($incl_name); + } + } + + $excl = $other->getExcludePatterns($p); + if ($excl !== null) { + foreach($excl as $excl_name) { + $o = $this->createExclude(); + $o->setName($excl_name); + } + } + } + + /** Returns the filtered include patterns. */ + function getIncludePatterns(Project $p) { + if ($this->isReference()) { + $o = $this->getRef($p); + return $o->getIncludePatterns($p); + } else { + $this->readFiles($p); + return $this->makeArray($this->includeList, $p); + } + } + + /** Returns the filtered exclude patterns. */ + function getExcludePatterns(Project $p) { + if ($this->isReference()) { + $o = $this->getRef($p); + return $o->getExcludePatterns($p); + } else { + $this->readFiles($p); + return $this->makeArray($this->excludeList, $p); + } + } + + /** helper for FileSet. */ + function hasPatterns() { + return (boolean) count($this->includesFileList) > 0 || count($this->excludesFileList) > 0 + || count($this->includeList) > 0 || count($this->excludeList) > 0; + } + + /** + * Performs the check for circular references and returns the + * referenced PatternSet. + */ + function getRef(Project $p) { + if (!$this->checked) { + $stk = array(); + array_push($stk, $this); + $this->dieOnCircularReference($stk, $p); + } + $o = $this->ref->getReferencedObject($p); + if (!($o instanceof PatternSet)) { + $msg = $this->ref->getRefId()." doesn't denote a patternset"; + throw new BuildException($msg); + } else { + return $o; + } + } + + /** Convert a array of PatternSetNameEntry elements into an array of Strings. */ + private function makeArray(&$list, Project $p) { + + if (count($list) === 0) { + return null; + } + + $tmpNames = array(); + foreach($list as $ne) { + $pattern = (string) $ne->evalName($p); + if ($pattern !== null && strlen($pattern) > 0) { + array_push($tmpNames, $pattern); + } + } + return $tmpNames; + } + + /** Read includesfile or excludesfile if not already done so. */ + private function readFiles(Project $p) { + if (!empty($this->includesFileList)) { + foreach($this->includesFileList as $ne) { + $fileName = (string) $ne->evalName($p); + if ($fileName !== null) { + $inclFile = $p->resolveFile($fileName); + if (!$inclFile->exists()) { + throw new BuildException("Includesfile ".$inclFile->getAbsolutePath()." not found."); + } + $this->readPatterns($inclFile, $this->includeList, $p); + } + } + $this->includesFileList = array(); + } + + if (!empty($this->excludesFileList)) { + foreach($this->excludesFileList as $ne) { + $fileName = (string) $ne->evalName($p); + if ($fileName !== null) { + $exclFile = $p->resolveFile($fileName); + if (!$exclFile->exists()) { + throw new BuildException("Excludesfile ".$exclFile->getAbsolutePath()." not found."); + return; + } + $this->readPatterns($exclFile, $this->excludeList, $p); + } + } + $this->excludesFileList = array(); + } + } + + + function toString() { + + // We can't compile includeList into array because, toString() does + // not know about project: + // + // $includes = $this->makeArray($this->includeList, $this->project); + // $excludes = $this->makeArray($this->excludeList, $this->project); + + if (empty($this->includeList)) { + $includes = "empty"; + } else { + $includes = ""; + foreach($this->includeList as $ne) { + $includes .= $ne->toString() . ","; + } + $includes = rtrim($includes, ","); + } + + if (empty($this->excludeList)) { + $excludes = "empty"; + } else { + $excludes = ""; + foreach($this->excludeList as $ne) { + $excludes .= $ne->toString() . ","; + } + $excludes = rtrim($excludes, ","); + } + + return "patternSet{ includes: $includes excludes: $excludes }"; + } +} + + +/** + * "Internal" class for holding an include/exclude pattern. + * + * @package phing.types + */ +class PatternSetNameEntry { + + /** + * The pattern. + * @var string + */ + private $name; + + /** + * The if-condition property for this pattern to be applied. + * @var string + */ + private $ifCond; + + /** + * The unless-condition property for this pattern to be applied. + * @var string + */ + private $unlessCond; + + /** + * An alias for the setName() method. + * @see setName() + * @param string $pattern + */ + public function setPattern($pattern) { + $this->setName($pattern); + } + + /** + * Set the pattern text. + * @param string $name The pattern + */ + public function setName($name) { + $this->name = (string) $name; + } + + /** + * Sets an if-condition property for this pattern to match. + * @param string $cond + */ + public function setIf($cond) { + $this->ifCond = (string) $cond; + } + + + /** + * Sets an unless-condition property for this pattern to match. + * @param string $cond + */ + public function setUnless($cond) { + $this->unlessCond = (string) $cond; + } + + /** + * Get the pattern text. + * @return string The pattern. + */ + public function getName() { + return $this->name; + } + + /** + * Evaluates the pattern. + * @return string The pattern or null if it is ruled out by a condition. + */ + public function evalName(Project $project) { + return $this->valid($project) ? $this->name : null; + } + + + /** + * Checks whether pattern should be applied based on whether the if and unless + * properties are set in project. + * @param Project $project + * @return boolean + */ + public function valid(Project $project) { + if ($this->ifCond !== null && $project->getProperty($this->ifCond) === null) { + return false; + } else if ($this->unlessCond !== null && $project->getProperty($this->unlessCond) !== null) { + return false; + } + return true; + } + + /** + * Gets a string representation of this pattern. + * @return string + */ + public function toString() { + $buf = $this->name; + if (($this->ifCond !== null) || ($this->unlessCond !== null)) { + $buf .= ":"; + $connector = ""; + + if ($this->ifCond !== null) { + $buf .= "if->{$this->ifCond}"; + $connector = ";"; + } + if ($this->unlessCond !== null) { + $buf .= "$connector unless->{$this->unlessCond}"; + } + } + return $buf; + } +} diff --git a/buildscripts/phing/classes/phing/types/PearPackageFileSet.php b/buildscripts/phing/classes/phing/types/PearPackageFileSet.php new file mode 100644 index 00000000..8cf99b28 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/PearPackageFileSet.php @@ -0,0 +1,179 @@ +<?php +/** + * Part of phing, the PHP build tool + * + * PHP version 5 + * + * @category Types + * @package phing.types + * @author Christian Weiske <cweiske@cweiske.de> + * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html + * @version SVN: $Id: 5ba010b83645d0ea709761a3d8260fc013239458 $ + * @link http://www.phing.info/ + */ +require_once 'phing/types/FileSet.php'; +require_once 'PEAR/Config.php'; +require_once 'phing/util/PearPackageScanner.php'; + +/** + * Fileset that contains files of an installed PEAR package. + * It can be used to package up PEAR package dependencies in own + * release files (zip, tgz, phar). + * + * @internal + * A normal fileset is used that way in CopyTask, rSTTask: + * <code> + * $ds = $fs->getDirectoryScanner($project); + * $fromDir = $fs->getDir($project); + * $srcFiles = $ds->getIncludedFiles(); + * $srcDirs = $ds->getIncludedDirectories(); + * </code> + * The scanner is used as follows: + * <code> + * $ds->getBaseDir() + * $ds->scan() + * </code> + * + * @category Types + * @package phing.types + * @author Christian Weiske <cweiske@cweiske.de> + * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html + * @link http://www.phing.info/ + */ +class PearPackageFileSet extends FileSet +{ + /** + * Name of channel the package is from, e.g. "pear.php.net". + * + * @var string + */ + protected $channel; + + /** + * Package name to get files from, e.g. "Console_CommandLine" + * + * @var string + */ + protected $package; + + /** + * Use files of that role only. + * Multiple roles are not supported, and you always have to specify one. + * + * @var string + */ + protected $role = 'php'; + + /** + * Prefix to prepend to the file paths in the zip + */ + protected $prefix; + + /** + * Full path to a PEAR config file. + * If none provided, default one is used. + */ + protected $config; + + /** + * @var PearPackageScanner instance + */ + protected $pps; + + + /** + * Creates and returns the pear package scanner. + * Scanner already has scan() called. + * + * @param Project $project Current phing project + * + * @return PearPackageScanner + */ + public function getDirectoryScanner(Project $project) + { + if ($this->isReference()) { + $obj = $this->getRef($project); + return $obj->getDirectoryScanner($project); + } + + $this->loadPearPackageScanner(); + return $this->pps; + } + + /** + * Returns the base directory all package files are relative to + * + * @return PhingFile Base directory + */ + public function getDir() + { + if ($this->pps === null) { + $this->loadPearPackageScanner(); + } + return new PhingFile((string) $this->pps->getBaseDir()); + } + + /** + * Loads the package scanner instance into $this->pps + * + * @return void + */ + protected function loadPearPackageScanner() + { + $this->pps = new PearPackageScanner(); + $this->pps->setPackage($this->package); + $this->pps->setChannel($this->channel); + $this->pps->setRole($this->role); + $this->pps->setConfig($this->config); + $this->pps->scan(); + } + + /** + * Sets the package name. + * If no channel is given, "pear.php.net" is used. + * + * @param string $package Single package name, or "channel/name" combination + * + * @return void + */ + public function setPackage($package) + { + $parts = explode('/', $package); + if (count($parts) > 2) { + throw new BuildException('Invalid package name: ' . $package); + } + + if (count($parts) == 1) { + $this->channel = 'pear.php.net'; + $this->package = $parts[0]; + } else { + $this->channel = $parts[0]; + $this->package = $parts[1]; + } + } + + /** + * Sets the role of files that should be included. + * Examples are php,doc,script + * + * @param string $role PEAR file role + * + * @return void + */ + public function setRole($role) + { + $this->role = $role; + } + + /** + * Sets the full path to the PEAR configuration file + * + * @param string $config Configuration file + * + * @return void + */ + public function setConfig($config) + { + $this->config = $config; + } +} diff --git a/buildscripts/phing/classes/phing/types/PhingFilterReader.php b/buildscripts/phing/classes/phing/types/PhingFilterReader.php new file mode 100755 index 00000000..2337977a --- /dev/null +++ b/buildscripts/phing/classes/phing/types/PhingFilterReader.php @@ -0,0 +1,136 @@ +<?php +/* + * $Id: fee06c9abce6700d773004bd83a5312d94c5aa29 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/types/DataType.php'; +include_once 'phing/types/Parameter.php'; + +/** + * A PhingFilterReader is a wrapper class that encloses the className + * and configuration of a Configurable FilterReader. + * + * @author Yannick Lecaillez <yl@seasonfive.com> + * @version $Id$ + * @see FilterReader + * @package phing.types + */ +class PhingFilterReader extends DataType { + + private $className; + private $parameters = array(); + private $classPath; + + function setClassName($className) { + $this->className = $className; + } + + function getClassName() { + return $this->className; + } + + /** + * Set the classpath to load the FilterReader through (attribute). + * @param Path $classpath + */ + function setClasspath(Path $classpath) { + if ( $this->isReference() ) { + throw $this->tooManyAttributes(); + } + if ( $this->classPath === null ) { + $this->classPath = $classpath; + } else { + $this->classPath->append($classpath); + } + } + + /* + * Set the classpath to load the FilterReader through (nested element). + */ + function createClasspath() { + if ( $this->isReference() ) { + throw $this->noChildrenAllowed(); + } + if ( $this->classPath === null ) { + $this->classPath = new Path($this->project); + } + return $this->classPath->createPath(); + } + + function getClasspath() { + return $this->classPath; + } + + function setClasspathRef(Reference $r) { + if ( $this->isReference() ) { + throw $this->tooManyAttributes(); + } + $o = $this->createClasspath(); + $o->setRefid($r); + } + + function addParam(Parameter $param) { + $this->parameters[] = $param; + } + + function createParam() { + $num = array_push($this->parameters, new Parameter()); + return $this->parameters[$num-1]; + } + + function getParams() { + // We return a COPY + $ret = array(); + for($i=0,$size=count($this->parameters); $i < $size; $i++) { + $ret[] = clone $this->parameters[$i]; + } + return $ret; + } + + /* + * Makes this instance in effect a reference to another PhingFilterReader + * instance. + * + * <p>You must not set another attribute or nest elements inside + * this element if you make it a reference.</p> + * + * @param Reference $r the reference to which this instance is associated + * @exception BuildException if this instance already has been configured. + */ + function setRefid(Reference $r) { + if ( (count($this->parameters) !== 0) || ($this->className !== null) ) { + throw $this->tooManyAttributes(); + } + $o = $r->getReferencedObject($this->getProject()); + if ( $o instanceof PhingFilterReader ) { + $this->setClassName($o->getClassName()); + $this->setClasspath($o->getClassPath()); + foreach($o->getParams() as $p) { + $this->addParam($p); + } + } else { + $msg = $r->getRefId()." doesn\'t refer to a PhingFilterReader"; + throw new BuildException($msg); + } + + parent::setRefid($r); + } +} + + diff --git a/buildscripts/phing/classes/phing/types/Reference.php b/buildscripts/phing/classes/phing/types/Reference.php new file mode 100644 index 00000000..5f490b3f --- /dev/null +++ b/buildscripts/phing/classes/phing/types/Reference.php @@ -0,0 +1,56 @@ +<?php +/* + * $Id: 48c142f41717a31654d5cbfdb37f339e84adf391 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** Class to hold a reference to another object in the project. + * @package phing.types + */ +class Reference { + + protected $refid; + + function __construct($id = null) { + if ($id !== null) { + $this->setRefId($id); + } + } + + function setRefId($id) { + $this->refid = (string) $id; + } + + function getRefId() { + return $this->refid; + } + + /** returns reference to object in references container of project */ + function getReferencedObject($project) { + if ($this->refid === null) { + throw new BuildException("No reference specified"); + } + $refs = $project->getReferences(); + $o = @$refs[$this->refid]; + if (!is_object($o)) { + throw new BuildException("Reference {$this->refid} not found."); + } + return $o; + } +} + diff --git a/buildscripts/phing/classes/phing/types/RegularExpression.php b/buildscripts/phing/classes/phing/types/RegularExpression.php new file mode 100755 index 00000000..84a25591 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/RegularExpression.php @@ -0,0 +1,128 @@ +<?php +/* + * $Id: 257bd788b6185a3561f10a8de40502473076b6dd $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +include_once 'phing/types/DataType.php'; +include_once 'phing/Project.php'; +include_once 'phing/util/regexp/Regexp.php'; + +/** + * A regular expression datatype. Keeps an instance of the + * compiled expression for speed purposes. This compiled + * expression is lazily evaluated (it is compiled the first + * time it is needed). The syntax is the dependent on which + * regular expression type you are using. + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @version $Id$ + * @access public + * @see phing.util.regex.RegexMatcher + * @package phing.types +*/ +class RegularExpression extends DataType { + + private $regexp = null; + /** + * @todo Probably both $ignoreCase and $multiline should be removed + * from attribute list of RegularExpression class: + * actual values are preserved on regexp *engine* level, not expression + * object itself. + */ + private $ignoreCase = false; + private $multiline = false; + + function __construct() { + $this->regexp = new Regexp(); + } + + function setPattern($pattern) { + $this->regexp->setPattern($pattern); + } + + function setReplace($replace) { + $this->regexp->setReplace($replace); + } + + function getPattern($p) { + if ( $this->isReference() ) { + $ref = $this->getRef($p); + return $ref->getPattern($p); + } + return $this->regexp->getPattern(); + } + + function getReplace($p) { + if ( $this->isReference() ) { + $ref = $this->getRef($p); + return $ref->getReplace($p); + } + + return $this->regexp->getReplace(); + } + + function setModifiers($modifiers) { + $this->regexp->setModifiers($modifiers); + } + + function getModifiers() { + return $this->regexp->getModifiers(); + } + + function setIgnoreCase($bit) { + $this->regexp->setIgnoreCase($bit); + } + + function getIgnoreCase() { + return $this->regexp->getIgnoreCase(); + } + + function setMultiline($multiline) { + $this->regexp->setMultiline($multiline); + } + + function getMultiline() { + return $this->regexp->getMultiline(); + } + + function getRegexp(Project $p) { + if ( $this->isReference() ) { + $ref = $this->getRef($p); + return $ref->getRegexp($p); + } + return $this->regexp; + } + + function getRef(Project $p) { + if ( !$this->checked ) { + $stk = array(); + array_push($stk, $this); + $this->dieOnCircularReference($stk, $p); + } + + $o = $this->ref->getReferencedObject($p); + if ( !($o instanceof RegularExpression) ) { + throw new BuildException($this->ref->getRefId()." doesn't denote a RegularExpression"); + } else { + return $o; + } + } +} + + diff --git a/buildscripts/phing/classes/phing/types/TokenReader.php b/buildscripts/phing/classes/phing/types/TokenReader.php new file mode 100755 index 00000000..98ed3083 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/TokenReader.php @@ -0,0 +1,66 @@ +<?php +/* + * $Id: c267e1935c57a372e5b8fc07eef73bd084195e82 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +// include_once 'phing/system/io/Reader.php'; // really this is unrelated to Reader +include_once 'phing/system/io/IOException.php'; +include_once 'phing/filters/ReplaceTokens.php'; // For class Token + +/** + * Abstract class for TokenReaders. + * + * @author Manuel Holtgewe + * @version $Id$ + * @package phing.filters.util + */ +abstract class TokenReader { + + /** + * Reference to the Project the TokenReader is used in. + * @var Project + */ + protected $project; + + /** + * Constructor + * @param object Reference to the project the TokenReader is used in. + */ + function __construct(Project $project) { + $this->project = $project; + } + + /** + * Utility function for logging + */ + function log($level, $msg) { + $this->project->log($level, $msg); + } + + /** + * Reads the next token from the Reader + * + * @throws IOException - On error + * @return string + */ + abstract public function readToken(); + +} + + diff --git a/buildscripts/phing/classes/phing/types/TokenSource.php b/buildscripts/phing/classes/phing/types/TokenSource.php new file mode 100644 index 00000000..0ca0367a --- /dev/null +++ b/buildscripts/phing/classes/phing/types/TokenSource.php @@ -0,0 +1,157 @@ +<?php +/* + * $Id: 1bc91e925ce194c2e7a615e8f8c950d2057a9cb5 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/types/DataType.php'; +include_once 'phing/util/StringHelper.php'; + +/** + * A parameter is composed of a name, type and value. + * + * Example of usage: + * + * <replacetokens> + * <tokensource classname="phing.filters.util.IniFileTokenReader"> + * <!-- all params for the TokenReader here --> + * <param name="file" value="tokens.ini" /> + * </tokensource> + * </replacetokens> + * + * or: + * + * <filterreader classname="phing.filters.ReplaceTokens"> + * <param type="tokensource> + * <param name="classname" value="phing.filters.util.IniFileTokenReader" /> + * <param name="file" value="tokens.ini" /> + * </param> + * </filterreader> + * + * @author <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a> + * @package phing.types + */ +class TokenSource extends DataType { + + /** + * String to hold the path to the TokenReader + * @var string + */ + protected $classname = null; + + /** + * Array holding parameters for the wrapped TokenReader. + * @var array + */ + protected $parameters = array(); + + /** + * Reference to the TokenReader used by this TokenSource + * @var TokenReader + */ + protected $reader; + + /** + * Array with key/value pairs of tokens + */ + protected $tokens = array(); + + /** + * This method is called to load the sources from the reader + * into the buffer of the source. + */ + function load() { + // Create new Reader + if ($this->classname === null) { + throw new BuildException("No Classname given to TokenSource."); + } + + $classname = Phing::import($this->classname); + $this->reader = new $classname($this->project); + + // Configure Reader + $this->configureTokenReader($this->reader); + + // Load Tokens + try { + while ($token = $this->reader->readToken()) { + $this->tokens[] = $token; + } + } catch (BuildException $e) { + $this->log("Error reading TokenSource: " . $e->getMessage(), Project::MSG_WARN); + } catch (IOException $e) { + $this->log("Error reading TokenSource: " . $e->getMessage(), Project::MSG_WARN); + } + } + + /** + * This function uses the wrapper to read the tokens and then + * returns them. + * + * @access public + */ + function getTokens() { + if ($this->tokens === null) + $this->Load(); + + return $this->tokens; + } + + /** + * Configures a TokenReader with the parameters passed to the + * TokenSource. + * @param TokenReader $reader + */ + private function configureTokenReader(TokenReader $reader) { + $count = count($this->parameters); + for ($i = 0; $i < $count; $i++) { + $method_name = "Set" . $this->parameters[$i]->getName(); + $value = $this->parameters[$i]->getValue(); + $reader->$method_name($value); + } + } + + /** + * Set the classname (dot-path) to use for handling token replacement. + * @param string $c + */ + function setClassname($c) { + $this->classname = $c; + } + + /** + * Returns the qualified classname (dot-path) to use for handling token replacement. + * @return string + */ + function getClassname() { + return $this->classname; + } + + /** + * Create nested <param> tag. + * Uses standard name/value Parameter class. + * @return Parameter + */ + function createParam() { + $num = array_push($this->parameters, new Parameter()); + return $this->parameters[$num-1]; + } +} + + + diff --git a/buildscripts/phing/classes/phing/types/defaults.properties b/buildscripts/phing/classes/phing/types/defaults.properties new file mode 100644 index 00000000..a2d86350 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/defaults.properties @@ -0,0 +1,13 @@ +# phing default types +commandline=phing.types.Commandline +fileset=phing.types.FileSet +dirset=phing.types.DirSet +filelist=phing.types.FileList +patternset=phing.types.PatternSet +mapper=phing.types.Mapper +filterchain=phing.types.FilterChain +filterreader=phing.types.PhingFilterReader +regexp=phing.types.RegularExpression +param=phing.types.Parameter +path=phing.types.Path +selector=phing.types.selectors.SelectSelector
\ No newline at end of file diff --git a/buildscripts/phing/classes/phing/types/selectors/AndSelector.php b/buildscripts/phing/classes/phing/types/selectors/AndSelector.php new file mode 100644 index 00000000..1a27829d --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/AndSelector.php @@ -0,0 +1,67 @@ +<?php +/* + * $Id: f36556afb9487cce2e56d5c174e3ce4c43ef968f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseSelectorContainer.php'; + +/** + * This selector has a collection of other selectors, all of which have to + * select a file in order for this selector to select it. + * + * @author Hans Lellelid, hans@xmpl.org (Phing) + * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> (Ant) + * @package phing.types.selectors + */ +class AndSelector extends BaseSelectorContainer { + + public function toString() { + $buf = ""; + if ($this->hasSelectors()) { + $buf .= "{andselect: "; + $buf .= parent::toString(); + $buf .= "}"; + } + return $buf; + } + + /** + * Returns true (the file is selected) only if all other selectors + * agree that the file should be selected. + * + * @param basedir the base directory the scan is being done from + * @param filename the name of the file to check + * @param file a PhingFile object for the filename that the selector + * can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + $this->validate(); + $selectors = $this->selectorElements(); + for($i=0,$size=count($selectors); $i < $size; $i++) { + $result = $selectors[$i]->isSelected($basedir, $filename, $file); + if (!$result) { + return false; + } + } + return true; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/BaseExtendSelector.php b/buildscripts/phing/classes/phing/types/selectors/BaseExtendSelector.php new file mode 100644 index 00000000..d8ae0444 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/BaseExtendSelector.php @@ -0,0 +1,62 @@ +<?php + +/* + * $Id: 0c36c2b00f8ab8d20025b9ad38043c762b6fc7f9 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/ExtendFileSelector.php'; +require_once 'phing/types/selectors/BaseSelector.php'; +include_once 'phing/types/Parameter.php'; + +/** + * Convenience base class for all selectors accessed through ExtendSelector. + * It provides support for gathering the parameters together as well as for + * assigning an error message and throwing a build exception if an error is + * detected. + * + * @author Hans Lellelid, hans@xmpl.org (Phing) + * @author Bruce Atherton, bruce@callenish.com (Ant) + * @package phing.types.selectors + */ +abstract class BaseExtendSelector extends BaseSelector implements ExtendFileSelector { + + /** The passed in parameter array. */ + protected $parameters = null; + + /** + * Set all the Parameters for this custom selector, collected by + * the ExtendSelector class. + * + * @param parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + $this->parameters = $parameters; + } + + /** + * Allows access to the parameters gathered and set within the + * <custom> tag. + * + * @return the set of parameters defined for this selector + */ + protected function getParameters() { + return $this->parameters; + } +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/BaseSelector.php b/buildscripts/phing/classes/phing/types/selectors/BaseSelector.php new file mode 100644 index 00000000..c463fa33 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/BaseSelector.php @@ -0,0 +1,84 @@ +<?php +/* + * $Id: e1f8e20eb87ea465d29ba3add6fada790642bcf8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/FileSelector.php'; + +/** + * A convenience base class that you can subclass Selectors from. It + * provides some helpful common behaviour. Note that there is no need + * for Selectors to inherit from this class, it is only necessary that + * they implement FileSelector. + * + * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> + * @package phing.types.selectors + */ +abstract class BaseSelector extends DataType implements FileSelector { + + private $errmsg = null; + + /** + * Allows all selectors to indicate a setup error. Note that only + * the first error message is recorded. + * + * @param msg The error message any BuildException should throw. + */ + public function setError($msg) { + if ($this->errmsg === null) { + $this->errmsg = $msg; + } + } + + /** + * Returns any error messages that have been set. + * + * @return the error condition + */ + public function getError() { + return $this->errmsg; + } + + + /** + * <p>Subclasses can override this method to provide checking of their + * state. So long as they call validate() from isSelected(), this will + * be called automatically (unless they override validate()).</p> + * <p>Implementations should check for incorrect settings and call + * setError() as necessary.</p> + */ + public function verifySettings() { + } + + /** + * Subclasses can use this to throw the requisite exception + * in isSelected() in the case of an error condition. + */ + public function validate() { + if ($this->getError() === null) { + $this->verifySettings(); + } + if ($this->getError() !== null) { + throw new BuildException($this->errmsg); + } + } + +} + + diff --git a/buildscripts/phing/classes/phing/types/selectors/BaseSelectorContainer.php b/buildscripts/phing/classes/phing/types/selectors/BaseSelectorContainer.php new file mode 100644 index 00000000..3b2a10b1 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/BaseSelectorContainer.php @@ -0,0 +1,270 @@ +<?php + +/* + * $Id: 9dd90d3e78d751562859bbe5179db148ee5b025c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/SelectorContainer.php'; +require_once 'phing/types/selectors/BaseSelector.php'; + +/** + * This is the base class for selectors that can contain other selectors. + * + * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> (Ant) + * @package phing.types.selectors + */ +abstract class BaseSelectorContainer extends BaseSelector implements SelectorContainer { + + private $selectorsList = array(); + + /** + * Indicates whether there are any selectors here. + */ + public function hasSelectors() { + return !(empty($this->selectorsList)); + } + + /** + * Gives the count of the number of selectors in this container + */ + public function selectorCount() { + return count($this->selectorsList); + } + + /** + * Returns a copy of the selectors as an array. + */ + public function getSelectors(Project $p) { + $result = array(); + for($i=0,$size=count($this->selectorsList); $i < $size; $i++) { + $result[] = clone $this->selectorsList[$i]; + } + return $result; + } + + /** + * Returns an array for accessing the set of selectors (not a copy). + */ + public function selectorElements() { + return $this->selectorsList; + } + + /** + * Convert the Selectors within this container to a string. This will + * just be a helper class for the subclasses that put their own name + * around the contents listed here. + * + * @return comma separated list of Selectors contained in this one + */ + public function toString() { + $buf = ""; + $arr = $this->selectorElements(); + for($i=0,$size=count($arr); $i < $size; $i++) { + $buf .= $arr[$i]->toString() . (isset($arr[$i+1]) ? ', ' : ''); + } + return $buf; + } + + /** + * Add a new selector into this container. + * + * @param selector the new selector to add + * @return the selector that was added + */ + public function appendSelector(FileSelector $selector) { + $this->selectorsList[] = $selector; + } + + /** + * <p>This implementation validates the container by calling + * verifySettings() and then validates each contained selector + * provided that the selector implements the validate interface. + * </p> + * <p>Ordinarily, this will validate all the elements of a selector + * container even if the isSelected() method of some elements is + * never called. This has two effects:</p> + * <ul> + * <li>Validation will often occur twice. + * <li>Since it is not required that selectors derive from + * BaseSelector, there could be selectors in the container whose + * error conditions are not detected if their isSelected() call + * is never made. + * </ul> + */ + public function validate() { + $this->verifySettings(); + $errmsg = $this->getError(); + if ($errmsg !== null) { + throw new BuildException($errmsg); + } + foreach($this->selectorsList as $o) { + if ($o instanceof BaseSelector) { + $o->validate(); + } + } + } + + /* Methods below all add specific selectors */ + + /** + * add a "Select" selector entry on the selector list + */ + public function createSelector() { + $o = new SelectSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add an "And" selector entry on the selector list + */ + public function createAnd() { + $o = new AndSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add an "Or" selector entry on the selector list + */ + public function createOr() { + $o = new OrSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a "Not" selector entry on the selector list + */ + public function createNot() { + $o = new NotSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a "None" selector entry on the selector list + */ + public function createNone() { + $o = new NoneSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a majority selector entry on the selector list + */ + public function createMajority() { + $o = new MajoritySelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a selector date entry on the selector list + */ + public function createDate() { + $o = new DateSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a selector size entry on the selector list + */ + public function createSize() { + $o = new SizeSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a selector filename entry on the selector list + */ + public function createFilename() { + $o = new FilenameSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add an extended selector entry on the selector list + */ + public function createCustom() { + $o = new ExtendSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a contains selector entry on the selector list + */ + public function createContains() { + $o = new ContainsSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a contains selector entry on the selector list + */ + public function createContainsRegexp() { + $o = new ContainsRegexpSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a present selector entry on the selector list + */ + public function createPresent() { + $o = new PresentSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a depth selector entry on the selector list + */ + public function createDepth() { + $o = new DepthSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a depends selector entry on the selector list + */ + public function createDepend() { + $o = new DependSelector(); + $this->appendSelector($o); + return $o; + } + + /** + * add a type selector entry on the selector list + */ + public function createType() { + $o = new TypeSelector(); + $this->appendSelector($o); + return $o; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/ContainsRegexpSelector.php b/buildscripts/phing/classes/phing/types/selectors/ContainsRegexpSelector.php new file mode 100755 index 00000000..5b45bac8 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/ContainsRegexpSelector.php @@ -0,0 +1,164 @@ +<?php + +/* + * $Id: 2a891d7cc3fb1b710b72d51e6a8cf2d0b553f91a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseExtendSelector.php'; +include_once 'phing/types/RegularExpression.php'; + +/** + * Selector that filters files based on whether they contain a + * particular string using regexp. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @version $Id$ + * @package phing.types.selectors + */ +class ContainsRegexpSelector extends BaseExtendSelector { + + /** @var string The expression set from XML. */ + private $userProvidedExpression; + + /** @var Regexp */ + private $myExpression; + + private $casesensitive = true; + + /** @var RegularExpression */ + private $myRegExp; + + const EXPRESSION_KEY = "expression"; + + const CASE_KEY = "casesensitive"; + + public function toString() { + $buf = "{containsregexpselector expression: "; + $buf .= $this->userProvidedExpression; + $buf .= " casesensitive: "; + if ($this->casesensitive) { + $buf .= "true"; + } else { + $buf .= "false"; + } + $buf .= "}"; + return $buf; + } + + /** + * The expression to match on within a file. + * + * @param string $exp the string that a file must contain to be selected. + */ + public function setExpression($exp) { + $this->userProvidedExpression = $exp; + } + + /** + * Whether to ignore case in the regex match. + * + * @param boolean $casesensitive whether to pay attention to case sensitivity + */ + public function setCasesensitive($casesensitive) { + $this->casesensitive = $casesensitive; + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param array $parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i=0,$size=count($parameters); $i < $size; $i++) { + $paramname = $parameters[$i]->getName(); + switch(strtolower($paramname)) { + case self::EXPRESSION_KEY: + $this->setExpression($parameters[$i]->getValue()); + break; + case self::CASE_KEY: + $this->setCasesensitive($parameters[$i]->getValue()); + break; + default: + $this->setError("Invalid parameter " . $paramname); + } + } // for each param + } // if params + } + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the pattern attribute has been set. + * + */ + public function verifySettings() { + if ($this->userProvidedExpression === null) { + $this->setError("The expression attribute is required"); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file a PhingFile object the selector can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + if ($file->isDirectory()) { + return true; + } + + if ($this->myRegExp === null) { + $this->myRegExp = new RegularExpression(); + $this->myRegExp->setPattern($this->userProvidedExpression); + if (!$this->casesensitive) { + $this->myRegExp->setIgnoreCase(true); + } + $this->myExpression = $this->myRegExp->getRegexp($this->getProject()); + } + + $in = null; + try { + $in = new BufferedReader(new FileReader($file)); + $teststr = $in->readLine(); + while ($teststr !== null) { + if ($this->myExpression->matches($teststr)) { + return true; + } + $teststr = $in->readLine(); + } + return false; + } catch (IOException $ioe) { + if ($in) $in->close(); + throw new BuildException("Could not read file " . $filename); + } + $in->close(); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/ContainsSelector.php b/buildscripts/phing/classes/phing/types/selectors/ContainsSelector.php new file mode 100644 index 00000000..5e270583 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/ContainsSelector.php @@ -0,0 +1,151 @@ +<?php + +/* + * $Id: ab2c641c048573b0a9976b7cb92138b8ceda511f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/types/selectors/BaseExtendSelector.php'; + +/** + * Selector that filters files based on whether they contain a + * particular string. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class ContainsSelector extends BaseExtendSelector { + + private $contains = null; + private $casesensitive = true; + const CONTAINS_KEY = "text"; + const CASE_KEY = "casesensitive"; + + public function toString() { + $buf = "{containsselector text: "; + $buf .= $this->contains; + $buf .= " casesensitive: "; + if ($this->casesensitive) { + $buf .= "true"; + } else { + $buf .= "false"; + } + $buf .= "}"; + return $buf; + } + + /** + * The string to search for within a file. + * + * @param string $contains the string that a file must contain to be selected. + */ + public function setText($contains) { + $this->contains = $contains; + } + + /** + * Whether to ignore case in the string being searched. + * + * @param boolean $casesensitive whether to pay attention to case sensitivity + */ + public function setCasesensitive($casesensitive) { + $this->casesensitive = $casesensitive; + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param array $parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i=0,$size=count($parameters); $i < $size; $i++) { + $paramname = $parameters[$i]->getName(); + switch(strtolower($paramname)) { + case self::CONTAINS_KEY: + $this->setText($parameters[$i]->getValue()); + break; + case self::CASE_KEY: + $this->setCasesensitive($parameters[$i]->getValue()); + break; + default: + $this->setError("Invalid parameter " . $paramname); + } + } // for each param + } // if params + } + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the pattern attribute has been set. + * + */ + public function verifySettings() { + if ($this->contains === null) { + $this->setError("The text attribute is required"); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file a PhingFile object the selector can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + if ($file->isDirectory()) { + return true; + } + + $userstr = $this->contains; + if (!$this->casesensitive) { + $userstr = strtolower($this->contains); + } + + $in = null; + try { + $in = new BufferedReader(new FileReader($file)); + $teststr = $in->readLine(); + while ($teststr !== null) { + if (!$this->casesensitive) { + $teststr = strtolower($teststr); + } + if (strpos($teststr, $userstr) !== false) { + return true; + } + $teststr = $in->readLine(); + } + return false; + } catch (IOException $ioe) { + if ($in) $in->close(); + throw new BuildException("Could not read file " . $filename); + } + $in->close(); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/DateSelector.php b/buildscripts/phing/classes/phing/types/selectors/DateSelector.php new file mode 100755 index 00000000..0f8c28a8 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/DateSelector.php @@ -0,0 +1,214 @@ +<?php + +/* + * $Id: f05cee91082616c66b2e109157b1d2f2298a66f8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseExtendSelector.php'; + +/** + * Selector that chooses files based on their last modified date. Ant uses + * millisecond precision (thanks to Java); PHP is forced to use only seconds + * precision. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @version $Id: f05cee91082616c66b2e109157b1d2f2298a66f8 $ + * @package phing.types.selectors + */ +class DateSelector extends BaseExtendSelector { + + private $seconds = -1; // millis in Ant, but PHP doesn't support that level of precision + private $dateTime = null; + private $includeDirs = false; + private $granularity = 0; + private $cmp = 2; + const MILLIS_KEY = "millis"; + const DATETIME_KEY = "datetime"; + const CHECKDIRS_KEY = "checkdirs"; + const GRANULARITY_KEY = "granularity"; + const WHEN_KEY = "when"; + private static $timeComparisons = array("before", "after", "equal"); + + public function __construct() { + //if (Os.isFamily("dos")) { + // granularity = 2000; + //} + } + + public function toString() { + $buf = "{dateselector date: "; + $buf .= $this->dateTime; + $buf .= " compare: "; + if ($this->cmp === 0) { + $buf .= "before"; + } elseif ($this->cmp === 1) { + $buf .= "after"; + } else { + $buf .= "equal"; + } + $buf .= " granularity: "; + $buf .= $this->granularity; + $buf .= "}"; + return $buf; + } + + /** + * For users that prefer to express time in seconds since 1970 + * + * @param int $seconds the time to compare file's last modified date to, + * expressed in milliseconds + */ + public function setSeconds($seconds) { + $this->seconds = (int) $seconds; + } + + /** + * Returns the seconds value the selector is set for. + */ + public function getSeconds() { + return $this->seconds; + } + + /** + * Sets the date. The user must supply it in MM/DD/YYYY HH:MM AM_PM + * format + * + * @param string $dateTime a string in MM/DD/YYYY HH:MM AM_PM format + */ + public function setDatetime($dateTime) { + $dt = strtotime($dateTime); + if ($dt == -1) { + $this->setError("Date of " . $dateTime + . " Cannot be parsed correctly. It should be in" + . " a format parsable by PHP's strtotime() function."); + } else { + $this->dateTime = $dateTime; + $this->setSeconds($dt); + } + } + + /** + * Should we be checking dates on directories? + * + * @param boolean $includeDirs whether to check the timestamp on directories + */ + public function setCheckdirs($includeDirs) { + $this->includeDirs = (boolean) $includeDirs; + } + + /** + * Sets the number of milliseconds leeway we will give before we consider + * a file not to have matched a date. + * @param int $granularity + */ + public function setGranularity($granularity) { + $this->granularity = (int) $granularity; + } + + /** + * Sets the type of comparison to be done on the file's last modified + * date. + * + * @param string $cmp The comparison to perform + */ + public function setWhen($cmp) { + $idx = array_search($cmp, self::$timeComparisons, true); + if ($idx === null) { + $this->setError("Invalid value for ".WHEN_KEY.": ".$cmp); + } else { + $this->cmp = $idx; + } + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param array $parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i=0,$size=count($parameters); $i < $size; $i++) { + $paramname = $parameters[$i]->getName(); + switch(strtolower($paramname)) { + case self::MILLIS_KEY: + $this->setMillis($parameters[$i]->getValue()); + break; + case self::DATETIME_KEY: + $this->setDatetime($parameters[$i]->getValue()); + break; + case self::CHECKDIRS_KEY: + $this->setCheckdirs($parameters[$i]->getValue()); + break; + case self::GRANULARITY_KEY: + $this->setGranularity($parameters[$i]->getValue()); + break; + case self::WHEN_KEY: + $this->setWhen($parameters[$i]->getValue()); + break; + default: + $this->setError("Invalid parameter " . $paramname); + } // switch + } + } + } + + /** + * This is a consistency check to ensure the selector's required + * values have been set. + */ + public function verifySettings() { + if ($this->dateTime === null && $this->seconds < 0) { + $this->setError("You must provide a datetime or the number of " + . "seconds."); + } elseif ($this->seconds < 0) { + $this->setError("Date of " . $this->dateTime + . " results in negative seconds" + . " value relative to epoch (January 1, 1970, 00:00:00 GMT)."); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param PhingFile $basedir the base directory the scan is being done from + * @param string $filename is the name of the file to check + * @param PhingFile $file is a PhingFile object the selector can use + * @return boolean Whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + $this->validate(); + if ($file->isDirectory() && ($this->includeDirs === false)) { + return true; + } + if ($this->cmp === 0) { + return (($file->lastModified() - $this->granularity) < $this->seconds); + } elseif ($this->cmp === 1) { + return (($file->lastModified() - $this->granularity) > $this->seconds); + } else { + return (abs($file->lastModified() - $this->seconds) <= $this->granularity); + } + } + +} + + diff --git a/buildscripts/phing/classes/phing/types/selectors/DependSelector.php b/buildscripts/phing/classes/phing/types/selectors/DependSelector.php new file mode 100755 index 00000000..3b869199 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/DependSelector.php @@ -0,0 +1,151 @@ +<?php + +/* + * $Id: eac9e808c89c2a8f414f86afc589eda4f218dd6e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseSelector.php'; + +/** + * Selector that filters files based on whether they are newer than + * a matching file in another directory tree. It can contain a mapper + * element, so isn't available as an ExtendSelector (since those + * parameters can't hold other elements). + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @version $Id$ + * @package phing.types.selectors + */ +class DependSelector extends BaseSelector { + + private $targetdir = null; + private $mapperElement = null; + private $map = null; + private $granularity = 0; + + public function __construct() { + // not yet supported: + //if (Os.isFamily("dos")) { + // $this->granularity = 2000; + //} + } + + public function toString() { + $buf = "{dependselector targetdir: "; + if ($this->targetdir === null) { + $buf .= "NOT YET SET"; + } else { + $buf .= $this->targetdir->getName(); + } + $buf .= " granularity: "; + $buf .= $this->granularity; + if ($this->map !== null) { + $buf .= " mapper: "; + $buf .= $this->map->toString(); + } elseif ($this->mapperElement !== null) { + $buf .= " mapper: "; + $buf .= $this->mapperElement->toString(); + } + $buf .= "}"; + return $buf; + } + + /** + * The name of the file or directory which is checked for out-of-date + * files. + * + * @param targetdir the directory to scan looking for files. + */ + public function setTargetdir(PhingFile $targetdir) { + $this->targetdir = $targetdir; + } + + /** + * Sets the number of milliseconds leeway we will give before we consider + * a file out of date. + */ + public function setGranularity($granularity) { + $this->granularity = (int) granularity; + } + + /** + * Defines the FileNameMapper to use (nested mapper element). + * @throws BuildException + */ + public function createMapper() { + if ($this->mapperElement !== null) { + throw new BuildException("Cannot define more than one mapper"); + } + $this->mapperElement = new Mapper($this->project); + return $this->mapperElement; + } + + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the dest attribute has been set and we have a mapper. + */ + public function verifySettings() { + if ($this->targetdir === null) { + $this->setError("The targetdir attribute is required."); + } + if ($this->mapperElement === null) { + $this->map = new IdentityMapper(); + } else { + $this->map = $this->mapperElement->getImplementation(); + } + if ($this->map === null) { + $this->setError("Could not set <mapper> element."); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file is a PhingFile object the selector can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + // Determine file whose out-of-dateness is to be checked + $destfiles = $this->map->main($filename); + + // If filename does not match the To attribute of the mapper + // then filter it out of the files we are considering + if ($destfiles === null) { + return false; + } + // Sanity check + if (count($destfiles) !== 1 || $destfiles[0] === null) { + throw new BuildException("Invalid destination file results for " . $this->targetdir . " with filename " . $filename); + } + $destname = $destfiles[0]; + $destfile = new PhingFile($this->targetdir, $destname); + + return SelectorUtils::isOutOfDate($file, $destfile, $this->granularity); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/DepthSelector.php b/buildscripts/phing/classes/phing/types/selectors/DepthSelector.php new file mode 100755 index 00000000..0ad6c8eb --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/DepthSelector.php @@ -0,0 +1,158 @@ +<?php +/* + * $Id: 9c244177a7f95995cd30c67c9fee47c1e977e4e2 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseExtendSelector.php'; + +/** + * Selector that filters files based on the how deep in the directory + * tree they are. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @version $Id$ + * @package phing.types.selectors + */ +class DepthSelector extends BaseExtendSelector { + + public $min = -1; + public $max = -1; + const MIN_KEY = "min"; + const MAX_KEY = "max"; + + public function toString() { + $buf = "{depthselector min: "; + $buf .= $this->min; + $buf .= " max: "; + $buf .= $this->max; + $buf .= "}"; + return $buf; + } + + /** + * The minimum depth below the basedir before a file is selected. + * + * @param min minimum directory levels below basedir to go + */ + public function setMin($min) { + $this->min = (int) $min; + } + + /** + * The minimum depth below the basedir before a file is selected. + * + * @param min maximum directory levels below basedir to go + */ + public function setMax($max) { + $this->max = (int) $max; + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i = 0, $size=count($parameters); $i < $size; $i++) { + $paramname = $parameters[$i]->getName(); + switch(strtolower($paramname)) { + case self::MIN_KEY: + $this->setMin($parameters[$i]->getValue()); + break; + case self::MAX_KEY: + $this->setMax($parameters[$i]->getValue()); + break; + + default: + $this->setError("Invalud parameter " . $paramname); + } // switch + } + } + } + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the max depth is not lower than the min depth. + */ + public function verifySettings() { + if ($this->min < 0 && $this->max < 0) { + $this->setError("You must set at least one of the min or the " . + "max levels."); + } + if ($this->max < $this->min && $this->max > -1) { + $this->setError("The maximum depth is lower than the minimum."); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. Most of the work + * for this selector is offloaded into SelectorUtils, a static class + * that provides the same services for both FilenameSelector and + * DirectoryScanner. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file is a PhingFile object the selector can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + $depth = -1; + // If you felt daring, you could cache the basedir absolute path + $abs_base = $basedir->getAbsolutePath(); + $abs_file = $file->getAbsolutePath(); + + $tok_base = explode(DIRECTORY_SEPARATOR, $abs_base); + $tok_file = explode(DIRECTORY_SEPARATOR, $abs_file); + + for($i=0,$size=count($tok_file); $i < $size; $i++) { + $filetoken = $tok_file[$i]; + if (isset($tok_base[$i])) { + $basetoken = $tok_base[$i]; + // Sanity check. Ditch it if you want faster performance + if ($basetoken !== $filetoken) { + throw new BuildException("File " . $filename . + " does not appear within " . $abs_base . "directory"); + } + } else { // no more basepath tokens + $depth++; + if ($this->max > -1 && $depth > $this->max) { + return false; + } + } + } + if (isset($tok_base[$i + 1])) { + throw new BuildException("File " . $filename . + " is outside of " . $abs_base . "directory tree"); + } + if ($this->min > -1 && $depth < $this->min) { + return false; + } + return true; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/ExtendFileSelector.php b/buildscripts/phing/classes/phing/types/selectors/ExtendFileSelector.php new file mode 100644 index 00000000..8394387c --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/ExtendFileSelector.php @@ -0,0 +1,43 @@ +<?php + +/* + * $Id: dc3c5cb2a3043b7a3f7600591ce825b6563ec0ce $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/Parameterizable.php'; +require_once 'phing/types/selectors/FileSelector.php'; + +/** + * This is the interface to be used by all custom selectors, those that are + * called through the <custom> tag. It is the amalgamation of two + * interfaces, the FileSelector and the Paramterizable interface. Note that + * you will almost certainly want the default behaviour for handling + * Parameters, so you probably want to use the BaseExtendSelector class + * as the base class for your custom selector rather than implementing + * this interface from scratch. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +interface ExtendFileSelector extends Parameterizable, FileSelector { + // No further methods necessary. This is just an amalgamation of two other + // interfaces. +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/ExtendSelector.php b/buildscripts/phing/classes/phing/types/selectors/ExtendSelector.php new file mode 100644 index 00000000..1204ef12 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/ExtendSelector.php @@ -0,0 +1,124 @@ +<?php + +/* + * $Id: 1683a0a93b4bb5486f3cffbe40e8260f886c6258 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/util/StringHelper.php'; + +/** + * Selector that selects files by forwarding the request on to other classes. + * + * TODO - Consider adding Path (phing.types.Path) support to this class + * and to the Mappers class. See Ant versions for implimentation details. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class ExtendSelector extends BaseSelector { + + private $classname; + private $dynselector; + private $parameters = array(); + + /** + * Sets the classname of the custom selector. + * + * @param classname is the class which implements this selector + */ + public function setClassname($classname) { + $this->classname = $classname; + } + + /** + * Instantiates the identified custom selector class. + */ + public function selectorCreate() { + if ($this->classname !== null && $this->classname !== "") { + try { + // assume it's fully qualified, import it + $cls = Phing::import($this->classname); + + // make sure class exists + if (class_exists($cls)) { + $this->dynselector = new $cls(); + } else { + $this->setError("Selector " . $this->classname . " not initialized, no such class"); + } + } catch (Exception $e) { + $this->setError("Selector " . $this->classname . " not initialized, could not create class: " . $e->getMessage()); + } + } else { + $this->setError("There is no classname specified"); + } + } + + /** + * Create new parameters to pass to custom selector. + * + * @param p The new Parameter object + */ + public function addParam(Parameter $p) { + $this->parameters[] = $p; + } + + /** + * These are errors specific to ExtendSelector only. If there are + * errors in the custom selector, it should throw a BuildException + * when isSelected() is called. + */ + public function verifySettings() { + // Creation is done here rather than in isSelected() because some + // containers may do a validation pass before running isSelected(), + // but we need to check for the existence of the created class. + if ($this->dynselector === null) { + $this->selectorCreate(); + } + + if (empty($this->classname)) { + $this->setError("The classname attribute is required"); + } elseif ($this->dynselector === null) { + $this->setError("Internal Error: The custom selector was not created"); + } elseif ( !($this->dynselector instanceof ExtendFileSelector) && (count($this->parameters) > 0)) { + $this->setError("Cannot set parameters on custom selector that does not " + . "implement ExtendFileSelector."); + } + } + + + /** + * Allows the custom selector to choose whether to select a file. This + * is also where the Parameters are passed to the custom selector. + * + * @throws BuildException + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + if (count($this->parameters) > 0 && $this->dynselector instanceof ExtendFileSelector) { + // We know that dynselector must be non-null if no error message + $this->dynselector->setParameters($this->parameters); + } + return $this->dynselector->isSelected($basedir, $filename, $file); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/FileSelector.php b/buildscripts/phing/classes/phing/types/selectors/FileSelector.php new file mode 100644 index 00000000..3c014d61 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/FileSelector.php @@ -0,0 +1,47 @@ +<?php + +/* + * $Id: 26b8712469a798faf79d1c877aad89d64880f061 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * This is the interface to be used by all selectors. + * + * @author Hans Lellelid, hans@xmpl.org (Phing) + * @author Bruce Atherton, bruce@callenish.com (Ant) + * @package phing.types.selectors + */ +interface FileSelector { + + /** + * Method that each selector will implement to create their + * selection behaviour. If there is a problem with the setup + * of a selector, it can throw a BuildException to indicate + * the problem. + * + * @param basedir A PhingFile object for the base directory + * @param filename The name of the file to check + * @param file A PhingFile object for this filename + * @return whether the file should be selected or not + * @throws BuildException if the selector was not configured correctly + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file); + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/FilenameSelector.php b/buildscripts/phing/classes/phing/types/selectors/FilenameSelector.php new file mode 100644 index 00000000..04abbe2e --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/FilenameSelector.php @@ -0,0 +1,157 @@ +<?php + +/* + * $Id: d0a6af11eeda50b911bad3717528a9ea72291185 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +include_once 'phing/types/selectors/BaseExtendSelector.php'; + +/** + * Selector that filters files based on the filename. + * + * @author Hans Lellelid, hans@xmpl.org (Phing) + * @author Bruce Atherton, bruce@callenish.com (Ant) + * @package phing.types.selectors + */ +class FilenameSelector extends BaseExtendSelector { + + private $pattern = null; + private $casesensitive = true; + private $negated = false; + const NAME_KEY = "name"; + const CASE_KEY = "casesensitive"; + const NEGATE_KEY = "negate"; + + public function toString() { + $buf = "{filenameselector name: "; + $buf .= $this->pattern; + $buf .= " negate: "; + if ($this->negated) { + $buf .= "true"; + } else { + $buf .= "false"; + } + $buf .= " casesensitive: "; + if ($this->casesensitive) { + $buf .= "true"; + } else { + $buf .= "false"; + } + $buf .= "}"; + return $buf; + } + + /** + * The name of the file, or the pattern for the name, that + * should be used for selection. + * + * @param pattern the file pattern that any filename must match + * against in order to be selected. + */ + public function setName($pattern) { + $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $pattern); + $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); + + if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) { + $pattern .= "**"; + } + $this->pattern = $pattern; + } + + /** + * Whether to ignore case when checking filenames. + * + * @param casesensitive whether to pay attention to case sensitivity + */ + public function setCasesensitive($casesensitive) { + $this->casesensitive = $casesensitive; + } + + /** + * You can optionally reverse the selection of this selector, + * thereby emulating an <exclude> tag, by setting the attribute + * negate to true. This is identical to surrounding the selector + * with <not></not>. + * + * @param negated whether to negate this selection + */ + public function setNegate($negated) { + $this->negated = $negated; + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param array $parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i=0, $len=count($parameters); $i < $len; $i++) { + $paramname = $parameters[$i]->getName(); + switch(strtolower($paramname)) { + case self::NAME_KEY: + $this->setName($parameters[$i]->getValue()); + break; + case self::CASE_KEY: + $this->setCasesensitive($parameters[$i]->getValue()); + break; + case self::NEGATE_KEY: + $this->setNegate($parameters[$i]->getValue()); + break; + default: + $this->setError("Invalid parameter " . $paramname); + } + } // for each param + } // if params + } + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the name attribute has been set. + * + */ + public function verifySettings() { + if ($this->pattern === null) { + $this->setError("The name attribute is required"); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. Most of the work + * for this selector is offloaded into SelectorUtils, a static class + * that provides the same services for both FilenameSelector and + * DirectoryScanner. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file is a PhingFile object the selector can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + $this->validate(); + return (SelectorUtils::matchPath($this->pattern, $filename, $this->casesensitive) + === !($this->negated)); + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/MajoritySelector.php b/buildscripts/phing/classes/phing/types/selectors/MajoritySelector.php new file mode 100644 index 00000000..5ad2629e --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/MajoritySelector.php @@ -0,0 +1,92 @@ +<?php + +/* + * $Id: 9071d88eb880abe5a299f7c5db707d4439ccaa6c $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +/** + * This selector is here just to shake up your thinking a bit. Don't get + * too caught up in boolean, there are other ways you can evaluate a + * collection of selectors. This one takes a vote of the selectors it + * contains, and majority wins. You could also have an "all-but-one" + * selector, a "weighted-average" selector, and so on. These are left + * as exercises for the reader (as are the usecases where this would + * be necessary). + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class MajoritySelector extends BaseSelectorContainer { + + private $allowtie = true; + + public function toString() { + $buf = ""; + if ($this->hasSelectors()) { + $buf .= "{majorityselect: "; + $buf .= parent::toString(); + $buf .= "}"; + } + return $buf; + } + + public function setAllowtie($tiebreaker) { + $this->allowtie = $tiebreaker; + } + + /** + * Returns true (the file is selected) if most of the other selectors + * agree. In case of a tie, go by the allowtie setting. That defaults + * to true, meaning in case of a tie, the file is selected. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file is a PhingFile object for the filename that the selector + * can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + $yesvotes = 0; + $novotes = 0; + + $selectors = $this->selectorElements(); + for($i=0,$size=count($selectors); $i < $size; $i++) { + $result = $selectors[$i]->isSelected($basedir,$filename,$file); + if ($result) { + $yesvotes = $yesvotes + 1; + } else { + $novotes = $novotes + 1; + } + } + if ($yesvotes > $novotes) { + return true; + } + else if ($novotes > $yesvotes) { + return false; + } + // At this point, we know we have a tie. + return $this->allowtie; + } +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/NoneSelector.php b/buildscripts/phing/classes/phing/types/selectors/NoneSelector.php new file mode 100644 index 00000000..f8941208 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/NoneSelector.php @@ -0,0 +1,71 @@ +<?php +/* + * $Id: a085187a008c4d8e8ba25fd6f1315b3dee92ec27 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseSelectorContainer.php'; + +/** + * This selector has a collection of other selectors. All of those selectors + * must refuse to select a file before the file is considered selected by + * this selector. + * + * @author Hans Lellelid <hans@xmpl.org> + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class NoneSelector extends BaseSelectorContainer { + + public function toString() { + $buf = ""; + if ($this->hasSelectors()) { + $buf .= "{noneselect: "; + $buf .= parent::toString(); + $buf .= "}"; + } + return $buf; + } + + /** + * Returns true (the file is selected) only if all other selectors + * agree that the file should not be selected. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file is a java.io.File object for the filename that the selector + * can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + $selectors = $this->selectorElements(); + + for($i=0,$size=count($selectors); $i < $size; $i++) { + $result = $selectors[$i]->isSelected($basedir, $filename, $file); + if ($result) { + return false; + } + } + return true; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/NotSelector.php b/buildscripts/phing/classes/phing/types/selectors/NotSelector.php new file mode 100644 index 00000000..08e70a5a --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/NotSelector.php @@ -0,0 +1,59 @@ +<?php + +/* + * $Id: 8dcce9d2d304b7a8d4be0ef5b5111b582846840d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/NoneSelector.php'; + +/** + * This selector has one other selectors whose meaning it inverts. It + * actually relies on NoneSelector for its implementation of the + * isSelected() method, but it adds a check to ensure there is only one + * other selector contained within. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class NotSelector extends NoneSelector { + + public function toString() { + $buf = ""; + if ($this->hasSelectors()) { + $buf .= "{notselect: "; + $buf .= parent::toString(); + $buf .= "}"; + } + return $buf; + } + + /** + * Makes sure that there is only one entry, sets an error message if + * not. + */ + public function verifySettings() { + if ($this->selectorCount() != 1) { + $this->setError("One and only one selector is allowed within the " . + "<not> tag"); + } + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/OrSelector.php b/buildscripts/phing/classes/phing/types/selectors/OrSelector.php new file mode 100644 index 00000000..56e6df1e --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/OrSelector.php @@ -0,0 +1,72 @@ +<?php +/* + * $Id: bfed3cc534a3e1ab8789f0ed4a6e64419979e21a $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseSelectorContainer.php'; + +/** + * This selector has a collection of other selectors, any of which have to + * select a file in order for this selector to select it. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class OrSelector extends BaseSelectorContainer { + + public function toString() { + $buf = ""; + if ($this->hasSelectors()) { + $buf .= "{orselect: "; + $buf .= parent::toString(); + $buf .= "}"; + } + return $buf; + } + + /** + * Returns true (the file is selected) if any of the other selectors + * agree that the file should be selected. + * + * @param basedir the base directory the scan is being done from + * @param filename the name of the file to check + * @param file a PhingFile object for the filename that the selector + * can use + * @return boolean Whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + $selectors = $this->selectorElements(); + + // First, check that all elements are correctly configured + + for($i=0,$size=count($selectors); $i < $size; $i++) { + $result = $selectors[$i]->isSelected($basedir, $filename, $file); + if ($result) { + return true; + } + } + return false; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/PresentSelector.php b/buildscripts/phing/classes/phing/types/selectors/PresentSelector.php new file mode 100644 index 00000000..462d3927 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/PresentSelector.php @@ -0,0 +1,154 @@ +<?php + +/* + * $Id: db4c8bc8217483d4150b2d9e62d2ef5129038b4e $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Selector that filters files based on whether they appear in another + * directory tree. It can contain a mapper element, so isn't available + * as an ExtendSelector (since those parameters can't hold other + * elements). + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class PresentSelector extends BaseSelector { + + private $targetdir = null; + private $mapperElement = null; + private $map = null; + private $destmustexist = true; + private static $filePresence = array("srconly", "both"); + + public function toString() { + $buf = "{presentselector targetdir: "; + if ($this->targetdir === null) { + $buf .= "NOT YET SET"; + } else { + $buf .= $this->targetdir->getName(); + } + $buf .= " present: "; + if ($this->destmustexist) { + $buf .= "both"; + } else { + $buf .= "srconly"; + } + if ($this->map !== null) { + $buf .= $this->map->toString(); + } elseif ($this->mapperElement !== null) { + $buf .= $this->mapperElement->toString(); + } + $buf .= "}"; + return $buf; + } + + /** + * The name of the file or directory which is checked for matching + * files. + * + * @param targetdir the directory to scan looking for matching files. + */ + public function setTargetdir(PhingFile $targetdir) { + $this->targetdir = $targetdir; + } + + /** + * Defines the FileNameMapper to use (nested mapper element). + * @throws BuildException + */ + public function createMapper() { + if ($this->mapperElement !== null) { + throw new BuildException("Cannot define more than one mapper"); + } + $this->mapperElement = new Mapper($this->getProject()); + return $this->mapperElement; + } + + + /** + * This sets whether to select a file if its dest file is present. + * It could be a <code>negate</code> boolean, but by doing things + * this way, we get some documentation on how the system works. + * A user looking at the documentation should clearly understand + * that the ONLY files whose presence is being tested are those + * that already exist in the source directory, hence the lack of + * a <code>destonly</code> option. + * + * @param string $fp An attribute set to either <code>srconly</code> or + * <code>both</code>. + */ + public function setPresent($fp) { + $idx = array_search($fp, self::$filePresence, true); + if ( $idx === 0 ) { + $this->destmustexist = false; + } + } + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the targetdir attribute has been set and we have a mapper. + */ + public function verifySettings() { + if ($this->targetdir === null) { + $this->setError("The targetdir attribute is required."); + } + if ($this->mapperElement === null) { + $this->map = new IdentityMapper(); + } else { + $this->map = $this->mapperElement->getImplementation(); + } + if ($this->map === null) { + $this->setError("Could not set <mapper> element."); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param basedir the base directory the scan is being done from + * @param filename is the name of the file to check + * @param file is a PhingFile object the selector can use + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + // Determine file whose existence is to be checked + $destfiles = $this->map->main($filename); + // If filename does not match the To attribute of the mapper + // then filter it out of the files we are considering + if ($destfiles === null) { + return false; + } + // Sanity check + if (count($destfiles) !== 1 || $destfiles[0] === null) { + throw new BuildException("Invalid destination file results for " + . $this->targetdir . " with filename " . $filename); + } + $destname = $destfiles[0]; + $destfile = new PhingFile($this->targetdir, $destname); + return $destfile->exists() === $this->destmustexist; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/SelectSelector.php b/buildscripts/phing/classes/phing/types/selectors/SelectSelector.php new file mode 100755 index 00000000..0db73e5d --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/SelectSelector.php @@ -0,0 +1,124 @@ +<?php + +/* + * $Id: aa3a5cceea362713959333bda113e0ca5428a530 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/AndSelector.php'; + +/** + * This selector just holds one other selector and forwards all + * requests to it. It exists so that there is a single selector + * type that can exist outside of any targets, as an element of + * project. It overrides all of the reference stuff so that it + * works as expected. Note that this is the only selector you + * can reference. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @version $Id$ + * @package phing.types.selectors + */ +class SelectSelector extends AndSelector { + + public function toString() { + $buf = ""; + if ($this->hasSelectors()) { + $buf .= "{select: "; + $buf .= parent::toString(); + $buf .= "}"; + } + return $buf; + } + + /** + * Performs the check for circular references and returns the + * referenced Selector. + */ + private function getRef() { + $o = $this->getCheckedRef(get_class($this), "SelectSelector"); + return $o; + } + + /** + * Indicates whether there are any selectors here. + */ + public function hasSelectors() { + if ($this->isReference()) { + return $this->getRef()->hasSelectors(); + } + return parent::hasSelectors(); + } + + /** + * Gives the count of the number of selectors in this container + */ + public function selectorCount() { + if ($this->isReference()) { + return $this->getRef()->selectorCount(); + } + return parent::selectorCount(); + } + + /** + * Returns the set of selectors as an array. + */ + public function getSelectors(Project $p) { + if ($this->isReference()) { + return $this->getRef()->getSelectors($p); + } + return parent::getSelectors($p); + } + + /** + * Returns an enumerator for accessing the set of selectors. + */ + public function selectorElements() { + if ($this->isReference()) { + return $this->getRef()->selectorElements(); + } + return parent::selectorElements(); + } + + /** + * Add a new selector into this container. + * + * @param selector the new selector to add + * @return the selector that was added + */ + public function appendSelector(FileSelector $selector) { + if ($this->isReference()) { + throw $this->noChildrenAllowed(); + } + parent::appendSelector($selector); + } + + /** + * Makes sure that there is only one entry, sets an error message if + * not. + */ + public function verifySettings() { + if ($this->selectorCount() != 1) { + $this->setError("One and only one selector is allowed within the " + . "<selector> tag"); + } + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/SelectorContainer.php b/buildscripts/phing/classes/phing/types/selectors/SelectorContainer.php new file mode 100644 index 00000000..0ee42237 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/SelectorContainer.php @@ -0,0 +1,141 @@ +<?php + +/* + * $Id: 6fedde4695e4838f435f336b7936190b16429706 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +/** + * This is the interface for selectors that can contain other selectors. + * + * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> + * @package phing.types.selectors + */ +interface SelectorContainer { + + /** + * Indicates whether there are any selectors here. + * + * @return whether any selectors are in this container + */ + public function hasSelectors(); + + /** + * Gives the count of the number of selectors in this container + * + * @return the number of selectors in this container + */ + public function selectorCount(); + + /** + * Returns a *copy* of the set of selectors as an array. + * + * @return an array of selectors in this container + */ + public function getSelectors(Project $p); + + /** + * Returns an array for accessing the set of selectors. + * + * @return an enumerator that goes through each of the selectors + */ + public function selectorElements(); + + /** + * Add a new selector into this container. + * + * @param selector the new selector to add + * @return the selector that was added + */ + public function appendSelector(FileSelector $selector); + + /* Methods below all add specific selectors */ + + /** + * add a "Select" selector entry on the selector list + */ + public function createSelector(); + + /** + * add an "And" selector entry on the selector list + */ + public function createAnd(); + + /** + * add an "Or" selector entry on the selector list + */ + public function createOr(); + + /** + * add a "Not" selector entry on the selector list + */ + public function createNot(); + + /** + * add a "None" selector entry on the selector list + */ + public function createNone(); + + /** + * add a majority selector entry on the selector list + */ + public function createMajority(); + + /** + * add a selector date entry on the selector list + */ + public function createDate(); + + /** + * add a selector size entry on the selector list + */ + public function createSize(); + + /** + * add a selector filename entry on the selector list + */ + public function createFilename(); + + /** + * add an extended selector entry on the selector list + */ + public function createCustom(); + + /** + * add a contains selector entry on the selector list + */ + public function createContains(); + + /** + * add a present selector entry on the selector list + */ + public function createPresent(); + + /** + * add a depth selector entry on the selector list + */ + public function createDepth(); + + /** + * add a depends selector entry on the selector list + */ + public function createDepend(); + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/SelectorScanner.php b/buildscripts/phing/classes/phing/types/selectors/SelectorScanner.php new file mode 100644 index 00000000..91fc3f4c --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/SelectorScanner.php @@ -0,0 +1,55 @@ +<?php + +/* + * $Id: 3c28c4c2e4c41cf4507870e472ed9f278e2d9c9f $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +/** + * An interface used to describe the actions required by any type of + * directory scanner that supports Selecters. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +interface SelectorScanner { + + /** + * Sets the selectors the scanner should use. + * + * @param selectors the list of selectors + */ + public function setSelectors($selectors); + + /** + * Directories which were selected out of a scan. + * + * @param selectors list selector objects + */ + public function getDeselectedDirectories(); + + /** + * Files which were selected out of a scan. + * + * @param selectors list selector objects + */ + public function getDeselectedFiles(); + +} diff --git a/buildscripts/phing/classes/phing/types/selectors/SelectorUtils.php b/buildscripts/phing/classes/phing/types/selectors/SelectorUtils.php new file mode 100644 index 00000000..2d7e5068 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/SelectorUtils.php @@ -0,0 +1,200 @@ +<?php + +/* + * $Id: 4a682bbe8751f6e09a725af7cfdf2bd17ab00645 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/util/StringHelper.php'; + +/** + * <p>This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + * </p> + * <p>This is a Singleton.</p> + * + * @author Hans Lellelid, hans@xmpl.org (Phing) + * @author Arnout J. Kuiper, ajkuiper@wxs.nl (Ant) + * @author Magesh Umasankar + * @author Bruce Atherton, bruce@callenish.com (Ant) + * @package phing.types.selectors + */ +class SelectorUtils { + + private static $instance; + + /** + * Retrieves the instance of the Singleton. + */ + public static function getInstance() { + if (!isset(self::$instance)) { + self::$instance = new SelectorUtils(); + } + return self::$instance; + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + * <p> + * This is not a general purpose test and should only be used if you + * can live with false positives. For example, <code>pattern=**\a</code> + * and <code>str=b</code> will yield <code>true</code>. + * + * @param pattern The pattern to match against. Must not be + * <code>null</code>. + * @param str The path to match, as a String. Must not be + * <code>null</code>. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static function matchPatternStart($pattern, $str, $isCaseSensitive = true) { + + // When str starts with a DIRECTORY_SEPARATOR, pattern has to start with a + // DIRECTORY_SEPARATOR. + // When pattern starts with a DIRECTORY_SEPARATOR, str has to start with a + // DIRECTORY_SEPARATOR. + if (StringHelper::startsWith(DIRECTORY_SEPARATOR, $str) !== + StringHelper::startsWith(DIRECTORY_SEPARATOR, $pattern)) { + return false; + } + + $patDirs = explode(DIRECTORY_SEPARATOR, $pattern); + $strDirs = explode(DIRECTORY_SEPARATOR, $str); + + $patIdxStart = 0; + $patIdxEnd = count($patDirs)-1; + $strIdxStart = 0; + $strIdxEnd = count($strDirs)-1; + + // up to first '**' + while ($patIdxStart <= $patIdxEnd && $strIdxStart <= $strIdxEnd) { + $patDir = $patDirs[$patIdxStart]; + if ($patDir == "**") { + break; + } + if (!self::match($patDir, $strDirs[$strIdxStart], $isCaseSensitive)) { + return false; + } + $patIdxStart++; + $strIdxStart++; + } + + if ($strIdxStart > $strIdxEnd) { + // String is exhausted + return true; + } elseif ($patIdxStart > $patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else { + // pattern now holds ** while string is not exhausted + // this will generate false positives but we can live with that. + return true; + } + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * @param pattern The pattern to match against. Must not be + * <code>null</code>. + * @param str The path to match, as a String. Must not be + * <code>null</code>. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return <code>true</code> if the pattern matches against the string, + * or <code>false</code> otherwise. + */ + public static function matchPath($pattern, $str, $isCaseSensitive = true) { + + $rePattern = preg_quote($pattern, '/'); + $dirSep = preg_quote(DIRECTORY_SEPARATOR, '/'); + $trailingDirSep = '(('.$dirSep.')?|('.$dirSep.').+)'; + $patternReplacements = array( + $dirSep.'\*\*'.$dirSep => $dirSep.'.*'.$trailingDirSep, + $dirSep.'\*\*' => $trailingDirSep, + '\*\*'.$dirSep => '.*'.$trailingDirSep, + '\*\*' => '.*', + '\*' => '[^'.$dirSep.']*', + '\?' => '[^'.$dirSep.']' + ); + $rePattern = str_replace(array_keys($patternReplacements), array_values($patternReplacements), $rePattern); + $rePattern = '/^'.$rePattern.'$/'.($isCaseSensitive ? '' : 'i'); + return (bool) preg_match($rePattern, $str); + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:<br> + * '*' means zero or more characters<br> + * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be <code>null</code>. + * @param str The string which must be matched against the pattern. + * Must not be <code>null</code>. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * + * @return <code>true</code> if the string matches against the pattern, + * or <code>false</code> otherwise. + */ + public static function match($pattern, $str, $isCaseSensitive = true) { + + $rePattern = preg_quote($pattern, '/'); + $rePattern = str_replace(array("\*", "\?"), array('.*', '.'), $rePattern); + $rePattern = '/^'.$rePattern.'$/'.($isCaseSensitive ? '' : 'i'); + return (bool) preg_match($rePattern, $str); + } + + /** + * Returns dependency information on these two files. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param PhingFile $src the original file + * @param PhingFile $target the file being compared against + * @param int $granularity the amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static function isOutOfDate(PhingFile $src, PhingFile $target, $granularity) { + if (!$src->exists()) { + return false; + } + if (!$target->exists()) { + return true; + } + if (($src->lastModified() - $granularity) > $target->lastModified()) { + return true; + } + return false; + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/SizeSelector.php b/buildscripts/phing/classes/phing/types/selectors/SizeSelector.php new file mode 100644 index 00000000..3aaf8a92 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/SizeSelector.php @@ -0,0 +1,228 @@ +<?php + +/* + * $Id: 4969c98a4e03305a23770e2afd5da641999f6174 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + +/** + * Selector that filters files based on their size. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Bruce Atherton <bruce@callenish.com> (Ant) + * @package phing.types.selectors + */ +class SizeSelector extends BaseExtendSelector { + + private $size = -1; + private $multiplier = 1; + private $sizelimit = -1; + private $cmp = 2; + const SIZE_KEY = "value"; + const UNITS_KEY = "units"; + const WHEN_KEY = "when"; + + private static $sizeComparisons = array("less", "more", "equal"); + private static $byteUnits = array("K", "k", "kilo", "KILO", + "Ki", "KI", "ki", "kibi", "KIBI", + "M", "m", "mega", "MEGA", + "Mi", "MI", "mi", "mebi", "MEBI", + "G", "g", "giga", "GIGA", + "Gi", "GI", "gi", "gibi", "GIBI", + "T", "t", "tera", "TERA", + /* You wish! */ "Ti", "TI", "ti", "tebi", "TEBI" + ); + + public function toString() { + $buf = "{sizeselector value: "; + $buf .= $this->sizelimit; + $buf .= "compare: "; + if ($this->cmp === 0) { + $buf .= "less"; + } elseif ($this->cmp === 1) { + $buf .= "more"; + } else { + $buf .= "equal"; + } + $buf .= "}"; + return $buf; + } + + /** + * A size selector needs to know what size to base its selecting on. + * This will be further modified by the multiplier to get an + * actual size limit. + * + * @param size the size to select against expressed in units + */ + public function setValue($size) { + $this->size = $size; + if (($this->multiplier !== 0) && ($this->size > -1)) { + $this->sizelimit = $size * $this->multiplier; + } + } + + /** + * Sets the units to use for the comparison. This is a little + * complicated because common usage has created standards that + * play havoc with capitalization rules. Thus, some people will + * use "K" for indicating 1000's, when the SI standard calls for + * "k". Others have tried to introduce "K" as a multiple of 1024, + * but that falls down when you reach "M", since "m" is already + * defined as 0.001. + * <p> + * To get around this complexity, a number of standards bodies + * have proposed the 2^10 standard, and at least one has adopted + * it. But we are still left with a populace that isn't clear on + * how capitalization should work. + * <p> + * We therefore ignore capitalization as much as possible. + * Completely mixed case is not possible, but all upper and lower + * forms are accepted for all long and short forms. Since we have + * no need to work with the 0.001 case, this practice works here. + * <p> + * This function translates all the long and short forms that a + * unit prefix can occur in and translates them into a single + * multiplier. + * + * @param $units The units to compare the size to. + * @return void + */ + public function setUnits($units) { + $i = array_search($units, self::$byteUnits, true); + if ($i === false) $i = -1; // make it java-like + + $this->multiplier = 0; + if (($i > -1) && ($i < 4)) { + $this->multiplier = 1000; + } elseif (($i > 3) && ($i < 9)) { + $this->multiplier = 1024; + } elseif (($i > 8) && ($i < 13)) { + $this->multiplier = 1000000; + } elseif (($i > 12) && ($i < 18)) { + $this->multiplier = 1048576; + } elseif (($i > 17) && ($i < 22)) { + $this->multiplier = 1000000000; + } elseif (($i > 21) && ($i < 27)) { + $this->multiplier = 1073741824; + } elseif (($i > 26) && ($i < 31)) { + $this->multiplier = 1000000000000; + } elseif (($i > 30) && ($i < 36)) { + $this->multiplier = 1099511627776; + } + if (($this->multiplier > 0) && ($this->size > -1)) { + $this->sizelimit = $this->size * $this->multiplier; + } + } + + /** + * This specifies when the file should be selected, whether it be + * when the file matches a particular size, when it is smaller, + * or whether it is larger. + * + * @param cmp The comparison to perform, an EnumeratedAttribute + */ + public function setWhen($cmp) { + $c = array_search($cmp, self::$sizeComparisons, true); + if ($c !== false) { + $this->cmp = $c; + } + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i = 0, $size=count($parameters); $i < $size; $i++) { + $paramname = $parameters[$i]->getName(); + switch(strtolower($paramname)) { + case self::SIZE_KEY: + try { + $this->setValue($parameters[$i]->getValue()); + } catch (Exception $nfe) { + $this->setError("Invalid size setting " + . $parameters[$i]->getValue()); + } + break; + case self::UNITS_KEY: + $this->setUnits($parameters[$i]->getValue()); + break; + case self::WHEN_KEY: + $this->setWhen($parameters[$i]->getValue()); + break; + default: + $this->setError("Invalid parameter " . $paramname); + } + } + } + } + + /** + * <p>Checks to make sure all settings are kosher. In this case, it + * means that the size attribute has been set (to a positive value), + * that the multiplier has a valid setting, and that the size limit + * is valid. Since the latter is a calculated value, this can only + * fail due to a programming error. + * </p> + * <p>If a problem is detected, the setError() method is called. + * </p> + */ + public function verifySettings() { + if ($this->size < 0) { + $this->setError("The value attribute is required, and must be positive"); + } elseif ($this->multiplier < 1) { + $this->setError("Invalid Units supplied, must be K,Ki,M,Mi,G,Gi,T,or Ti"); + } elseif ($this->sizelimit < 0) { + $this->setError("Internal error: Code is not setting sizelimit correctly"); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param basedir A PhingFile object for the base directory + * @param filename The name of the file to check + * @param file A PhingFile object for this filename + * @return whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + + $this->validate(); + + // Directory size never selected for + if ($file->isDirectory()) { + return true; + } + if ($this->cmp === 0) { + return ($file->length() < $this->sizelimit); + } elseif ($this->cmp === 1) { + return ($file->length() > $this->sizelimit); + } else { + return ($file->length() === $this->sizelimit); + } + } + +} + diff --git a/buildscripts/phing/classes/phing/types/selectors/TypeSelector.php b/buildscripts/phing/classes/phing/types/selectors/TypeSelector.php new file mode 100755 index 00000000..6ff024e8 --- /dev/null +++ b/buildscripts/phing/classes/phing/types/selectors/TypeSelector.php @@ -0,0 +1,120 @@ +<?php + +/* + * $Id: 95c34bb6a364dc0131e9e9c5c124d5074b4a2a21 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/BaseExtendSelector.php'; + +/** + * Selector that selects a certain kind of file: directory or regular file. + * + * @author Hans Lellelid <hans@xmpl.org> (Phing) + * @author Jeff Turner <jefft@apache.org> (Ant) + * @version $Id: 95c34bb6a364dc0131e9e9c5c124d5074b4a2a21 $ + * @package phing.types.selectors + */ +class TypeSelector extends BaseExtendSelector { + + private $type; + + /** Key to used for parameterized custom selector */ + const TYPE_KEY = "type"; + + /** Valid types */ + private static $types = array('file', 'dir', 'link'); + + /** + * @return string A string describing this object + */ + public function toString() { + $buf = "{typeselector type: " . $this->type . "}"; + return $buf; + } + + /** + * Set the type of file to require. + * @param string $type The type of file - 'file' or 'dir' + */ + public function setType($type) { + $this->type = $type; + } + + /** + * When using this as a custom selector, this method will be called. + * It translates each parameter into the appropriate setXXX() call. + * + * @param array $parameters the complete set of parameters for this selector + */ + public function setParameters($parameters) { + parent::setParameters($parameters); + if ($parameters !== null) { + for ($i = 0, $size=count($parameters); $i < $size; $i++) { + $paramname = $parameters[$i]->getName(); + if (self::TYPE_KEY == strtolower($paramname)) { + $this->setType($parameters[$i]->getValue()); + } else { + $this->setError("Invalid parameter " . $paramname); + } + } + } + } + + /** + * Checks to make sure all settings are kosher. In this case, it + * means that the pattern attribute has been set. + * + */ + public function verifySettings() { + if ($this->type === null) { + $this->setError("The type attribute is required"); + } elseif (!in_array($this->type, self::$types, true)) { + $this->setError("Invalid type specified; must be one of (" . implode(self::$types) . ")"); + } + } + + /** + * The heart of the matter. This is where the selector gets to decide + * on the inclusion of a file in a particular fileset. + * + * @param PhingFile $basedir the base directory the scan is being done from + * @param string $filename is the name of the file to check + * @param PhingFile $file is a PhingFile object the selector can use + * @return boolean Whether the file should be selected or not + */ + public function isSelected(PhingFile $basedir, $filename, PhingFile $file) { + // throw BuildException on error + $this->validate(); + + if ($file->isLink()) { + if ($this->type == 'link') + return true; + + $this->log($file->getAbsolutePath() . " is a link, proceeding with " . $file->getCanonicalPath() . " instead.", Project::MSG_DEBUG); + $file = new PhingFile($file->getCanonicalPath()); + } + + if ($file->isDirectory()) { + return $this->type === 'dir'; + } else { + return $this->type === 'file'; + } + } + +} diff --git a/buildscripts/phing/classes/phing/util/DataStore.php b/buildscripts/phing/classes/phing/util/DataStore.php new file mode 100755 index 00000000..af696697 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/DataStore.php @@ -0,0 +1,151 @@ +<?php + +/* + * $Id: 77f24d8b9d8082b4c23cb4cd5d23a06a3be88e2d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/system/io/PhingFile.php'; +require_once 'phing/system/io/FileWriter.php'; + +/** + * An abstract representation of file and directory pathnames. + * + * @package phing.util + * @author Michiel Rook <mrook@php.net> + * @version $Id$ + */ +class DataStore +{ + private $data = array(); + private $file = null; + + /** + * Constructs a new data store + * + * @param PhingFile $file object pointing to the data store on disk + */ + function __construct(PhingFile $file) + { + $this->file = $file; + + if ($this->file->exists()) + { + $this->read(); + } + } + + /** + * Destructor + */ + function __destruct() + { + $this->commit(); + } + + /** + * Retrieves a value from the data store + * + * @param string $key the key + * + * @return mixed the value + */ + public function get($key) + { + if (!isset($this->data[$key])) + { + return null; + } + else + { + return $this->data[$key]; + } + } + + /** + * Adds a value to the data store + * + * @param string $key the key + * @param mixed $value the value + * @param boolean $autocommit whether to auto-commit (write) + * the data store to disk + * + * @return none + */ + public function put($key, $value, $autocommit = false) + { + $this->data[$key] = $value; + + if ($autocommit) + { + $this->commit(); + } + } + + /** + * Commits data store to disk + * + * @return none + */ + public function commit() + { + $this->write(); + } + + /** + * Internal function to read data store from file + * + * @return none + */ + private function read() + { + if (!$this->file->canRead()) + { + throw new BuildException("Can't read data store from '" . + $file->getPath() . "'"); + } + else + { + $serializedData = $this->file->contents(); + + $this->data = unserialize($serializedData); + } + } + + /** + * Internal function to write data store to file + * + * @return none + */ + private function write() + { + if (!$this->file->canWrite()) + { + throw new BuildException("Can't write data store to '" . + $file->getPath() . "'"); + } + else + { + $serializedData = serialize($this->data); + + $writer = new FileWriter($this->file); + $writer->write($serializedData); + $writer->close(); + } + } +}; diff --git a/buildscripts/phing/classes/phing/util/DirectoryScanner.php b/buildscripts/phing/classes/phing/util/DirectoryScanner.php new file mode 100755 index 00000000..02d5be88 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/DirectoryScanner.php @@ -0,0 +1,755 @@ +<?php +/* + * $Id: 7aef4b4e372e89055248ab063660dbee92a98cc3 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/types/selectors/SelectorScanner.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/types/selectors/SelectorUtils.php'; + +/** + * Class for scanning a directory for files/directories that match a certain + * criteria. + * + * These criteria consist of a set of include and exclude patterns. With these + * patterns, you can select which files you want to have included, and which + * files you want to have excluded. + * + * The idea is simple. A given directory is recursively scanned for all files + * and directories. Each file/directory is matched against a set of include + * and exclude patterns. Only files/directories that match at least one + * pattern of the include pattern list, and don't match a pattern of the + * exclude pattern list will be placed in the list of files/directories found. + * + * When no list of include patterns is supplied, "**" will be used, which + * means that everything will be matched. When no list of exclude patterns is + * supplied, an empty list is used, such that nothing will be excluded. + * + * The pattern matching is done as follows: + * The name to be matched is split up in path segments. A path segment is the + * name of a directory or file, which is bounded by DIRECTORY_SEPARATOR + * ('/' under UNIX, '\' under Windows). + * E.g. "abc/def/ghi/xyz.php" is split up in the segments "abc", "def", "ghi" + * and "xyz.php". + * The same is done for the pattern against which should be matched. + * + * Then the segments of the name and the pattern will be matched against each + * other. When '**' is used for a path segment in the pattern, then it matches + * zero or more path segments of the name. + * + * There are special case regarding the use of DIRECTORY_SEPARATOR at + * the beginning of the pattern and the string to match: + * When a pattern starts with a DIRECTORY_SEPARATOR, the string + * to match must also start with a DIRECTORY_SEPARATOR. + * When a pattern does not start with a DIRECTORY_SEPARATOR, the + * string to match may not start with a DIRECTORY_SEPARATOR. + * When one of these rules is not obeyed, the string will not + * match. + * + * When a name path segment is matched against a pattern path segment, the + * following special characters can be used: + * '*' matches zero or more characters, + * '?' matches one character. + * + * Examples: + * + * "**\*.php" matches all .php files/dirs in a directory tree. + * + * "test\a??.php" matches all files/dirs which start with an 'a', then two + * more characters and then ".php", in a directory called test. + * + * "**" matches everything in a directory tree. + * + * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where + * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). + * + * Case sensitivity may be turned off if necessary. By default, it is + * turned on. + * + * Example of usage: + * $ds = new DirectroyScanner(); + * $includes = array("**\*.php"); + * $excludes = array("modules\*\**"); + * $ds->SetIncludes($includes); + * $ds->SetExcludes($excludes); + * $ds->SetBasedir("test"); + * $ds->SetCaseSensitive(true); + * $ds->Scan(); + * + * print("FILES:"); + * $files = ds->GetIncludedFiles(); + * for ($i = 0; $i < count($files);$i++) { + * println("$files[$i]\n"); + * } + * + * This will scan a directory called test for .php files, but excludes all + * .php files in all directories under a directory called "modules" + * + * This class is complete preg/ereg free port of the Java class + * org.apache.tools.ant.DirectoryScanner. Even functions that use preg/ereg + * internally (like split()) are not used. Only the _fast_ string functions + * and comparison operators (=== !=== etc) are used for matching and tokenizing. + * + * @author Arnout J. Kuiper, ajkuiper@wxs.nl + * @author Magesh Umasankar, umagesh@rediffmail.com + * @author Andreas Aderhold, andi@binarycloud.com + * + * @version $Id: 7aef4b4e372e89055248ab063660dbee92a98cc3 $ + * @package phing.util + */ +class DirectoryScanner implements SelectorScanner { + + /** default set of excludes */ + protected $DEFAULTEXCLUDES = array( + "**/*~", + "**/#*#", + "**/.#*", + "**/%*%", + "**/CVS", + "**/CVS/**", + "**/.cvsignore", + "**/SCCS", + "**/SCCS/**", + "**/vssver.scc", + "**/.svn", + "**/.svn/**", + "**/._*", + "**/.DS_Store", + "**/.darcs", + "**/.darcs/**", + "**/.git", + "**/.git/**", + "**/.gitattributes", + "**/.gitignore", + "**/.gitmodules", + ); + + /** The base directory which should be scanned. */ + protected $basedir; + + /** The patterns for the files that should be included. */ + protected $includes = null; + + /** The patterns for the files that should be excluded. */ + protected $excludes = null; + + /** Whether to expand/dereference symbolic links, default is false */ + protected $expandSymbolicLinks = false; + + /** + * The files that where found and matched at least one includes, and matched + * no excludes. + */ + protected $filesIncluded; + + /** The files that where found and did not match any includes. Trie */ + protected $filesNotIncluded; + + /** + * The files that where found and matched at least one includes, and also + * matched at least one excludes. Trie object. + */ + protected $filesExcluded; + + /** + * The directories that where found and matched at least one includes, and + * matched no excludes. + */ + protected $dirsIncluded; + + /** The directories that where found and did not match any includes. */ + protected $dirsNotIncluded; + + /** + * The files that where found and matched at least one includes, and also + * matched at least one excludes. + */ + protected $dirsExcluded; + + /** Have the vars holding our results been built by a slow scan? */ + protected $haveSlowResults = false; + + /** Should the file system be treated as a case sensitive one? */ + protected $isCaseSensitive = true; + + /** Selectors */ + protected $selectors = null; + + protected $filesDeselected; + protected $dirsDeselected; + + /** if there are no deselected files */ + protected $everythingIncluded = true; + + /** + * Does the path match the start of this pattern up to the first "**". + * This is a static mehtod and should always be called static + * + * This is not a general purpose test and should only be used if you + * can live with false positives. + * + * pattern=**\a and str=b will yield true. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string (path) to match + * @param isCaseSensitive must matches be case sensitive? + * @return boolean true if matches, otherwise false + */ + function matchPatternStart($pattern, $str, $isCaseSensitive = true) { + return SelectorUtils::matchPatternStart($pattern, $str, $isCaseSensitive); + } + + /** + * Matches a path against a pattern. Static + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string (path) to match + * @param isCaseSensitive must a case sensitive match be done? + * + * @return true when the pattern matches against the string. + * false otherwise. + */ + function matchPath($pattern, $str, $isCaseSensitive = true) { + return SelectorUtils::matchPath($pattern, $str, $isCaseSensitive); + } + + /** + * Matches a string against a pattern. The pattern contains two special + * characters: + * '*' which means zero or more characters, + * '?' which means one and only one character. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string that must be matched against the + * pattern + * + * @return boolean true when the string matches against the pattern, + * false otherwise. + * @access public + */ + function match($pattern, $str, $isCaseSensitive = true) { + return SelectorUtils::match($pattern, $str, $isCaseSensitive); + } + + /** + * Sets the basedir for scanning. This is the directory that is scanned + * recursively. All '/' and '\' characters are replaced by + * DIRECTORY_SEPARATOR + * + * @param basedir the (non-null) basedir for scanning + */ + function setBasedir($_basedir) { + $_basedir = str_replace('\\', DIRECTORY_SEPARATOR, $_basedir); + $_basedir = str_replace('/', DIRECTORY_SEPARATOR, $_basedir); + $this->basedir = $_basedir; + } + + /** + * Gets the basedir that is used for scanning. This is the directory that + * is scanned recursively. + * + * @return the basedir that is used for scanning + */ + function getBasedir() { + return $this->basedir; + } + + /** + * Sets the case sensitivity of the file system + * + * @param specifies if the filesystem is case sensitive + */ + function setCaseSensitive($_isCaseSensitive) { + $this->isCaseSensitive = ($_isCaseSensitive) ? true : false; + } + + /** + * Sets the set of include patterns to use. All '/' and '\' characters are + * replaced by DIRECTORY_SEPARATOR. So the separator used need + * not match DIRECTORY_SEPARATOR. + * + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes list of include patterns + */ + function setIncludes($_includes = array()) { + if (empty($_includes) || is_null($_includes)) { + $this->includes = null; + } else { + for ($i = 0; $i < count($_includes); $i++) { + $pattern = null; + $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_includes[$i]); + $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); + if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) { + $pattern .= "**"; + } + $this->includes[] = $pattern; + } + } + } + + /** + * Sets the set of exclude patterns to use. All '/' and '\' characters are + * replaced by <code>File.separatorChar</code>. So the separator used need + * not match <code>File.separatorChar</code>. + * + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes list of exclude patterns + */ + + function setExcludes($_excludes = array()) { + if (empty($_excludes) || is_null($_excludes)) { + $this->excludes = null; + } else { + for ($i = 0; $i < count($_excludes); $i++) { + $pattern = null; + $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_excludes[$i]); + $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); + if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) { + $pattern .= "**"; + } + $this->excludes[] = $pattern; + } + } + } + + /** + * Sets whether to expand/dereference symbolic links + * + * @param expandSymbolicLinks boolean value + */ + function setExpandSymbolicLinks($expandSymbolicLinks) + { + $this->expandSymbolicLinks = $expandSymbolicLinks; + } + + /** + * Scans the base directory for files that match at least one include + * pattern, and don't match any exclude patterns. + * + */ + function scan() { + + if ((empty($this->basedir)) || (!@is_dir($this->basedir))) { + return false; + } + + if ($this->includes === null) { + // No includes supplied, so set it to 'matches all' + $this->includes = array("**"); + } + if (is_null($this->excludes)) { + $this->excludes = array(); + } + + $this->filesIncluded = array(); + $this->filesNotIncluded = array(); + $this->filesExcluded = array(); + $this->dirsIncluded = array(); + $this->dirsNotIncluded = array(); + $this->dirsExcluded = array(); + $this->dirsDeselected = array(); + $this->filesDeselected = array(); + + if ($this->isIncluded("")) { + if (!$this->isExcluded("")) { + if ($this->isSelected("", $this->basedir)) { + $this->dirsIncluded[] = ""; + } else { + $this->dirsDeselected[] = ""; + } + } else { + $this->dirsExcluded[] = ""; + } + } else { + $this->dirsNotIncluded[] = ""; + } + + $this->scandir($this->basedir, "", true); + return true; + } + + /** + * Toplevel invocation for the scan. + * + * Returns immediately if a slow scan has already been requested. + */ + protected function slowScan() { + + if ($this->haveSlowResults) { + return; + } + + // copy trie object add CopyInto() method + $excl = $this->dirsExcluded; + $notIncl = $this->dirsNotIncluded; + + for ($i=0, $_i=count($excl); $i < $_i; $i++) { + if (!$this->couldHoldIncluded($excl[$i])) { + $this->scandir($this->basedir.$excl[$i], $excl[$i].DIRECTORY_SEPARATOR, false); + } + } + + for ($i=0, $_i=count($notIncl); $i < $_i; $i++) { + if (!$this->couldHoldIncluded($notIncl[$i])) { + $this->scandir($this->basedir.$notIncl[$i], $notIncl[$i].DIRECTORY_SEPARATOR, false); + } + } + + $this->haveSlowResults = true; + } + + /** + * Lists contens of a given directory and returns array with entries + * + * @param src String. Source path and name file to copy. + * + * @access public + * @return array directory entries + * @author Albert Lash, alash@plateauinnovation.com + */ + + function listDir($_dir) { + $d = dir($_dir); + $list = array(); + while(($entry = $d->read()) !== false) { + if ($entry != "." && $entry != "..") { + $list[] = $entry; + } + } + $d->close(); + return $list; + } + + /** + * Scans the passed dir for files and directories. Found files and + * directories are placed in their respective collections, based on the + * matching of includes and excludes. When a directory is found, it is + * scanned recursively. + * + * @param dir the directory to scan + * @param vpath the path relative to the basedir (needed to prevent + * problems with an absolute path when using dir) + * + * @access private + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + */ + private function scandir($_rootdir, $_vpath, $_fast) { + + if (!is_readable($_rootdir)) { + return; + } + + $newfiles = self::listDir($_rootdir); + + for ($i=0,$_i=count($newfiles); $i < $_i; $i++) { + + $file = $_rootdir . DIRECTORY_SEPARATOR . $newfiles[$i]; + $name = $_vpath . $newfiles[$i]; + + if (@is_link($file) && !$this->expandSymbolicLinks) + { + if ($this->isIncluded($name)) { + if (!$this->isExcluded($name)) { + if ($this->isSelected($name, $file)) { + $this->filesIncluded[] = $name; + } else { + $this->everythingIncluded = false; + $this->filesDeselected[] = $name; + } + } else { + $this->everythingIncluded = false; + $this->filesExcluded[] = $name; + } + } else { + $this->everythingIncluded = false; + $this->filesNotIncluded[] = $name; + } + } + else + if (@is_dir($file)) { + if ($this->isIncluded($name)) { + if (!$this->isExcluded($name)) { + if ($this->isSelected($name, $file)) { + $this->dirsIncluded[] = $name; + if ($_fast) { + $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); + } + } else { + $this->everythingIncluded = false; + $this->dirsDeselected[] = $name; + if ($_fast && $this->couldHoldIncluded($name)) { + $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); + } + } + } else { + $this->everythingIncluded = false; + $this->dirsExcluded[] = $name; + if ($_fast && $this->couldHoldIncluded($name)) { + $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); + } + } + } else { + $this->everythingIncluded = false; + $this->dirsNotIncluded[] = $name; + if ($_fast && $this->couldHoldIncluded($name)) { + $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); + } + } + + if (!$_fast) { + $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); + } + + } elseif (@is_file($file)) { + if ($this->isIncluded($name)) { + if (!$this->isExcluded($name)) { + if ($this->isSelected($name, $file)) { + $this->filesIncluded[] = $name; + } else { + $this->everythingIncluded = false; + $this->filesDeselected[] = $name; + } + } else { + $this->everythingIncluded = false; + $this->filesExcluded[] = $name; + } + } else { + $this->everythingIncluded = false; + $this->filesNotIncluded[] = $name; + } + } + } + } + + /** + * Tests whether a name matches against at least one include pattern. + * + * @param name the name to match + * @return <code>true</code> when the name matches against at least one + * include pattern, <code>false</code> otherwise. + */ + protected function isIncluded($_name) { + for ($i=0, $_i=count($this->includes); $i < $_i; $i++) { + if (DirectoryScanner::matchPath($this->includes[$i], $_name, $this->isCaseSensitive)) { + return true; + } + } + return false; + } + + /** + * Tests whether a name matches the start of at least one include pattern. + * + * @param name the name to match + * @return <code>true</code> when the name matches against at least one + * include pattern, <code>false</code> otherwise. + */ + protected function couldHoldIncluded($_name) { + for ($i = 0; $i < count($this->includes); $i++) { + if (DirectoryScanner::matchPatternStart($this->includes[$i], $_name, $this->isCaseSensitive)) { + return true; + } + } + return false; + } + + /** + * Tests whether a name matches against at least one exclude pattern. + * + * @param name the name to match + * @return <code>true</code> when the name matches against at least one + * exclude pattern, <code>false</code> otherwise. + */ + protected function isExcluded($_name) { + for ($i = 0; $i < count($this->excludes); $i++) { + if (DirectoryScanner::matchPath($this->excludes[$i], $_name, $this->isCaseSensitive)) { + return true; + } + } + return false; + } + + /** + * Get the names of the files that matched at least one of the include + * patterns, and matched none of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + function getIncludedFiles() { + return $this->filesIncluded; + } + + /** + * Get the names of the files that matched at none of the include patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + function getNotIncludedFiles() { + $this->slowScan(); + return $this->filesNotIncluded; + } + + /** + * Get the names of the files that matched at least one of the include + * patterns, an matched also at least one of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + + function getExcludedFiles() { + $this->slowScan(); + return $this->filesExcluded; + } + + /** + * <p>Returns the names of the files which were selected out and + * therefore not ultimately included.</p> + * + * <p>The names are relative to the base directory. This involves + * performing a slow scan if one has not already been completed.</p> + * + * @return the names of the files which were deselected. + * + * @see #slowScan + */ + public function getDeselectedFiles() { + $this->slowScan(); + return $this->filesDeselected; + } + + /** + * Get the names of the directories that matched at least one of the include + * patterns, an matched none of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + + function getIncludedDirectories() { + return $this->dirsIncluded; + } + + /** + * Get the names of the directories that matched at none of the include + * patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + function getNotIncludedDirectories() { + $this->slowScan(); + return $this->dirsNotIncluded; + } + + /** + * <p>Returns the names of the directories which were selected out and + * therefore not ultimately included.</p> + * + * <p>The names are relative to the base directory. This involves + * performing a slow scan if one has not already been completed.</p> + * + * @return the names of the directories which were deselected. + * + * @see #slowScan + */ + public function getDeselectedDirectories() { + $this->slowScan(); + return $this->dirsDeselected; + } + + /** + * Get the names of the directories that matched at least one of the include + * patterns, an matched also at least one of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + function getExcludedDirectories() { + $this->slowScan(); + return $this->dirsExcluded; + } + + /** + * Adds the array with default exclusions to the current exclusions set. + * + */ + function addDefaultExcludes() { + //$excludesLength = ($this->excludes == null) ? 0 : count($this->excludes); + foreach($this->DEFAULTEXCLUDES as $pattern) { + $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $pattern); + $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); + $this->excludes[] = $pattern; + } + } + + /** + * Sets the selectors that will select the filelist. + * + * @param selectors specifies the selectors to be invoked on a scan + */ + public function setSelectors($selectors) { + $this->selectors = $selectors; + } + + /** + * Returns whether or not the scanner has included all the files or + * directories it has come across so far. + * + * @return <code>true</code> if all files and directories which have + * been found so far have been included. + */ + public function isEverythingIncluded() { + return $this->everythingIncluded; + } + + /** + * Tests whether a name should be selected. + * + * @param string $name The filename to check for selecting. + * @param string $file The full file path. + * @return boolean False when the selectors says that the file + * should not be selected, True otherwise. + */ + protected function isSelected($name, $file) { + if ($this->selectors !== null) { + $basedir = new PhingFile($this->basedir); + $file = new PhingFile($file); + if (!$file->canRead()) + return false; + + foreach($this->selectors as $selector) { + if (!$selector->isSelected($basedir, $name, $file)) { + return false; + } + } + } + return true; + } + +} diff --git a/buildscripts/phing/classes/phing/util/ExtendedFileStream.php b/buildscripts/phing/classes/phing/util/ExtendedFileStream.php new file mode 100755 index 00000000..9c09b56f --- /dev/null +++ b/buildscripts/phing/classes/phing/util/ExtendedFileStream.php @@ -0,0 +1,129 @@ +<?php + + include_once 'phing/system/io/PhingFile.php'; + + /** + * $Id: f7e4641c758d5e781ad209d3eaf653a20404bf56 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + /** + * Extended file stream wrapper class which auto-creates directories + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: f7e4641c758d5e781ad209d3eaf653a20404bf56 $ + * @package phing.util + */ + class ExtendedFileStream + { + private $fp = NULL; + + static function registerStream() + { + if (!in_array("efile", stream_get_wrappers())) + { + stream_wrapper_register("efile", "ExtendedFileStream"); + } + } + + static function unregisterStream() + { + stream_wrapper_unregister("efile"); + } + + private function createDirectories($path) + { + $f = new PhingFile($path); + if (!$f->exists()) { + $f->mkdirs(); + } + } + + function stream_open($path, $mode, $options, &$opened_path) + { + $filepath = substr($path, 8); + + $this->createDirectories(dirname($filepath)); + + $this->fp = fopen($filepath, $mode); + + return true; + } + + function stream_close() + { + fclose($this->fp); + $this->fp = NULL; + } + + function stream_read($count) + { + return fread($this->fp, $count); + } + + function stream_write($data) + { + return fwrite($this->fp, $data); + } + + function stream_eof() + { + return feof($this->fp); + } + + function stream_tell() + { + return ftell($this->fp); + } + + function stream_seek($offset, $whence) + { + return fseek($this->fp, $offset, $whence); + } + + function stream_flush() + { + return fflush($this->fp); + } + + function stream_stat() + { + return fstat($this->fp); + } + + function unlink($path) + { + return FALSE; + } + + function rename($path_from, $path_to) + { + return FALSE; + } + + function mkdir($path, $mode, $options) + { + return FALSE; + } + + function rmdir($path, $options) + { + return FALSE; + } + }; + diff --git a/buildscripts/phing/classes/phing/util/FileUtils.php b/buildscripts/phing/classes/phing/util/FileUtils.php new file mode 100755 index 00000000..c98bbdd3 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/FileUtils.php @@ -0,0 +1,298 @@ +<?php +/* + * $Id: fe077b4174763861e773f5e5e55bbfc5030cac4d $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +include_once 'phing/system/lang/Character.php'; +include_once 'phing/util/StringHelper.php'; +include_once 'phing/system/io/BufferedReader.php'; +include_once 'phing/system/io/BufferedWriter.php'; +include_once 'phing/filters/util/ChainReaderHelper.php'; +include_once 'phing/system/io/PhingFile.php'; + +/** + * File utility class. + * - handles os independent stuff etc + * - mapper stuff + * - filter stuff + * + * @package phing.util + * @version $Id$ + */ +class FileUtils { + + /** + * Returns a new Reader with filterchains applied. If filterchains are empty, + * simply returns passed reader. + * + * @param Reader $in Reader to modify (if appropriate). + * @param array &$filterChains filter chains to apply. + * @param Project $project + * @return Reader Assembled Reader (w/ filter chains). + */ + public static function getChainedReader(Reader $in, &$filterChains, Project $project) { + if (!empty($filterChains)) { + $crh = new ChainReaderHelper(); + $crh->setBufferSize(65536); // 64k buffer, but isn't being used (yet?) + $crh->setPrimaryReader($in); + $crh->setFilterChains($filterChains); + $crh->setProject($project); + $rdr = $crh->getAssembledReader(); + return $rdr; + } else { + return $in; + } + } + + /** + * Copies a file using filter chains. + * + * @param PhingFile $sourceFile + * @param PhingFile $destFile + * @param boolean $overwrite + * @param boolean $preserveLastModified + * @param array $filterChains + * @param Project $project + * @param integer $mode + * @return void + */ + function copyFile(PhingFile $sourceFile, PhingFile $destFile, $overwrite = false, $preserveLastModified = true, &$filterChains = null, Project $project, $mode = 0755) { + + if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) { + if ($destFile->exists() && $destFile->isFile()) { + $destFile->delete(); + } + + // ensure that parent dir of dest file exists! + $parent = $destFile->getParentFile(); + if ($parent !== null && !$parent->exists()) { + $parent->mkdirs($mode); + } + + if ((is_array($filterChains)) && (!empty($filterChains))) { + + $in = self::getChainedReader(new BufferedReader(new FileReader($sourceFile)), $filterChains, $project); + $out = new BufferedWriter(new FileWriter($destFile)); + + // New read() methods returns a big buffer. + while(-1 !== ($buffer = $in->read())) { // -1 indicates EOF + $out->write($buffer); + } + + if ( $in !== null ) + $in->close(); + if ( $out !== null ) + $out->close(); + + $destFile->setMode($sourceFile->getMode()); + + } else { + // simple copy (no filtering) + $sourceFile->copyTo($destFile); + } + + if ($preserveLastModified) { + $destFile->setLastModified($sourceFile->lastModified()); + } + + } + } + + /** + * Interpret the filename as a file relative to the given file - + * unless the filename already represents an absolute filename. + * + * @param $file the "reference" file for relative paths. This + * instance must be an absolute file and must not contain + * ./ or ../ sequences (same for \ instead of /). + * @param $filename a file name + * + * @return PhingFile A PhingFile object pointing to an absolute file that doesn't contain ./ or ../ sequences + * and uses the correct separator for the current platform. + */ + function resolveFile($file, $filename) { + // remove this and use the static class constant File::seperator + // as soon as ZE2 is ready + $fs = FileSystem::getFileSystem(); + + $filename = str_replace('/', $fs->getSeparator(), str_replace('\\', $fs->getSeparator(), $filename)); + + // deal with absolute files + if (StringHelper::startsWith($fs->getSeparator(), $filename) || + (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':')) { + return new PhingFile($this->normalize($filename)); + } + + if (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':') { + return new PhingFile($this->normalize($filename)); + } + + $helpFile = new PhingFile($file->getAbsolutePath()); + + $tok = strtok($filename, $fs->getSeparator()); + while ($tok !== false) { + $part = $tok; + if ($part === '..') { + $parentFile = $helpFile->getParent(); + if ($parentFile === null) { + $msg = "The file or path you specified ($filename) is invalid relative to ".$file->getPath(); + throw new IOException($msg); + } + $helpFile = new PhingFile($parentFile); + } else if ($part === '.') { + // Do nothing here + } else { + $helpFile = new PhingFile($helpFile, $part); + } + $tok = strtok($fs->getSeparator()); + } + return new PhingFile($helpFile->getAbsolutePath()); + } + + /** + * Normalize the given absolute path. + * + * This includes: + * - Uppercase the drive letter if there is one. + * - Remove redundant slashes after the drive spec. + * - resolve all ./, .\, ../ and ..\ sequences. + * - DOS style paths that start with a drive letter will have + * \ as the separator. + * @param string $path Path to normalize. + * @return string + */ + function normalize($path) { + + $path = (string) $path; + $orig = $path; + + $path = str_replace('/', DIRECTORY_SEPARATOR, str_replace('\\', DIRECTORY_SEPARATOR, $path)); + + // make sure we are dealing with an absolute path + if (!StringHelper::startsWith(DIRECTORY_SEPARATOR, $path) + && !(strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':')) { + throw new IOException("$path is not an absolute path"); + } + + $dosWithDrive = false; + $root = null; + + // Eliminate consecutive slashes after the drive spec + + if (strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':') { + $dosWithDrive = true; + + $ca = str_replace('/', '\\', $path); + $ca = StringHelper::toCharArray($ca); + + $path = strtoupper($ca[0]).':'; + + for ($i=2, $_i=count($ca); $i < $_i; $i++) { + if (($ca[$i] !== '\\') || + ($ca[$i] === '\\' && $ca[$i - 1] !== '\\') + ) { + $path .= $ca[$i]; + } + } + + $path = str_replace('\\', DIRECTORY_SEPARATOR, $path); + + if (strlen($path) == 2) { + $root = $path; + $path = ""; + } else { + $root = substr($path, 0, 3); + $path = substr($path, 3); + } + + } else { + if (strlen($path) == 1) { + $root = DIRECTORY_SEPARATOR; + $path = ""; + } else if ($path{1} == DIRECTORY_SEPARATOR) { + // UNC drive + $root = DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR; + $path = substr($path, 2); + } + else { + $root = DIRECTORY_SEPARATOR; + $path = substr($path, 1); + } + } + + $s = array(); + array_push($s, $root); + $tok = strtok($path, DIRECTORY_SEPARATOR); + while ($tok !== false) { + $thisToken = $tok; + if ("." === $thisToken) { + $tok = strtok(DIRECTORY_SEPARATOR); + continue; + } elseif (".." === $thisToken) { + if (count($s) < 2) { + // using '..' in path that is too short + throw new IOException("Cannot resolve path: $orig"); + } else { + array_pop($s); + } + } else { // plain component + array_push($s, $thisToken); + } + $tok = strtok(DIRECTORY_SEPARATOR); + } + + $sb = ""; + for ($i=0,$_i=count($s); $i < $_i; $i++) { + if ($i > 1) { + // not before the filesystem root and not after it, since root + // already contains one + $sb .= DIRECTORY_SEPARATOR; + } + $sb .= (string) $s[$i]; + } + + + $path = (string) $sb; + if ($dosWithDrive === true) { + $path = str_replace('/', '\\', $path); + } + return $path; + } + + /** + * @return boolean Whether contents of two files is the same. + */ + public function contentEquals(PhingFile $file1, PhingFile $file2) { + + if (!($file1->exists() || $file2->exists())) { + return false; + } + + if (!($file1->canRead() || $file2->canRead())) { + return false; + } + + $c1 = file_get_contents($file1->getAbsolutePath()); + $c2 = file_get_contents($file2->getAbsolutePath()); + + return trim($c1) == trim($c2); + } + +} + diff --git a/buildscripts/phing/classes/phing/util/LogWriter.php b/buildscripts/phing/classes/phing/util/LogWriter.php new file mode 100755 index 00000000..ae97705e --- /dev/null +++ b/buildscripts/phing/classes/phing/util/LogWriter.php @@ -0,0 +1,95 @@ +<?php + + /** + * $Id: ccb6c8b930bb3f293074969323467edeaf40bd02 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + require_once 'phing/system/io/Writer.php'; + require_once 'phing/Task.php'; + + /** + * Extends the Writer class to output messages to Phing's log + * + * @author Michiel Rook <mrook@php.net> + * @version $Id: ccb6c8b930bb3f293074969323467edeaf40bd02 $ + * @package phing.util + */ + class LogWriter extends Writer + { + private $task = NULL; + + private $level = NULL; + + /** + * Constructs a new LogWriter object + */ + function __construct(Task $task, $level = Project::MSG_INFO) + { + $this->task = $task; + $this->level = $level; + } + + /** + * @see Writer::write() + */ + function write($buf, $off = null, $len = null) + { + $lines = explode("\n", $buf); + + foreach ($lines as $line) + { + if ($line == "") + { + continue; + } + + $this->task->log($line, $this->level); + } + } + + /** + * @see Writer::reset() + */ + function reset() + { + } + + /** + * @see Writer::close() + */ + function close() + { + } + + /** + * @see Writer::open() + */ + function open() + { + } + + /** + * @see Writer::getResource() + */ + function getResource() + { + return $this->task; + } + } + diff --git a/buildscripts/phing/classes/phing/util/PathTokenizer.php b/buildscripts/phing/classes/phing/util/PathTokenizer.php new file mode 100644 index 00000000..0a469acf --- /dev/null +++ b/buildscripts/phing/classes/phing/util/PathTokenizer.php @@ -0,0 +1,245 @@ +<?php +/* + * $Id: 0ca7f2a419b5cc4285a8f84695c4b680b0aa190b $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + + + +include_once 'phing/util/StringHelper.php'; + + + +/** + + * A Path tokenizer takes a path and returns the components that make up + + * that path. + + * + + * The path can use path separators of either ':' or ';' and file separators + + * of either '/' or '\'. + + * + + * @author Hans Lellelid <hans@xmpl.org> (Phing) + + * @author Conor MacNeill (Ant) + + * @author Jeff Tulley <jtulley@novell.com> (Ant) + + * @package phing.util + + */ + +class PathTokenizer { + + + + /** + + * A array of tokens, created by preg_split(). + + */ + + private $tokens = array(); + + + + /** + + * A string which stores any path components which have been read ahead + + * due to DOS filesystem compensation. + + * @var string + + */ + + private $lookahead; + + + + /** + + * Flag to indicate whether or not we are running on a platform with a + + * DOS style filesystem + + * @var boolean + + */ + + private $dosStyleFilesystem; + + + + /** + + * Constructs a path tokenizer for the specified path. + + * + + * @param path The path to tokenize. Must not be <code>null</code>. + + */ + + public function __construct($path) { + + // on Windows and Unix, we can ignore delimiters and still have + + // enough information to tokenize correctly. + + $this->tokens = preg_split("/[;:]/", $path, -1, PREG_SPLIT_NO_EMPTY); + + $this->dosStyleFilesystem = ( PATH_SEPARATOR == ';'); + + } + + + + /** + + * Tests if there are more path elements available from this tokenizer's + + * path. If this method returns <code>true</code>, then a subsequent call + + * to nextToken will successfully return a token. + + * + + * @return <code>true</code> if and only if there is at least one token + + * in the string after the current position; <code>false</code> otherwise. + + */ + + public function hasMoreTokens() { + + if ($this->lookahead !== null) { + + return true; + + } + + return !empty($this->tokens); + + } + + + + /** + + * Returns the next path element from this tokenizer. + + * + + * @return the next path element from this tokenizer. + + * + + * @throws Exception if there are no more elements in this tokenizer's path. + + */ + + public function nextToken() { + + + + if ($this->lookahead !== null) { + + $token = $this->lookahead; + + $this->lookahead = null; + + } else { + + $token = trim(array_shift($this->tokens)); + + } + + + + + + if (strlen($token) === 1 && Character::isLetter($token{0}) + + && $this->dosStyleFilesystem + + && !empty($this->tokens)) { + + // we are on a dos style system so this path could be a drive + + // spec. We look at the next token + + $nextToken = trim(array_shift($this->tokens)); + + if (StringHelper::startsWith('\\', $nextToken) || StringHelper::startsWith('/', $nextToken)) { + + // we know we are on a DOS style platform and the next path + + // starts with a slash or backslash, so we know this is a + + // drive spec + + $token .= ':' . $nextToken; + + } else { + + // store the token just read for next time + + $this->lookahead = $nextToken; + + } + + } + + + + return $token; + + } + + + + /** + + * Non StringTokenizer function, that indicates whether the specified path is contained in loaded tokens. + + * We can do this easily because in PHP implimentation we're using arrays. + + * @param string $path path to search for. + + * @return boolean + + */ + + public function contains($path) { + + return in_array($path, $this->tokens, true); + + } + + + +} + + + diff --git a/buildscripts/phing/classes/phing/util/PearPackageScanner.php b/buildscripts/phing/classes/phing/util/PearPackageScanner.php new file mode 100644 index 00000000..9071ed37 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/PearPackageScanner.php @@ -0,0 +1,170 @@ +<?php +/** + * Part of phing, the PHP build tool + * + * PHP version 5 + * + * @category Util + * @package phing.util + * @author Christian Weiske <cweiske@cweiske.de> + * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html + * @version SVN: $Id: e549026313edf53c67f495489f671cf0b71df80d $ + * @link http://www.phing.info/ + */ +require_once 'phing/util/DirectoryScanner.php'; +require_once 'PEAR/Config.php'; + +/** + * Scans for files in a PEAR package. + * + * @category Util + * @package phing.util + * @author Christian Weiske <cweiske@cweiske.de> + * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html + * @link http://www.phing.info/ + */ +class PearPackageScanner extends DirectoryScanner +{ + protected $packageInfo; + protected $role = 'php'; + protected $config; + protected $package; + protected $channel = 'pear.php.net'; + + /** + * Sets the name of the PEAR package to get the files from + * + * @param string $package Package name without channel + * + * @return void + */ + public function setPackage($package) + { + $this->package = $package; + } + + /** + * Sets the name of the package channel name + * + * @param string $channel package channel name or alias + * + * @return void + */ + public function setChannel($channel) + { + $this->channel = $channel; + } + + /** + * Sets the full path to the PEAR configuration file + * + * @param string $config Configuration file + * + * @return void + */ + public function setConfig($config) + { + if ($config != '' && !file_exists($config)) { + throw new BuildException( + 'PEAR configuration file "' . $config . '" does not exist' + ); + } + + $this->config = $config; + } + + /** + * Sets the role of files that should be included. + * Examples are php,doc,script + * + * @param string $role PEAR file role + * + * @return void + * + * @internal + * We do not verify the role against a hardcoded list since that + * would break packages with additional roles. + */ + public function setRole($role) + { + if ($role == '') { + throw new BuildException('A non-empty role is required'); + } + + $this->role = $role; + } + + /** + * Loads the package information. + * + * @return void + * + * @uses $packageInfo + */ + protected function init() + { + if (!$this->packageInfo) { + $this->packageInfo = $this->loadPackageInfo(); + } + } + + /** + * Loads and returns the PEAR package information. + * + * @return PEAR_PackageFile_v2 Package information object + * + * @throws BuildException When the package does not exist + */ + protected function loadPackageInfo() + { + $cfg = PEAR_Config::singleton($this->config); + $reg = $cfg->getRegistry(); + if (!$reg->packageExists($this->package, $this->channel)) { + throw new BuildException( + sprintf( + 'PEAR package %s/%s does not exist', + $this->channel, $this->package + ) + ); + } + + $packageInfo = $reg->getPackage($this->package, $this->channel); + return $packageInfo; + } + + /** + * Generates the list of included files and directories + * + * @return boolean True if all went well, false if something was wrong + * + * @uses $filesIncluded + * @uses $filesDeselected + * @uses $filesNotIncluded + * @uses $filesExcluded + * @uses $everythingIncluded + * @uses $dirsIncluded + * @uses $dirsDeselected + * @uses $dirsNotIncluded + * @uses $dirsExcluded + */ + public function scan() + { + $this->init(); + $list = $this->packageInfo->getFilelist(); + $found = null; + foreach ($list as $file => $att) { + if ($att['role'] != $this->role) { + continue; + } + $this->filesIncluded[] = $file; + $found = array($file, $att); + } + if ($found !== null) { + list($file, $att) = $found; + $this->setBaseDir(substr($att['installed_as'], 0, -strlen($file))); + } + + return true; + } + +} diff --git a/buildscripts/phing/classes/phing/util/SourceFileScanner.php b/buildscripts/phing/classes/phing/util/SourceFileScanner.php new file mode 100644 index 00000000..d958a15f --- /dev/null +++ b/buildscripts/phing/classes/phing/util/SourceFileScanner.php @@ -0,0 +1,159 @@ +<?php +/* + * $Id: 0555ae8b4e7418c211bd69a066877ab8cf95fbb8 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Utility class that collects the functionality of the various + * scanDir methods that have been scattered in several tasks before. + * + * The only method returns an array of source files. The array is a + * subset of the files given as a parameter and holds only those that + * are newer than their corresponding target files. + * @package phing.util + */ +class SourceFileScanner { + + /** Instance of FileUtils */ + private $fileUtils; + + /** Task this class is working for -- for logging purposes. */ + private $task; + + /** + * @param task The task we should log messages through + */ + function __construct($task) { + $this->task = $task; + $this->fileUtils = new FileUtils(); + } + + /** + * Restrict the given set of files to those that are newer than + * their corresponding target files. + * + * @param files the original set of files + * @param srcDir all files are relative to this directory + * @param destDir target files live here. if null file names + * returned by the mapper are assumed to be absolute. + * @param FilenameMapper knows how to construct a target file names from + * source file names. + * @param force Boolean that determines if the files should be + * forced to be copied. + */ + function restrict(&$files, $srcDir, $destDir, $mapper, $force = false) { + $now = time(); + $targetList = ""; + + /* + If we're on Windows, we have to munge the time up to 2 secs to + be able to check file modification times. + (Windows has a max resolution of two secs for modification times) + */ + $osname = strtolower(Phing::getProperty('os.name')); + + // indexOf() + $index = ((($res = strpos($osname, 'win')) === false) ? -1 : $res); + if ($index >= 0 ) { + $now += 2000; + } + + $v = array(); + + for ($i=0, $size=count($files); $i < $size; $i++) { + + $targets = $mapper->main($files[$i]); + if (empty($targets)) { + $this->task->log($files[$i]." skipped - don't know how to handle it", Project::MSG_VERBOSE); + continue; + } + + $src = null; + try { + if ($srcDir === null) { + $src = new PhingFile($files[$i]); + } else { + $src = $this->fileUtils->resolveFile($srcDir, $files[$i]); + } + + if ($src->lastModified() > $now) { + $this->task->log("Warning: ".$files[$i]." modified in the future (".$src->lastModified()." > ".$now.")", Project::MSG_WARN); + } + } catch (IOException $ioe) { + $this->task->log("Unable to read file ".$files[$i]." (skipping): " . $ioe->getMessage()); + continue; + } + + $added = false; + $targetList = ""; + + for ($j=0,$_j=count($targets); (!$added && $j < $_j); $j++) { + + $dest = null; + if ($destDir === null) { + $dest = new PhingFile($targets[$j]); + } else { + $dest = $this->fileUtils->resolveFile($destDir, $targets[$j]); + } + + if (!$dest->exists()) { + $this->task->log($files[$i]." added as " . $dest->__toString() . " doesn't exist.", Project::MSG_VERBOSE); + $v[] =$files[$i]; + $added = true; + } elseif ($src->lastModified() > $dest->lastModified()) { + $this->task->log($files[$i]." added as " . $dest->__toString() . " is outdated.", Project::MSG_VERBOSE ); + $v[]=$files[$i]; + $added = true; + } elseif ($force === true) { + $this->task->log($files[$i]." added as " . $dest->__toString() . " is forced to be overwritten.", Project::MSG_VERBOSE ); + $v[]=$files[$i]; + $added = true; + } else { + if (strlen($targetList) > 0) { + $targetList .= ", "; + } + $targetList .= $dest->getAbsolutePath(); + } + } + + if (!$added) { + $this->task->log($files[$i]." omitted as ".$targetList." ".(count($targets) === 1 ? " is " : " are ")."up to date.", Project::MSG_VERBOSE); + } + + } + $result = array(); + $result = $v; + return $result; + } + + /** + * Convenience layer on top of restrict that returns the source + * files as PhingFile objects (containing absolute paths if srcDir is + * absolute). + */ + function restrictAsFiles(&$files, &$srcDir, &$destDir, &$mapper) { + $res = $this->restrict($files, $srcDir, $destDir, $mapper); + $result = array(); + for ($i=0; $i<count($res); $i++) { + $result[$i] = new PhingFile($srcDir, $res[$i]); + } + return $result; + } +} + diff --git a/buildscripts/phing/classes/phing/util/StringHelper.php b/buildscripts/phing/classes/phing/util/StringHelper.php new file mode 100644 index 00000000..31d0b0e9 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/StringHelper.php @@ -0,0 +1,208 @@ +<?php + +/** + * String helper utility class. + * + * This class includes some Java-like functions for parsing strings, + * as well as some functions for getting qualifiers / unqualifying phing-style + * classpaths. (e.g. "phing.util.StringHelper"). + * + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.system.util + */ +class StringHelper { + + private static $TRUE_VALUES = array("on", "true", "t", "yes"); + private static $FALSE_VALUES = array("off", "false", "f", "no"); + + /** + * Replaces identifier tokens with corresponding text values in passed string. + * + * @param array $strings Array of strings to multiply. (If string is passed, will convert to array) + * @param array $tokens The tokens to search for. + * @param array $replacements The values with which to replace found tokens. + * @return string + */ + public static function multiply($strings, $tokens, $replacements) { + $strings = (array) $strings; + $results = array(); + foreach ($strings as $string) { + $results[] = str_replace($tokens, $replacements, $string); + } + return $results; + } + + /** + * Remove qualification to name. + * E.g. eg.Cat -> Cat + * @param string $qualifiedName + * @param string $separator Character used to separate. + */ + public static function unqualify($qualifiedName, $separator = '.') { + // if false, then will be 0 + $pos = strrpos($qualifiedName, $separator); + if ($pos === false) { + return $qualifiedName; // there is no '.' in the qualifed name + } else { + return substr($qualifiedName, $pos + 1); // start just after '.' + } + } + + /** + * Converts a string to an indexed array of chars + * There's really no reason for this to be used in PHP, since strings + * are all accessible using the $string{0} notation. + * @param string $string + * @return array + * @deprecated + */ + public static function toCharArray($str) { + $ret=array(); + $len=strlen($str); + for ($i=0; $i < $len; $i++) { + $ret[] = $str{$i}; + } + return $ret; + } + + /** + * Get the qualifier part of a qualified name. + * E.g. eg.Cat -> eg + * @return string + */ + public static function qualifier($qualifiedName, $seperator = '.') { + $pos = strrchr($qualifiedName, $seperator); + if ($pos === false) { + return ''; + } else { + return substr($qualifiedName, 0, $pos); + } + } + + /** + * @param array $columns String[] + * @param string $prefix + * @return array String[] + */ + public static function prefix( $columns, $prefix) { + if ($prefix == null) return $columns; + $qualified = array(); + foreach($columns as $key => $column) { + $qualified[$key] = $prefix . $column; + } + return $qualified; + } + + /** + * + * @return string + */ + public static function root($qualifiedName, $separator = '.') { + $loc = strpos($qualifiedName, $separator); + return ($loc === false) ? $qualifiedName : substr($qualifiedName, 0, $loc); + } + + /** + * @return int + */ + public static function hashCode($string) { + return crc32($string); + } + + /** + * @return boolean + */ + public static function booleanValue($s) { + if (is_bool($s)) { + return $s; // it's already boolean (not a string) + } + // otherwise assume it's something like "true" or "t" + $trimmed = strtolower(trim($s)); + return (boolean) in_array($trimmed, self::$TRUE_VALUES); + } + + /** tests if a string is a representative of a boolean */ + public static function isBoolean($s) { + + if (is_bool($s)) { + return true; // it already is boolean + } + + if ($s === "" || $s === null || !is_string($s)) { + return false; // not a valid string for testing + } + + $test = trim(strtolower($s)); + return (boolean) in_array($test, array_merge(self::$FALSE_VALUES, self::$TRUE_VALUES)); + } + + /** + * Creates a key based on any number of passed params. + * @return string + */ + public static function key() { + $args = func_get_args(); + return serialize($args); + } + + /** tests if a string starts with a given string */ + public static function startsWith($check, $string) { + if ($check === "" || $check === $string) { + return true; + } else { + return (strpos($string, $check) === 0) ? true : false; + } + } + + /** tests if a string ends with a given string */ + public static function endsWith($check, $string) { + if ($check === "" || $check === $string) { + return true; + } else { + return (strpos(strrev($string), strrev($check)) === 0) ? true : false; + } + } + + /** + * a natural way of getting a subtring, php's circular string buffer and strange + * return values suck if you want to program strict as of C or friends + */ + public static function substring($string, $startpos, $endpos = -1) { + $len = strlen($string); + $endpos = (int) (($endpos === -1) ? $len-1 : $endpos); + if ($startpos > $len-1 || $startpos < 0) { + trigger_error("substring(), Startindex out of bounds must be 0<n<$len", E_USER_ERROR); + } + if ($endpos > $len-1 || $endpos < $startpos) { + trigger_error("substring(), Endindex out of bounds must be $startpos<n<".($len-1), E_USER_ERROR); + } + if ($startpos === $endpos) { + return (string) $string{$startpos}; + } else { + $len = $endpos-$startpos; + } + return substr($string, $startpos, $len+1); + } + + /** + * Does the value correspond to a slot variable? + * @param string $value + */ + public static function isSlotVar($value) { + $value = trim($value); + if ($value === "") return false; + return preg_match('/^%\{([\w\.\-]+)\}$/', $value); + } + + /** + * Extracts the variable name for a slot var in the format %{task.current_file} + * @param string $var The var from build file. + * @return string Extracted name part. + */ + public static function slotVar($var) { + return trim($var, '%{} '); + } + +} + + diff --git a/buildscripts/phing/classes/phing/util/regexp/PregEngine.php b/buildscripts/phing/classes/phing/util/regexp/PregEngine.php new file mode 100644 index 00000000..76cf56b3 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/regexp/PregEngine.php @@ -0,0 +1,167 @@ +<?php +/* + * $Id: 94607411e16d4c9091369ff4a65ea8f44bde8781 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/util/regexp/RegexpEngine.php'; + +/** + * PREG Regexp Engine. + * Implements a regexp engine using PHP's preg_match(), preg_match_all(), and preg_replace() functions. + * + * @author hans lellelid, hans@velum.net + * @package phing.util.regexp + */ +class PregEngine implements RegexpEngine { + + /** + * Set to null by default to distinguish between false and not set + * @var boolean + */ + private $ignoreCase = null; + + /** + * Set to null by default to distinguish between false and not set + * @var boolean + */ + private $multiline = null; + + /** + * Pattern modifiers + * @link http://php.net/manual/en/reference.pcre.pattern.modifiers.php + * @var string + */ + private $modifiers = null; + + /** + * Sets pattern modifiers for regex engine + * + * @param string $mods Modifiers to be applied to a given regex + * @return void + */ + public function setModifiers($mods) { + $this->modifiers = (string)$mods; + } + + /** + * Gets pattern modifiers. + * @return string + */ + public function getModifiers() { + $mods = $this->modifiers; + if($this->getIgnoreCase()) { + $mods .= 'i'; + } elseif($this->getIgnoreCase() === false) { + $mods = str_replace('i', '', $mods); + } + if($this->getMultiline()) { + $mods .= 's'; + } elseif($this->getMultiline() === false) { + $mods = str_replace('s', '', $mods); + } + // filter out duplicates + $mods = preg_split('//', $mods, -1, PREG_SPLIT_NO_EMPTY); + $mods = implode('', array_unique($mods)); + return $mods; + } + + /** + * Sets whether or not regex operation is case sensitive. + * @param boolean $bit + * @return void + */ + function setIgnoreCase($bit) { + $this->ignoreCase = (boolean) $bit; + } + + /** + * Gets whether or not regex operation is case sensitive. + * @return boolean + */ + function getIgnoreCase() { + return $this->ignoreCase; + } + + /** + * Sets whether regexp should be applied in multiline mode. + * @param boolean $bit + */ + function setMultiline($bit) { + $this->multiline = $bit; + } + + /** + * Gets whether regexp is to be applied in multiline mode. + * @return boolean + */ + function getMultiline() { + return $this->multiline; + } + + /** + * The pattern needs to be converted into PREG style -- which includes adding expression delims & any flags, etc. + * @param string $pattern + * @return string prepared pattern. + */ + private function preparePattern($pattern) + { + // Use backquotes since hardly ever found in a regexp pattern, avoids using preg_quote + return '`'.$pattern.'`' . $this->getModifiers(); + } + + /** + * Matches pattern against source string and sets the matches array. + * @param string $pattern The regex pattern to match. + * @param string $source The source string. + * @param array $matches The array in which to store matches. + * @return boolean Success of matching operation. + */ + function match($pattern, $source, &$matches) { + return preg_match($this->preparePattern($pattern), $source, $matches); + } + + /** + * Matches all patterns in source string and sets the matches array. + * @param string $pattern The regex pattern to match. + * @param string $source The source string. + * @param array $matches The array in which to store matches. + * @return boolean Success of matching operation. + */ + function matchAll($pattern, $source, &$matches) { + return preg_match_all($this->preparePattern($pattern), $source, $matches); + } + + /** + * Replaces $pattern with $replace in $source string. + * References to \1 group matches will be replaced with more preg-friendly + * $1. + * @param string $pattern The regex pattern to match. + * @param string $replace The string with which to replace matches. + * @param string $source The source string. + * @return string The replaced source string. + */ + function replace($pattern, $replace, $source) { + // convert \1 -> $1, because we want to use the more generic \1 in the XML + // but PREG prefers $1 syntax. + $replace = preg_replace('/\\\(\d+)/', '\$$1', $replace); + return preg_replace($this->preparePattern($pattern), $replace, $source); + } + +} + diff --git a/buildscripts/phing/classes/phing/util/regexp/Regexp.php b/buildscripts/phing/classes/phing/util/regexp/Regexp.php new file mode 100755 index 00000000..7188997e --- /dev/null +++ b/buildscripts/phing/classes/phing/util/regexp/Regexp.php @@ -0,0 +1,203 @@ +<?php +/* + * $Id: b669eb9f2dd8533cba67b2058b7cbc2f558bdeae $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * A factory class for regex functions. + * @author Hans Lellelid <hans@xmpl.org> + * @package phing.util.regexp + * @version $Id$ + */ +class Regexp { + + /** + * Matching groups found. + * @var array + */ + private $groups = array(); + + /** + * Pattern to match. + * @var string + */ + private $pattern; + + /** + * Replacement pattern. + * @var string + */ + private $replace; + + /** + * The regex engine -- e.g. 'preg' or 'ereg'; + * @var RegexpEngine + */ + private $engine; + + /** + * Constructor sets the regex engine to use (preg by default). + * @param string $_engineType The regex engine to use. + */ + function __construct($engineType='preg') { + if ($engineType == 'preg') { + include_once 'phing/util/regexp/PregEngine.php'; + $this->engine = new PregEngine(); + } elseif ($engineType == 'ereg') { + include_once 'phing/util/regexp/EregEngine.php'; + $this->engine = new EregEngine(); + } else { + throw new BuildException("Invalid engine type for Regexp: " . $engineType); + } + } + + /** + * Sets pattern to use for matching. + * @param string $pat The pattern to match on. + * @return void + */ + public function setPattern($pat) { + $this->pattern = (string) $pat; + } + + + /** + * Gets pattern to use for matching. + * @return string The pattern to match on. + */ + public function getPattern() { + return $this->pattern; + } + + /** + * Sets replacement string. + * @param string $rep The pattern to replace matches with. + * @return void + */ + public function setReplace($rep) { + $this->replace = (string) $rep; + } + + /** + * Gets replacement string. + * @return string The pattern to replace matches with. + */ + public function getReplace() { + return $this->replace; + } + + /** + * Performs match of specified pattern against $subject. + * @param string $subject The subject, on which to perform matches. + * @return boolean Whether or not pattern matches subject string passed. + */ + public function matches($subject) { + if($this->pattern === null) { + throw new Exception("No pattern specified for regexp match()."); + } + return $this->engine->match($this->pattern, $subject, $this->groups); + } + + /** + * Performs replacement of specified pattern and replacement strings. + * @param string $subject Text on which to perform replacement. + * @return string subject after replacement has been performed. + */ + public function replace($subject) { + if ($this->pattern === null || $this->replace === null) { + throw new Exception("Missing pattern or replacement string regexp replace()."); + } + return $this->engine->replace($this->pattern, $this->replace, $subject); + } + + /** + * Get array of matched groups. + * @return array Matched groups + */ + function getGroups() { + return $this->groups; + } + + /** + * Get specific matched group. + * @param integer $idx + * @return string specified group or NULL if group is not set. + */ + function getGroup($idx) { + if (!isset($this->groups[$idx])) { + return null; + } + return $this->groups[$idx]; + } + + /** + * Sets pattern modifiers for regex engine + * + * @param string $mods Modifiers to be applied to a given regex + * @return void + */ + public function setModifiers($mods) { + $this->engine->setModifiers($mods); + } + + /** + * Gets pattern modifiers. + * Subsequent call to engines getModifiers() filters out duplicates + * i.e. if i is provided in $mods, and setIgnoreCase(true), "i" + * modifier would be included only once + * @return string + */ + public function getModifiers() { + return $this->engine->getModifiers(); + } + + /** + * Sets whether the regexp matching is case insensitive. + * (default is false -- i.e. case sensisitive) + * @param boolean $bit + */ + function setIgnoreCase($bit) { + $this->engine->setIgnoreCase($bit); + } + + /** + * Gets whether the regexp matching is case insensitive. + * @return boolean + */ + function getIgnoreCase() { + return $this->engine->getIgnoreCase(); + } + + /** + * Sets whether regexp should be applied in multiline mode. + * @param boolean $bit + */ + function setMultiline($bit) { + $this->engine->setMultiline($bit); + } + + /** + * Gets whether regexp is to be applied in multiline mode. + * @return boolean + */ + function getMultiline() { + return $this->engine->getMultiline(); + } +} + diff --git a/buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php b/buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php new file mode 100755 index 00000000..3eb8c408 --- /dev/null +++ b/buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php @@ -0,0 +1,73 @@ +<?php +/* + * $Id: 5e2886f3fae60fff1fd142e79717a3a7a4555772 $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +/** + * Contains some shared attributes and methods -- and some abstract methods with + * engine-specific implementations that sub-classes must override. + * + * @author Hans Lellelid <hans@velum.net> + * @package phing.util.regexp + * @version $Id$ + */ +interface RegexpEngine { + + /** + * Sets whether or not regex operation should ingore case. + * @param boolean $bit + * @return void + */ + public function setIgnoreCase($bit); + + /** + * Returns status of ignore case flag. + * @return boolean + */ + public function getIgnoreCase(); + + /** + * Matches pattern against source string and sets the matches array. + * @param string $pattern The regex pattern to match. + * @param string $source The source string. + * @param array $matches The array in which to store matches. + * @return boolean Success of matching operation. + */ + function match($pattern, $source, &$matches); + + /** + * Matches all patterns in source string and sets the matches array. + * @param string $pattern The regex pattern to match. + * @param string $source The source string. + * @param array $matches The array in which to store matches. + * @return boolean Success of matching operation. + */ + function matchAll($pattern, $source, &$matches); + + /** + * Replaces $pattern with $replace in $source string. + * @param string $pattern The regex pattern to match. + * @param string $replace The string with which to replace matches. + * @param string $source The source string. + * @return string The replaced source string. + */ + function replace($pattern, $replace, $source); + +} + |