/** * 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; }
/** * Executes this target. */ public function main() { if ($this->name === null) { throw new BuildException('The name attribute must be specified'); } /* Words cannot describe how ridiculously f*****g stupid this is. Phing * seems to resolve properties only once, ever, so in order to run a * target multiple times with different properties we'll have to create * a new project, parse the build file all over again, copy everything * over from the current project, execute the new target, and then copy * everything back. F**k. */ $project = new Project(); try { foreach ($this->project->getBuildListeners() as $listener) { $project->addBuildListener($listener); } $project->setInputHandler($this->project->getInputHandler()); $this->project->copyUserProperties($project); $this->project->copyInheritedProperties($project); foreach ($this->project->getProperties() as $name => $property) { if ($project->getProperty($name) === null) { $project->setNewProperty($name, $property); } } $project->init(); ProjectConfigurator::configureProject($project, new PhingFile($this->project->getProperty('phing.file'))); Phing::setCurrentProject($project); $project->executeTarget($this->name); } catch (BuildException $be) { if ($this->exceptionsFatal) { throw $be; } else { $this->log('Ignoring build exception: ' . $be->getMessage(), Project::MSG_WARN); $this->log('Continuing build', Project::MSG_INFO); } } Phing::setCurrentProject($this->project); /** * :NOTE: copy all user properties so that child task may overwrite * properties that were already set in the parent project * * using the Project::copyUserProperties() will result in * properties not adjusted to the new value. */ foreach ($project->getUserProperties() as $name => $property) { $this->project->setUserProperty($name, $property); } $project->copyInheritedProperties($this->project); foreach ($project->getProperties() as $name => $property) { if ($this->project->getProperty($name) === null) { $this->project->setNewProperty($name, $property); } } /* F**k. */ unset($project); }
/** * @return void * @throws \BuildException */ public function main() { if (realpath($this->pathToChainFolder) === false) { throw new \BuildException('invalid path to chain folder'); } foreach ($this->chains as $chain) { $file = new \PhingFile($this->pathToChainFolder . '/' . $chain . '.xml'); \ProjectConfigurator::configureProject($this->project, $file); } }
/** * 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; }
/** * Returns entire SQL source * * @return string|null */ public function nextQuery() { $sql = null; 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; } $sql .= " " . $line . "\n"; } return $sql; }
protected function initPhingProperties(Project $project) { // Apply all file properties, then all non-file properties $properties = new Properties(); foreach ($this->fileProps as $key => $value) { $properties->put($key, $value); } foreach ($this->customProps as $key => $value) { $properties->put($key, $value); } // Then swap out placeholder values foreach ($properties->getProperties() as $key => $value) { $value = ProjectConfigurator::replaceProperties($project, $value, $properties->getProperties()); $project->setProperty($key, $value); } }
/** * Returns entire SQL source * * @return string|null */ public function nextQuery() { $sql = null; 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; } $sql .= " " . $line . "\n"; /** * fix issue with PDO and wrong formated multistatements * @issue 1108 */ if (StringHelper::endsWith($delimiter, $line)) { break; } } return $sql; }
/** * Executes this task. */ public function main() { if ($this->file === null) { throw new BuildException('The file attribute must be specified'); } $return = getcwd(); try { /* Resolve paths correctly: Everything we do as far as * configuration is concerned should be relative to the * new project file. */ chdir($this->file->getAbsoluteFile()->getParent()); $project = new AgaviProxyProject($this->project); $project->addReference('phing.parsing.context', new AgaviProxyXmlContext($project)); $project->setUserProperty('phing.file', $this->file->getAbsolutePath()); $project->init(); Phing::setCurrentProject($project); ProjectConfigurator::configureProject($project, $this->file); foreach ($project->getTargets() as $name => $target) { /* Make sure we don't add proxy targets back to our own project. */ if ($target instanceof AgaviProxyTarget && $target->getTarget()->getProject() === $this->project) { continue; } if (array_key_exists($name, $this->project->getTargets())) { throw new BuildException(sprintf('Target conflict: %s already exists in project (attempted to add from %s)', $name, $this->file->getAbsolutePath())); } $proxy = new AgaviProxyTarget(); $proxy->setName($name); $proxy->setDescription($target->getDescription()); $proxy->setTarget($target); $this->project->addTarget($name, $proxy); } Phing::setCurrentProject($this->project); $this->log(sprintf('Importing external build file %s', $this->file->getAbsolutePath()), Project::MSG_INFO); } catch (Exception $e) { $this->log(sprintf('Could not read %s: %s (skipping)', $this->file->getAbsolutePath(), $e->getMessage()), Project::MSG_WARN); } /* Go back from whence we came. */ chdir($return); }
/** * 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); $path = (string) $this->configurator->getBuildFile(); $project->setUserProperty("phing.file.{$canonicalName}", $path); $project->setUserProperty("phing.dir.{$canonicalName}", dirname($path)); } 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()))); } } } } }
/** * set up to run the named project * * @param filename name of project file to run * @throws BuildException */ protected function configureProject($filename) { $this->logBuffer = ""; $this->fullLogBuffer = ""; $this->project = new Project(); $this->project->init(); $f = new PhingFile($filename); $this->project->setUserProperty("phing.file", $f->getAbsolutePath()); $this->project->addBuildListener(new PhingTestListener($this)); ProjectConfigurator::configureProject($this->project, new PhingFile($filename)); }
/** * Replace ${} style constructions in the given value with the * string value of the corresponding data types. This method is * static. * * @param object the project that should be used for property look-ups * @param string the string to be scanned for property references * @param array proeprty keys * @return string the replaced string or <code>null</code> if the string * itself was null */ public static function replaceProperties(Project $project, $value, $keys) { 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; // 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 = preg_replace_callback('/\\$\\{([^}]+)\\}/', array('ProjectConfigurator', 'replacePropertyCallback'), $value); return $sb; }
$GLOBALS['LOGGER'] = Phing::import($GLOBALS['LOGGER']); $logger = new AgaviProxyBuildLogger(new $GLOBALS['LOGGER']()); $logger->setMessageOutputLevel($GLOBALS['VERBOSE'] ? Project::MSG_VERBOSE : Project::MSG_INFO); $logger->setOutputStream($GLOBALS['OUTPUT']); $logger->setErrorStream($GLOBALS['ERROR']); $project->addBuildListener($logger); $project->setInputHandler(new DefaultInputHandler()); $project->setUserProperty('phing.file', $GLOBALS['BUILD']->getAbsolutePath()); $project->setUserProperty('phing.version', Phing::getPhingVersion()); /* Phing f***s with the cwd. Really, brilliant. */ $project->setUserProperty('application.startdir', START_DIRECTORY); foreach ($GLOBALS['PROPERTIES'] as $name => $value) { $project->setUserProperty($name, $value); } $project->init(); ProjectConfigurator::configureProject($project, $GLOBALS['BUILD']); Phing::setCurrentProject($project); if ($GLOBALS['SHOW_LIST'] === true) { input_help_display(); $GLOBALS['OUTPUT']->write(PHP_EOL); $GLOBALS['OUTPUT']->write('Targets:' . PHP_EOL); $size = 0; $targets = array(); foreach ($project->getTargets() as $target) { $name = $target->getName(); $nameSize = strlen($name); $description = $target->getDescription(); if ($description !== null) { $size = $nameSize > $size ? $nameSize : $size; $targets[$name] = $description; }
/** * 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(); }
/** * 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."); } }
/** * Parse a Phing build file and copy the properties, tasks, data types and * targets it defines into the current project. * * @throws BuildException * @return void */ protected function importFile(PhingFile $file) { $ctx = $this->project->getReference("phing.parsing.context"); $cfg = $ctx->getConfigurator(); // 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 file from {$file->getAbsolutePath()}", Project::MSG_VERBOSE); ProjectConfigurator::configureProject($this->project, $file); }
/** * 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); } }
/** * Parse a Phing build file and copy the properties, tasks, data types and * targets it defines into the current project. * * @throws BuildException * @return void */ public function main() { if (!isset($this->file)) { throw new BuildException("Missing attribute 'file'"); } if ($this->getOwningTarget() == null || $this->getOwningTarget()->getName() != '') { throw new BuildException("import only allowed as a top-level task"); } $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(); // 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 file from {$file->getAbsolutePath()}", Project::MSG_VERBOSE); ProjectConfigurator::configureProject($this->project, $file); }
/** * read in lines and execute them * @throws SQLException, IOException */ public function runStatements(Reader $reader, $out = null) { $sql = ""; $line = ""; $in = new BufferedReader($reader); try { while (($line = $in->readLine()) !== null) { $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) - 1), $out); $sql = ""; } } // Catch any statements not followed by ; if ($sql !== "") { $this->execSQL($sql, $out); } } catch (SQLException $e) { throw new BuildException("Error running statements", $e); } }
/** * read in lines and execute them * @param Reader $reader * @param null $out * @throws BuildException */ 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); } }
/** * Read the statements from the .sql file and execute them. * Lines starting with '//', '--' or 'REM ' are ignored. * * Developer note: must be public in order to be called from * sudo-"inner" class PropelSQLExecTransaction. * * @param Reader $reader * @param $out Optional output stream. * @throws PDOException * @throws IOException */ public function runStatements(Reader $reader, $out = null) { $sql = ""; $line = ""; $sqlBacklog = ""; $hasQuery = false; $in = new BufferedReader($reader); $parser['pointer'] = 0; $parser['isInString'] = false; $parser['stringQuotes'] = ""; $parser['backslashCount'] = 0; $parser['parsedString'] = ""; $sqlParts = array(); while (($line = $in->readLine()) !== null) { $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; } if ($sqlBacklog !== "") { $sql = $sqlBacklog; $sqlBacklog = ""; } $sql .= " " . $line . PHP_EOL; // 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 .= PHP_EOL; } // DELIM_ROW doesn't need this (as far as i can tell) if ($this->delimiterType == self::DELIM_NORMAL) { // old regex, being replaced due to segfaults: // See: http://propel.phpdb.org/trac/ticket/294 //$reg = "#((?:\"(?:\\\\.|[^\"])*\"?)+|'(?:\\\\.|[^'])*'?|" . preg_quote($this->delimiter) . ")#"; //$sqlParts = preg_split($reg, $sql, 0, PREG_SPLIT_DELIM_CAPTURE); $i = $parser['pointer']; $c = strlen($sql); while ($i < $c) { $char = $sql[$i]; switch ($char) { case "\\": $parser['backslashCount']++; $this->log("c{$i}: found " . $parser['backslashCount'] . " backslash(es)", Project::MSG_VERBOSE); break; case "'": case "\"": if ($parser['isInString'] && $parser['stringQuotes'] == $char) { if (($parser['backslashCount'] & 1) == 0) { #$this->log("$i: out of string", Project::MSG_VERBOSE); $parser['isInString'] = false; } else { $this->log("c{$i}: rejected quoted delimiter", Project::MSG_VERBOSE); } } elseif (!$parser['isInString']) { $parser['stringQuotes'] = $char; $parser['isInString'] = true; #$this->log("$i: into string with $parser['stringQuotes']", Project::MSG_VERBOSE); } break; } if ($char == $this->delimiter && !$parser['isInString']) { $this->log("c{$i}: valid end of command found!", Project::MSG_VERBOSE); $sqlParts[] = $parser['parsedString']; $sqlParts[] = $this->delimiter; break; } $parser['parsedString'] .= $char; if ($char !== "\\") { if ($parser['backslashCount']) { $this->log("{$i}: backslash reset", Project::MSG_VERBOSE); } $parser['backslashCount'] = 0; } $i++; $parser['pointer']++; } $sqlBacklog = ""; foreach ($sqlParts as $sqlPart) { // we always want to append, even if it's a delim (which will be stripped off later) $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 === $this->delimiter) { $sql = $sqlBacklog; $sqlBacklog = ""; $hasQuery = true; } } } if ($hasQuery || $this->delimiterType == self::DELIM_ROW && $line == $this->delimiter) { // this assumes there is always a delimter on the end of the SQL statement. $sql = StringHelper::substring($sql, 0, strlen($sql) - 1 - strlen($this->delimiter)); $this->log("SQL: " . $sql, Project::MSG_VERBOSE); $this->execSQL($sql, $out); $sql = ""; $hasQuery = false; $parser['pointer'] = 0; $parser['isInString'] = false; $parser['stringQuotes'] = ""; $parser['backslashCount'] = 0; $parser['parsedString'] = ""; $sqlParts = array(); } } // Catch any statements not followed by ; if ($sql !== "") { $this->execSQL($sql, $out); } }
/** * 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; }
/** * 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 $tag * @param $attrs * @throws BuildException * @throws ExpatParseException * @internal param the $string tag that comes in * @internal param attributes $array the tag carries */ public function init($tag, $attrs) { $name = null; $depends = ""; $ifCond = null; $unlessCond = null; $id = null; $description = null; $isHidden = false; $logskipped = false; foreach ($attrs as $key => $value) { switch ($key) { case "name": $name = (string) $value; break; case "depends": $depends = (string) $value; break; case "if": $ifCond = (string) $value; break; case "unless": $unlessCond = (string) $value; break; case "id": $id = (string) $value; break; case "hidden": $isHidden = $value == 'true' || $value == '1' ? true : false; break; case "description": $description = (string) $value; break; case "logskipped": $logskipped = $value; break; default: 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->context->getCurrentTargets[$name])) { throw new BuildException("Duplicate target: {$name}", $this->parser->getLocation()); } $this->target = new Target(); $this->target->setHidden($isHidden); $this->target->setIf($ifCond); $this->target->setUnless($unlessCond); $this->target->setDescription($description); $this->target->setLogSkipped(StringHelper::booleanValue($logskipped)); // take care of dependencies if (strlen($depends) > 0) { $this->target->setDepends($depends); } // check to see if target with same name is already defined $projectTargets = $project->getTargets(); if (isset($projectTargets[$name])) { 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; $project->log("Already defined in main or a previous import, " . "define {$name} as {$newName}", Project::MSG_VERBOSE); $name = $newName; } else { $project->log("Already defined in main or a previous import, " . "ignore {$name}", Project::MSG_VERBOSE); $name = null; } } if ($name != null) { $this->target->setName($name); $project->addOrReplaceTarget($name, $this->target); if ($id !== null && $id !== "") { $project->addReference($id, $this->target); } } }
/** * Replaces ${} style constructions in the given value with the * string value of the corresponding data types. * * @param string $value The value string to be scanned for property references. * May be <code>null</code>. * * @return string 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); }
/** * Read the statements from the .sql file and execute them. * Lines starting with '//', '--' or 'REM ' are ignored. * * Developer note: must be public in order to be called from * sudo-"inner" class PropelSQLExecTransaction. * * @param Reader $reader * @param $out Optional output stream. * @throws SQLException * @throws IOException */ public function runStatements(Reader $reader, $out = null) { $sql = ""; $line = ""; $sqlBacklog = ""; $hasQuery = false; $in = new BufferedReader($reader); try { while (($line = $in->readLine()) !== null) { $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; } if ($sqlBacklog !== "") { $sql = $sqlBacklog; $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 == self::DELIM_NORMAL) { $reg = "#((?:\"(?:\\\\.|[^\"])*\"?)+|'(?:\\\\.|[^'])*'?|" . preg_quote($this->delimiter) . ")#"; $sqlParts = preg_split($reg, $sql, 0, PREG_SPLIT_DELIM_CAPTURE); $sqlBacklog = ""; foreach ($sqlParts as $sqlPart) { // we always want to append, even if it's a delim (which will be stripped off later) $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 === $this->delimiter) { $sql = $sqlBacklog; $sqlBacklog = ""; $hasQuery = true; } } } if ($hasQuery || $this->delimiterType == self::DELIM_ROW && $line == $this->delimiter) { // this assumes there is always a delimter on the end of the SQL statement. $sql = StringHelper::substring($sql, 0, strlen($sql) - 1 - strlen($this->delimiter)); $this->log("SQL: " . $sql, PROJECT_MSG_VERBOSE); $this->execSQL($sql, $out); $sql = ""; $hasQuery = false; } } // Catch any statements not followed by ; if ($sql !== "") { $this->execSQL($sql, $out); } } catch (SQLException $e) { throw $e; } }
/** * 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; }
/** 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"]; } $this->attributes = null; 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())); } } }
/** * 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++]; }
/** * 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); } }