array(), 'nodes' => array()); // ################################################################### /** * Constructor: initialies the registry * * @param object Controller */ function cacheV(&$controller) { $this->controller =& $controller; $this->set_hash(); } // ################################################################### /** * Sets the hash so we know what table we're dealing with * * @access public */ function set_hash() { $this->hash = md5($this->controller->repospath); } // ################################################################### /** * Returns a node string that has the beginning and ending slashes * removed to allow it to match to the _nodes cacheV table * * @access public * * @param string Original string * * @return string Matchable string */ function fetch_node_string($node) { return preg_replace('#(^/|/$)#', '', $node); } // ################################################################### /** * Returns a specific log entry * * @access public * * @param integer Revision number * * @return array Complete revision/commit entry */ function fetch_revision($revision) { $revision = $this->controller->registry->clean($revision, TYPE_UINT); if (!isset($this->memcache['revs']["$revision"])) { $this->memcache['revs']["$revision"] = $this->controller->registry->db->query_first("SELECT * FROM {$this->hash}_revs " . ($revision == 0 ? " ORDER BY revision DESC LIMIT 1" : "WHERE revision = $revision")); $this->memcache['revs']["$revision"]['files'] = unserialize($this->memcache['revs']["$revision"]['files']); } return $this->memcache['revs']["$revision"]; } // ################################################################### /** * Returns the revision entry before the specified one * * @access public * * @param string Node path * @param integer Revision number * * @return array Complete revision/commit entry */ function fetch_prev_revision($node, $revision) { $data = $this->fetch_node($node); $data = $data['history']; if (count($data['history']) < 1) { return $this->fetch_revision(0); } unset($data[ max(array_keys($data)) ]); return $this->fetch_revision(max(array_keys($data))); } // ################################################################### /** * Fetches the latest revision for a given path * * @access public * * @param string Node path * * @return integer Latest revision; FALSE if none (not in HEAD) */ function fetch_node($node) { $node = $this->fetch_node_string($node); if (!isset($this->memcache['nodes']["$node"])) { $result = $this->controller->registry->db->query_first("SELECT * FROM {$this->hash}_nodes WHERE name = '" . $this->controller->registry->escape($node) . "'"); if ($result == false) { return false; } $this->memcache['nodes']["$node"] = $result; $this->memcache['nodes']["$node"]['history'] = unserialize($this->memcache['nodes']["$node"]['history']); } return $this->memcache['nodes']["$node"]; } // ################################################################### /** * Checks to see if a given node is a directory. Returns TRUE if so. * * @access public * * @param string Node path * * @return bool TRUE if directory, FALSE if not */ function isdir($node) { $node = $this->fetch_node($node); if ($node['node'] == 'dir') { return true; } return false; } // ################################################################### /** * Checks to see if it's necessary to rebuild the cacheV table for the * current repository. This is done by making sure $count > 0. If not, * then rebuild() is run. This also checks against the cacheV table * to make sure that it's up-to-date against the root repository. * * @access public */ function exec_build() { $result = $this->controller->registry->db->query_first("SELECT MAX(revision) AS max FROM {$this->hash}_revs"); $this->count = $result['max']; // time to go from the start if ($this->count == 0) { $this->build(null); } else { // send an Xquery to SVN to see if we need to update $query = $this->controller->library->svn('info --xml ' . $this->controller->repospath); $query = implode("\n", $query); $tree = $this->controller->registry->xml->parse($query); if ($tree['info']['entry']['revision'] != $this->count) { $this->build($this->count); } } } // ################################################################### /** * Builds the cacheV table. This can be used to build only part of the * cache or the entire thing, if the revision is set to NULL. * * @access public * * @param integer Lower (current) revision */ function build($revision) { $start = microtime(); // get _revs $output = $this->controller->registry->svn->svn('log --xml -v ' . ($revision !== null ? '-r' . $revision . ':HEAD ' : '') . $this->controller->registry->repos->fetch_path($this->controller->registry->paths->repos)); $output = implode("\n", $output); $tree = $this->controller->registry->xml->parse($output); // get _nodes $output = $this->controller->registry->svn->svn('info --xml -R ' . ($revision !== null ? '-r' . $revision . ':HEAD ' : '') . $this->controller->registry->repos->fetch_path($this->controller->registry->paths->repos)); $output = implode("\n", $output); $infolist = $this->controller->registry->xml->parse($output); // other part of _nodes: properties $output = $this->controller->registry->svn->svn('proplist -v -R ' . ($revision !== null ? ' -r' . $revision . ':HEAD ' : '') . $this->controller->registry->repos->fetch_path($this->controller->registry->paths->repos)); foreach ($output AS $line) { if (preg_match('#^Properties on \'(.*?)\':$#', $line, $bits)) { $proplist["$index"]["$curprop"] = trim($proplist["$index"]["$curprop"]); $index = str_replace($this->controller->registry->repos->fetch_path($this->controller->registry->paths->repos), '', $bits[1]); $capture = false; } else { if (preg_match('#^\s+(.*)\s:\s(.*)#', $line, $matches)) { $curprop = $matches[1]; $proplist["$index"]["$curprop"] = $matches[2] . "\n"; $capture = true; } else if ($capture == true) { $proplist["$index"]["$curprop"] .= $line . "\n"; } } } // construct _revs inserts and the list of add revisions foreach ($tree['log']['logentry'] AS $log) { $this->controller->registry->xml->unify_node($log['paths']['path']); $inserts['revs'][] = "($log[revision], '{$log['author']['value']}', '{$log['date']['value']}', '" . $this->controller->registry->escape($log['msg']['value']) . "', '" . $this->controller->registry->escape(serialize($log['paths']['path'])) . "')"; foreach ($log['paths']['path'] AS $path) { if (trim($path['action']) == 'A') { $path['value'] = preg_replace('#^/#', '', $path['value']); $addlist["$path[value]"] = $log['revision']; } } } // construct list of HEAD nodes for _nodes foreach ($infolist['info']['entry'] AS $node) { $history = $this->controller->registry->svn->svn('log --xml ' . $node['url']['value']); $history = implode("\n", $history); $history = $this->controller->registry->xml->parse($history); $loglist = array(); $latestrev = -1; foreach ($history['log']['logentry'] AS $log) { $loglist["$log[revision]"] = array( 'revision' => $log['revision'], 'author' => $log['author']['value'], 'date' => $log['date']['value'], 'message' => $log['msg']['value'] ); } $inserts['nodes'][] = "('$node[path]', '" . $node['kind'] . "', " . $node['commit']['revision'] . ", '" . $this->controller->registry->escape(serialize($loglist)) . "', '" . $this->controller->registry->escape(serialize($proplist["$node[path]"])) . "')"; } // insert _revs $this->controller->registry->db->query(" REPLACE INTO {$this->hash}_revs (revision, author, dateline, message, files) VALUES " . implode(",\n", $inserts['revs']) ); // insert _nodes $this->controller->registry->db->query(" REPLACE INTO {$this->hash}_nodes (name, node, revision, history, properties) VALUES " . implode(",\n", $inserts['nodes']) ); $this->controller->registry->debug("TIME TO (RE)BUILD: " . $this->controller->registry->funct->fetch_microtime_diff($start)); } } /*=====================================================================*\ || ################################################################### || # $HeadURL$ || # $Id$ || ################################################################### \*=====================================================================*/ ?>