versions node to make method work shorter. * @var object SimpleXMLElement */ protected $_vers; /** * @var object simpleXMLElement */ protected $_xml; /** * Class constructor initialises the SimpleXML object and sets a few properties. * @param string $filepath Optional filespec for the XML file to use. Will use default otherwise. * * @throws \Exception If the XML is invalid. * @throws \RuntimeException If version file does not exist. */ public function __construct($filepath = null) { if (empty($filepath)) { if (defined('NN_VERSIONS')) { $filepath = NN_VERSIONS; } } if (! file_exists($filepath)) { throw new \RuntimeException("Versions file '$filepath' does not exist!'"); } $this->_filespec = $filepath; $this->out = new ColorCLI(); $this->git = new Git(); $this->getValidVersionsFile(); } public function changes(): int { return $this->_changes; } /** * Run all checks. * @param bool $update Whether the XML should be updated by the check. * @return bool True if any of the checks actually caused an update (not if it indicated one was needed), flase otherwise */ public function checkAll($update = true) { $this->checkGitTag($update); $this->checkSQLDb($update); $this->checkGitCommit($update); return $this->hasChanged(); } /** * Checks the git commit number against the XML's stored value. * @param bool $update Whether the XML should be updated by the check. * @return int|bool The new git commit number, or false. */ public function checkGitCommit($update = true) { // Since Dec 2014 we no longer maintain the git commit count in the XML file, as it is no // longer used in the code base. if ((int) $this->_vers->sql->db >= 307) { return 0; } $count = $this->git->commits(); if (GIT_PRE_COMMIT === true || $this->_vers->git->commit->__toString() < $count) { // Allow pre-commit to override the commit number (often branch number is higher than dev's) if ($update) { if (GIT_PRE_COMMIT === true) { // only the pre-commit script is allowed to set the NEXT commit number $count++; } if ($count !== $this->_vers->git->commit) { echo ColorCLI::primary("Updating commit number to {$count}"); $this->_vers->git->commit = $count; $this->_changes |= self::UPDATED_GIT_COMMIT; } } return $this->_vers->git->commit; } return false; } /** * Checks the git's latest version tag against the XML's stored value. Version should be * Major.Minor.Revision[.fix] (**commit number is NOT revision**). * @param bool $update Whether the XML should be updated by the check. * @return bool The new git's latest version tag, or false. */ public function checkGitTag($update = true) { trigger_error( 'This method is deprecated. Use app/extensions/utils/Versions::checkGitTag() instead.' ); $branch = $this->git->getBranch(); $this->_gitHighestTag = $latest = trim($this->git->tagLatest()); $ver = preg_match('#v(\d+\.\d+\.\d+).*#', $latest, $matches) ? $matches[1] : $latest; if (! in_array($branch, $this->_stable, false)) { if (version_compare($this->_vers->git->tag, '0.0.0', '!=')) { $this->_vers->git->tag = '0.0.0'; $this->_changes |= self::UPDATED_GIT_TAG; } return $this->_vers->git->tag; } // Check if version file's entry is the same as current branch's tag if (version_compare($this->_vers->git->tag, $latest, '!=')) { if ($update) { echo ColorCLI::primaryOver('Updating tag version to ').ColorCLI::headerOver($latest); $this->_vers->git->tag = $ver; $this->_changes |= self::UPDATED_GIT_TAG; } else { echo ColorCLI::primaryOver('Leaving tag version at '). ColorCLI::headerOver($this->_vers->git->tag); } return $this->_vers->git->tag; } else { echo ColorCLI::primaryOver('Tag version is ').ColorCLI::header($latest); } return false; } /** * Checks the database sqlpatch setting against the XML's stored value. * * @param bool $update Whether the XML should be updated by the check. * * @return bool The new database sqlpatch version, or false. */ public function checkSQLDb($update = false): bool { $this->checkSQLFileLatest($update); if ($this->_vers->sql->db->__toString() !== $this->_vers->sql->file->__toString()) { if ($update) { echo ColorCLI::primaryOver('Updating Db revision to '.$this->_vers->sql->file); $this->_vers->sql->db = $this->_vers->sql->file->__toString(); $this->_changes |= self::UPDATED_SQL_DB_PATCH; } return $this->_vers->patch->db; } return false; } /** * Checks the numeric value from the last SQL patch file, updating the versions file if desired. * * @param bool $update Whether to update the versions file. * * @return bool|int False if there is a problem, otherwise the number from the last patch file. */ public function checkSQLFileLatest($update = true) { $options = [ 'data' => NN_RES.'db'.DS.'schema'.DS.'data'.DS, 'ext' => 'sql', 'path' => NN_RES.'db'.DS.'patches'.DS.'mysql', 'regex' => '#^'.Utility::PATH_REGEX.'(?P\d{4})~(?P\w+)\.sql$#', 'safe' => true, ]; $files = Utility::getDirFiles($options); natsort($files); $last = preg_match($options['regex'], end($files), $matches) ? (int) $matches['patch'] : false; if ($update) { if ($last !== false && $this->_vers->sql->file->__toString() !== $last) { echo ColorCLI::primary('Updating latest patch file to '.$last); $this->_vers->sql->file = $last; $this->_changes |= self::UPDATED_SQL_FILE_LAST; } if ($this->_vers->sql->file->__toString() !== $last) { $this->_vers->sql->file = $last; $this->_changes |= self::UPDATED_SQL_DB_PATCH; } } return $last; } public function getCommit() { return $this->_vers->git->commit->__toString(); } public function getGitHookPrecommit() { return $this->_vers->git->hooks->precommit->__toString(); } public function getSQLPatchFromDb() { return $this->_vers->sql->db->__toString(); } public function getSQLPatchFromFiles() { return $this->_vers->sql->file->__toString(); } public function getTagVersion() { if (empty($this->_gitHighestTag)) { $this->checkGitTag(); } return $this->_gitHighestTag; } /** * @param null|string $filepath * * @return object|\SimpleXMLElement * @throws \Exception */ public function getValidVersionsFile($filepath = null) { $filepath = $filepath ?? $this->_filespec; $temp = libxml_use_internal_errors(true); $this->_xml = simplexml_load_string(file_get_contents($filepath)); libxml_use_internal_errors($temp); if ($this->_xml === false) { if (Utility::isCLI()) { ColorCLI::error("Your versions XML file ($filepath) is broken, try updating from git."); } throw new \RuntimeException("Failed to open versions XML file '$filepath'"); } if ($this->_xml->count() > 0) { $vers = $this->_xml->xpath('/nntmux/versions'); if ($vers[0]->count() === 0) { ColorCLI::error('Your versions XML file ({NN_VERSIONS}) does not contain version info, try updating from git.'); throw new \RuntimeException("Failed to find versions node in XML file '$filepath'"); } ColorCLI::primary('Your versions XML file ({NN_VERSIONS}) looks okay, continuing.'); $this->_vers = &$this->_xml->versions; } else { throw new \RuntimeException("No elements in file!\n"); } return $this->_xml; } /** * Check whether the XML has been changed by one of the methods here. * @return bool True if the XML has been changed. */ public function hasChanged(): bool { return $this->_changes !== 0; } public function save(): void { if ($this->hasChanged()) { $this->_xml->asXML($this->_filespec); $this->_changes = 0; } } }