Project class is getting kind of big, reorganize a bit with code folds
Project class is getting kind of big, reorganize a bit with code folds

<?php <?php
/** /**
* GitPHP Config defaults * GitPHP Config defaults
* *
* Lists all the config options and their default values * Lists all the config options and their default values
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Config * @subpackage Config
*/ */
   
   
/** /**
* This file is not usable as an actual config file. * This file is not usable as an actual config file.
* To use a config value you should copy the value * To use a config value you should copy the value
* into gitphp.conf.php * into gitphp.conf.php
*/ */
throw new Exception('The defaults file should not be used as your config.'); throw new Exception('The defaults file should not be used as your config.');
   
   
/********************************************************* /*********************************************************
* Projects * Projects
*/ */
   
/* /*
* projectroot * projectroot
* Full directory on server where projects are located * Full directory on server where projects are located
*/ */
//$gitphp_conf['projectroot'] = '/pub/gitprojects/'; //$gitphp_conf['projectroot'] = '/pub/gitprojects/';
   
/* /*
* exportedonly * exportedonly
* When listing all projects in the project root, * When listing all projects in the project root,
* (not specifying any projects manually or using a project list file) * (not specifying any projects manually or using a project list file)
* set this to true to only allow repositories with the * set this to true to only allow repositories with the
* special file git-daemon-export-ok (see the git-daemon man page) * special file git-daemon-export-ok (see the git-daemon man page)
*/ */
$gitphp_conf['exportedonly'] = false; $gitphp_conf['exportedonly'] = false;
   
   
   
/********************************************************* /*********************************************************
* Appearance * Appearance
*/ */
   
/* /*
* locale * locale
* This is the default locale/language used in the interface. * This is the default locale/language used in the interface.
* The locale must exist in include/resources/locale * The locale must exist in include/resources/locale
*/ */
$gitphp_conf['locale'] = 'en_US'; $gitphp_conf['locale'] = 'en_US';
   
/* /*
* title * title
* The string that will be used as the page title * The string that will be used as the page title
* The variable '$gitphp_appstring' will expand to * The variable '$gitphp_appstring' will expand to
* the name (gitphp) and version * the name (gitphp) and version
* The variable '$gitphp_version' will expand to the * The variable '$gitphp_version' will expand to the
* version number only * version number only
*/ */
$gitphp_conf['title'] = "$gitphp_appstring"; $gitphp_conf['title'] = "$gitphp_appstring";
   
/* /*
* homelink * homelink
* This is the text of the link in the upper left corner * This is the text of the link in the upper left corner
* that takes you back to the project list. * that takes you back to the project list.
*/ */
$gitphp_conf['homelink'] = 'projects'; $gitphp_conf['homelink'] = 'projects';
   
/* /*
* cloneurl * cloneurl
* Sets the base clone url to display for a project. * Sets the base clone url to display for a project.
* This is the publicly-accessible url of the projectroot * This is the publicly-accessible url of the projectroot
* that gets prepended to the project path to create the clone * that gets prepended to the project path to create the clone
* url. It can be any format, for example: * url. It can be any format, for example:
* *
* http://server.com/ * http://server.com/
* ssh://server.com/git/ * ssh://server.com/git/
* git://server.com/gitprojects/ * git://server.com/gitprojects/
* *
* If left blank/commented, no clone url will display. * If left blank/commented, no clone url will display.
*/ */
$gitphp_conf['cloneurl'] = 'http://localhost/git/'; $gitphp_conf['cloneurl'] = 'http://localhost/git/';
   
/* /*
* pushurl * pushurl
* Sets the base push url to display for a project. * Sets the base push url to display for a project.
* Works the same as cloneurl. * Works the same as cloneurl.
*/ */
$gitphp_conf['pushurl'] = 'ssh://localhost/git/'; $gitphp_conf['pushurl'] = 'ssh://localhost/git/';
   
/* /*
* bugpattern * bugpattern
* Sets the regular expression to use to find bug number * Sets the regular expression to use to find bug number
* references in log messages. The pattern should have a * references in log messages. The pattern should have a
* group that extracts just the bug ID to pass to the * group that extracts just the bug ID to pass to the
* bug tracker. * bug tracker.
* For example, '/#([0-9+)/' will recognize any number * For example, '/#([0-9+)/' will recognize any number
* with a '#' in front of it, and groups the numeric part * with a '#' in front of it, and groups the numeric part
* only. Another common example is '/bug:([0-9]+)/' to * only. Another common example is '/bug:([0-9]+)/' to
* extract bug numbers with 'bug:' in front of them. * extract bug numbers with 'bug:' in front of them.
*/ */
//$gitphp_conf['bugpattern'] = '/#([0-9]+)/'; //$gitphp_conf['bugpattern'] = '/#([0-9]+)/';
   
/* /*
* bugurl * bugurl
* Sets the URL for the bug tracker. This URL must have * Sets the URL for the bug tracker. This URL must have
* a backreference to the group in the bug pattern that * a backreference to the group in the bug pattern that
* contains the ID. For example, ${1} uses the first * contains the ID. For example, ${1} uses the first
* group. * group.
*/ */
//$gitphp_conf['bugurl'] = 'http://localhost/mantis/view.php?id=${1}'; //$gitphp_conf['bugurl'] = 'http://localhost/mantis/view.php?id=${1}';
   
/* /*
* self * self
* This is the path to the script that will be inserted * This is the path to the script that will be inserted
* in urls. If you leave this blank/commented the script * in urls. If you leave this blank/commented the script
* will try to guess the correct URL, but you can override * will try to guess the correct URL, but you can override
* it here if it's not being guessed correctly. * it here if it's not being guessed correctly.
*/ */
$gitphp_conf['self'] = 'http://localhost/gitphp/'; $gitphp_conf['self'] = 'http://localhost/gitphp/';
   
/* /*
* stylesheet * stylesheet
* Path to look and feel (skin) stylesheet * Path to look and feel (skin) stylesheet
*/ */
$gitphp_conf['stylesheet'] = 'gitphpskin.css'; $gitphp_conf['stylesheet'] = 'gitphpskin.css';
   
/* /*
* javascript * javascript
* Toggles on javascript features * Toggles on javascript features
*/ */
$gitphp_conf['javascript'] = true; $gitphp_conf['javascript'] = true;
   
   
   
/********************************************************* /*********************************************************
* Features * Features
*/ */
   
/* /*
* compat * compat
* Set this to true to turn on compatibility mode. This will cause * Set this to true to turn on compatibility mode. This will cause
* GitPHP to rely more on the git executable for loading data, * GitPHP to rely more on the git executable for loading data,
* which will bypass some of the limitations of PHP at the expense * which will bypass some of the limitations of PHP at the expense
* of performance. * of performance.
* Turn this on if you are experiencing issues viewing data for * Turn this on if you are experiencing issues viewing data for
* your projects. * your projects.
*/ */
$gitphp_conf['compat'] = false; $gitphp_conf['compat'] = false;
   
/** /**
* largeskip * largeskip
* When GitPHP is reading through the history for pages of the shortlog/log * When GitPHP is reading through the history for pages of the shortlog/log
* beyond the first, it needs to read from the tip but skip a number of commits * beyond the first, it needs to read from the tip but skip a number of commits
* for the previous pages. The more commits it needs to skip, the longer it takes. * for the previous pages. The more commits it needs to skip, the longer it takes.
* Calling the git executable is faster when skipping a large number of commits, * Calling the git executable is faster when skipping a large number of commits,
* ie reading a log page significantly beyond the first. This determines * ie reading a log page significantly beyond the first. This determines
* the threshold at which GitPHP will fall back to using the git exe for the log. * the threshold at which GitPHP will fall back to using the git exe for the log.
* Currently each log page shows 100 commits, so this would be calculated at * Currently each log page shows 100 commits, so this would be calculated at
* page number * 100. So for example at the default of 200, pages 0-2 would be * page number * 100. So for example at the default of 200, pages 0-2 would be
* loaded natively and pages 3+ would fall back on the git exe. * loaded natively and pages 3+ would fall back on the git exe.
*/ */
$gitphp_conf['largeskip'] = 200; $gitphp_conf['largeskip'] = 200;
   
/* /*
* compressformat * compressformat
* Indicates what kind of compression will be done on the * Indicates what kind of compression will be done on the
* snapshot archive. Recognized settings are: * snapshot archive. Recognized settings are:
* *
* GITPHP_COMPRESS_BZ2 - create a tar.bz2 file (php must have bz2 support) * GITPHP_COMPRESS_BZ2 - create a tar.bz2 file (php must have bz2 support)
* GITPHP_COMPRESS_GZ - create a tar.gz file (php must have gzip support) * GITPHP_COMPRESS_GZ - create a tar.gz file (php must have gzip support)
* GITPHP_COMPRESS_ZIP - create a zip file * GITPHP_COMPRESS_ZIP - create a zip file
* *
* Any other setting, or no setting, will create uncompressed tar archives * Any other setting, or no setting, will create uncompressed tar archives
* If you choose a compression format and your php does not support it, * If you choose a compression format and your php does not support it,
* gitphp will fall back to uncompressed tar archives * gitphp will fall back to uncompressed tar archives
*/ */
$gitphp_conf['compressformat'] = GITPHP_COMPRESS_ZIP; $gitphp_conf['compressformat'] = GITPHP_COMPRESS_ZIP;
   
/* /*
* compresslevel * compresslevel
* Sets the compression level for snapshots. Ranges from 1-9, with * Sets the compression level for snapshots. Ranges from 1-9, with
* 9 being the most compression but requiring the most processing * 9 being the most compression but requiring the most processing
* (bzip defaults to 4, gzip defaults to -1) * (bzip defaults to 4, gzip defaults to -1)
*/ */
$gitphp_conf['compresslevel'] = 9; $gitphp_conf['compresslevel'] = 9;
   
/* /*
* geshi * geshi
* Run blob output through geshi syntax highlighting * Run blob output through geshi syntax highlighting
* and line numbering * and line numbering
*/ */
$gitphp_conf['geshi'] = true; $gitphp_conf['geshi'] = true;
   
/* /*
* search * search
* Set this to false to disable searching * Set this to false to disable searching
*/ */
$gitphp_conf['search'] = true; $gitphp_conf['search'] = true;
   
/* /*
* filesearch * filesearch
* Set this to false to disable searching within files * Set this to false to disable searching within files
* (it can be resource intensive) * (it can be resource intensive)
*/ */
$gitphp_conf['filesearch'] = true; $gitphp_conf['filesearch'] = true;
   
/* /*
* filemimetype * filemimetype
* Attempt to read the file's mimetype when displaying * Attempt to read the file's mimetype when displaying
* (for example, displaying an image as an actual image * (for example, displaying an image as an actual image
* in a browser) * in a browser)
* This requires either PHP >= 5.3.0, PECL fileinfo, or * This requires either PHP >= 5.3.0, PECL fileinfo, or
* Linux * Linux
*/ */
$gitphp_conf['filemimetype'] = true; $gitphp_conf['filemimetype'] = true;
   
   
   
   
/********************************************************* /*********************************************************
* Executable/filesystem options * Executable/filesystem options
* Important to check if you're running windows * Important to check if you're running windows
*/ */
   
/* /*
* gitbin * gitbin
* Path to git binary * Path to git binary
* For example, /usr/bin/git on Linux * For example, /usr/bin/git on Linux
* or C:\\Program Files\\Git\\bin\\git.exe on Windows * or C:\\Program Files\\Git\\bin\\git.exe on Windows
* with msysgit. You can also omit the full path and just * with msysgit. You can also omit the full path and just
* use the executable name to search the user's $PATH. * use the executable name to search the user's $PATH.
* Note: Versions of PHP below 5.2 have buggy handling of spaces * Note: Versions of PHP below 5.2 have buggy handling of spaces
* in paths. Use the 8.3 version of the filename if you're * in paths. Use the 8.3 version of the filename if you're
* having trouble, e.g. C:\\Progra~1\\Git\\bin\\git.exe * having trouble, e.g. C:\\Progra~1\\Git\\bin\\git.exe
*/ */
// Linux: // Linux:
$gitphp_conf['gitbin'] = 'git'; $gitphp_conf['gitbin'] = 'git';
// Windows (msysgit): // Windows (msysgit):
$gitphp_conf['gitbin'] = 'C:\\Progra~1\\Git\\bin\\git.exe'; $gitphp_conf['gitbin'] = 'C:\\Progra~1\\Git\\bin\\git.exe';
   
/* /*
* diffbin * diffbin
* Path to diff binary * Path to diff binary
* Same rules as gitbin * Same rules as gitbin
*/ */
// Linux: // Linux:
$gitphp_conf['diffbin'] = 'diff'; $gitphp_conf['diffbin'] = 'diff';
// Windows (msysgit): // Windows (msysgit):
$gitphp_conf['diffbin'] = 'C:\\Progra~1\\Git\\bin\\diff.exe'; $gitphp_conf['diffbin'] = 'C:\\Progra~1\\Git\\bin\\diff.exe';
   
/* /*
* gittmp * gittmp
* Location for temporary files for diffs * Location for temporary files for diffs
*/ */
$gitphp_conf['gittmp'] = '/tmp/gitphp/'; $gitphp_conf['gittmp'] = '/tmp/gitphp/';
   
/* /*
* magicdb * magicdb
* Path to the libmagic db used to read mimetype * Path to the libmagic db used to read mimetype
* Only applies if filemimetype = true * Only applies if filemimetype = true
* You can leave this as null and let the system * You can leave this as null and let the system
* try to find the database for you, but that method * try to find the database for you, but that method
* is known to have issues * is known to have issues
* If the path is correct but it's still not working, * If the path is correct but it's still not working,
* try removing the file extension if you have it on, * try removing the file extension if you have it on,
* or vice versa * or vice versa
*/ */
// Linux: // Linux:
$gitphp_conf['magicdb'] = '/usr/share/misc/magic'; $gitphp_conf['magicdb'] = '/usr/share/misc/magic';
// Windows: // Windows:
$gitphp_conf['magicdb'] = 'C:\\wamp\\php\\extras\\magic'; $gitphp_conf['magicdb'] = 'C:\\wamp\\php\\extras\\magic';
   
   
   
   
   
/******************************************************* /*******************************************************
* Cache options * Cache options
*/ */
   
/* /*
* cache * cache
* Turns on template caching. If in doubt, leave it off * Turns on template caching. If in doubt, leave it off
* You will need to create a directory 'cache' and make it * You will need to create a directory 'cache' and make it
* writable by the server * writable by the server
*/ */
$gitphp_conf['cache'] = false; $gitphp_conf['cache'] = false;
   
/* /*
* objectcache * objectcache
* Turns on object caching. This caches immutable pieces of * Turns on object caching. This caches immutable pieces of
* data from the git repository. You will need to create a * data from the git repository. You will need to create a
* directory 'cache' and make it writable by the server. * directory 'cache' and make it writable by the server.
* This can be used in place of the template cache, or * This can be used in place of the template cache, or
* in addition to it for the maximum benefit. * in addition to it for the maximum benefit.
*/ */
$gitphp_conf['objectcache'] = false; $gitphp_conf['objectcache'] = false;
   
/* /*
* cacheexpire * cacheexpire
* Attempts to automatically expire cache when a new commit renders * Attempts to automatically expire cache when a new commit renders
* it out of date. * it out of date.
* This is a good option for most users because it ensures the cache * This is a good option for most users because it ensures the cache
* is always up to date and users are seeing correct information, * is always up to date and users are seeing correct information,
* although it is a slight performance hit. * although it is a slight performance hit.
* However, if your commits are coming in so quickly that the cache * However, if your commits are coming in so quickly that the cache
* is constantly being expired, turn this off. * is constantly being expired, turn this off.
*/ */
$gitphp_conf['cacheexpire'] = true; $gitphp_conf['cacheexpire'] = true;
   
/* /*
* cachelifetime * cachelifetime
* Sets how long a page will be cached, in seconds * Sets how long a page will be cached, in seconds
* If you are automatically expiring the cache * If you are automatically expiring the cache
* (see the 'cacheexpire' option above), then this can be set * (see the 'cacheexpire' option above), then this can be set
* relatively high - 3600 seconds (1 hour) or even longer. * relatively high - 3600 seconds (1 hour) or even longer.
* -1 means no timeout. * -1 means no timeout.
* If you have turned cacheexpire off because of too many * If you have turned cacheexpire off because of too many
* cache expirations, set this low (5-10 seconds). * cache expirations, set this low (5-10 seconds).
*/ */
$gitphp_conf['cachelifetime'] = 3600; $gitphp_conf['cachelifetime'] = 3600;
   
/* /*
* objectcachelifetime * objectcachelifetime
* Sets how long git objects will be cached, in seconds * Sets how long git objects will be cached, in seconds
* The object cache only stores immutable objects from * The object cache only stores immutable objects from
* the git repository, so there's no harm in setting * the git repository, so there's no harm in setting
* this to a high number. Set to -1 to never expire. * this to a high number. Set to -1 to never expire.
*/ */
$gitphp_conf['objectcachelifetime'] = 86400; $gitphp_conf['objectcachelifetime'] = 86400;
   
/* /*
* memcache * memcache
* Enables memcache support for caching data, instead of * Enables memcache support for caching data, instead of
* Smarty's standard on-disk cache. * Smarty's standard on-disk cache.
* Only applies if cache = true or objectcache = true (or both) * Only applies if cache = true or objectcache = true (or both)
* Requires either the Memcached or Memcache PHP extensions. * Requires either the Memcached or Memcache PHP extensions.
* This is an array of servers. Each server is specified as an * This is an array of servers. Each server is specified as an
* array. * array.
* Index 0 (required): The server hostname/IP * Index 0 (required): The server hostname/IP
* Index 1 (optional): The port, default is 11211 * Index 1 (optional): The port, default is 11211
* Index 2 (optional): The weight, default is 1 * Index 2 (optional): The weight, default is 1
*/ */
//$gitphp_conf['memcache'] = array( //$gitphp_conf['memcache'] = array(
// array('127.0.0.1', 11211, 2), // array('127.0.0.1', 11211, 2),
// array('memcacheserver1', 11211), // array('memcacheserver1', 11211),
// array('memcacheserver2') // array('memcacheserver2')
//); //);
   
   
   
/******************************************************* /*******************************************************
* Paths to php libraries * Paths to php libraries
*/ */
   
/* /*
* smarty_prefix * smarty_prefix
* This is the prefix where smarty is installed. * This is the prefix where smarty is installed.
* If an absolute (starts with /) path is given, * If an absolute (starts with /) path is given,
* Smarty.class.php will be searched for in that directory. * Smarty.class.php will be searched for in that directory.
* If a relative (doesn't start with /) path is given, * If a relative (doesn't start with /) path is given,
* that subdirectory inside the php include dirs will be * that subdirectory inside the php include dirs will be
* searched. So, for example, if you specify the path as * searched. So, for example, if you specify the path as
* "/usr/share/Smarty/" then the script will look for * "/usr/share/Smarty/" then the script will look for
* /usr/share/Smarty/Smarty.class.php. * /usr/share/Smarty/Smarty.class.php.
* If you specify the path as "smarty/" then it will search * If you specify the path as "smarty/" then it will search
* the include directories in php.ini's include_path directive, * the include directories in php.ini's include_path directive,
* so it would search in places like /usr/share/php and /usr/lib/php: * so it would search in places like /usr/share/php and /usr/lib/php:
* /usr/share/php/smarty/Smarty.class.php, * /usr/share/php/smarty/Smarty.class.php,
* /usr/lib/php/smarty/Smarty.class.php, etc. * /usr/lib/php/smarty/Smarty.class.php, etc.
* Leave blank to just search in the root of the php include directories * Leave blank to just search in the root of the php include directories
* like /usr/share/php/Smarty.class.php, /usr/lib/php/Smarty.class.php, etc. * like /usr/share/php/Smarty.class.php, /usr/lib/php/Smarty.class.php, etc.
*/ */
$gitphp_conf['smarty_prefix'] = 'lib/smarty/libs/'; $gitphp_conf['smarty_prefix'] = 'lib/smarty/libs/';
   
/* /*
* geshiroot * geshiroot
* Directory where geshi is installed, only applies if geshi is enabled * Directory where geshi is installed, only applies if geshi is enabled
* NOTE: this is the path to the base geshi.php file to include, * NOTE: this is the path to the base geshi.php file to include,
* NOT the various other geshi php source files! * NOT the various other geshi php source files!
* Leave blank if geshi.php is in the gitphp root * Leave blank if geshi.php is in the gitphp root
*/ */
$gitphp_conf['geshiroot'] = 'lib/geshi/'; $gitphp_conf['geshiroot'] = 'lib/geshi/';
   
   
   
   
/******************************************************* /*******************************************************
* Debugging options * Debugging options
*/ */
   
/* /*
* debug * debug
* Turns on extra warning messages and benchmarking. * Turns on extra warning messages
* Not recommended for production systems, as it will give * Not recommended for production systems, as it will give
* way more benchmarking info than you care about, and * way more info about what's happening than you care about, and
* will screw up non-html output (rss, opml, snapshots, etc) * will screw up non-html output (rss, opml, snapshots, etc)
*/ */
$gitphp_conf['debug'] = false; $gitphp_conf['debug'] = false;
   
  /*
  * benchmark
  * Turns on extra timestamp and memory benchmarking info
  * when debug mode is turned on. Generates lots of output.
  */
  $gitphp_conf['benchmark'] = false;
   
   
<?php <?php
/** /**
* GitPHP Log * GitPHP Log
* *
* Logging class * Logging class
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
   
/** /**
* Logging class * Logging class
* *
* @package GitPHP * @package GitPHP
*/ */
class GitPHP_Log class GitPHP_Log
{ {
/** /**
* instance * instance
* *
* Stores the singleton instance * Stores the singleton instance
* *
* @access protected * @access protected
* @static * @static
*/ */
protected static $instance; protected static $instance;
   
/** /**
* enabled * enabled
* *
* Stores whether logging is enabled * Stores whether logging is enabled
* *
* @access protected * @access protected
*/ */
protected $enabled = false; protected $enabled = false;
   
/** /**
  * benchmark
  *
  * Stores whether benchmarking is enabled
  *
  * @access protected
  */
  protected $benchmark = false;
   
  /**
* startTime * startTime
* *
* Stores the starting instant * Stores the starting instant
* *
* @access protected * @access protected
*/ */
protected $startTime; protected $startTime;
   
/** /**
* startMem * startMem
* *
* Stores the starting memory * Stores the starting memory
* *
* @access protected * @access protected
*/ */
protected $startMem; protected $startMem;
   
/** /**
* entries * entries
* *
* Stores the log entries * Stores the log entries
* *
* @access protected * @access protected
*/ */
protected $entries = array(); protected $entries = array();
   
/** /**
* GetInstance * GetInstance
* *
* Returns the singleton instance * Returns the singleton instance
* *
* @access public * @access public
* @static * @static
* @return mixed instance of logging clas * @return mixed instance of logging clas
*/ */
public static function GetInstance() public static function GetInstance()
{ {
if (!self::$instance) { if (!self::$instance) {
self::$instance = new GitPHP_Log(); self::$instance = new GitPHP_Log();
} }
   
return self::$instance; return self::$instance;
} }
   
/** /**
* __construct * __construct
* *
* Constructor * Constructor
* *
* @access public * @access public
* @return Log object * @return Log object
*/ */
public function __construct() public function __construct()
{ {
$this->startTime = microtime(true); $this->startTime = microtime(true);
$this->startMem = memory_get_usage(); $this->startMem = memory_get_usage();
   
$this->enabled = GitPHP_Config::GetInstance()->GetValue('debug', false); $this->enabled = GitPHP_Config::GetInstance()->GetValue('debug', false);
  $this->benchmark = GitPHP_Config::GetInstance()->GetValue('benchmark', false);
} }
   
/** /**
* SetStartTime * SetStartTime
* *
* Sets start time * Sets start time
* *
* @access public * @access public
* @param float $start starting microtime * @param float $start starting microtime
*/ */
public function SetStartTime($start) public function SetStartTime($start)
{ {
$this->startTime = $start; $this->startTime = $start;
} }
   
/** /**
* SetStartMemory * SetStartMemory
* *
* Sets start memory * Sets start memory
* *
* @access public * @access public
* @param integer $start starting memory * @param integer $start starting memory
*/ */
public function SetStartMemory($start) public function SetStartMemory($start)
{ {
$this->startMem = $start; $this->startMem = $start;
} }
   
/** /**
* Log * Log
* *
* Log an entry * Log an entry
* *
* @access public * @access public
* @param string $message message to log * @param string $message message to log
*/ */
public function Log($message) public function Log($message)
{ {
if (!$this->enabled) if (!$this->enabled)
return; return;
   
$entry = array(); $entry = array();
$entry['time'] = microtime(true);  
$entry['mem'] = memory_get_usage(); if ($this->benchmark) {
  $entry['time'] = microtime(true);
  $entry['mem'] = memory_get_usage();
  }
   
$entry['msg'] = $message; $entry['msg'] = $message;
$this->entries[] = $entry; $this->entries[] = $entry;
} }
   
/** /**
* GetEnabled * GetEnabled
* *
* Gets whether logging is enabled * Gets whether logging is enabled
* *
* @access public * @access public
* @return boolean true if logging is enabled * @return boolean true if logging is enabled
*/ */
public function GetEnabled() public function GetEnabled()
{ {
return $this->enabled; return $this->enabled;
} }
   
/** /**
* SetEnabled * SetEnabled
* *
* Sets whether logging is enabled * Sets whether logging is enabled
* *
* @access public * @access public
* @param boolean $enable true if logging is enabled * @param boolean $enable true if logging is enabled
*/ */
public function SetEnabled($enable) public function SetEnabled($enable)
{ {
$this->enabled = $enable; $this->enabled = $enable;
  }
   
  /**
  * GetBenchmark
  *
  * Gets whether benchmarking is enabled
  *
  * @access public
  * @return boolean true if benchmarking is enabled
  */
  public function GetBenchmark()
  {
  return $this->benchmark;
  }
   
  /**
  * SetBenchmark
  *
  * Sets whether benchmarking is enabled
  *
  * @access public
  * @param boolean $bench true if benchmarking is enabled
  */
  public function SetBenchmark($bench)
  {
  $this->benchmark = $bench;
} }
   
/** /**
* GetEntries * GetEntries
* *
* Calculates times and gets log entries * Calculates times and gets log entries
* *
* @access public * @access public
* @return array log entries * @return array log entries
*/ */
public function GetEntries() public function GetEntries()
{ {
$data = array(); $data = array();
if ($this->enabled) { if ($this->enabled) {
$endTime = microtime(true);  
$endMem = memory_get_usage(); if ($this->benchmark) {
  $endTime = microtime(true);
$lastTime = $this->startTime; $endMem = memory_get_usage();
$lastMem = $this->startMem;  
  $lastTime = $this->startTime;
$data[] = '[' . $this->startTime . '] [' . $this->startMem . ' bytes] Start'; $lastMem = $this->startMem;
   
  $data[] = 'DEBUG: [' . $this->startTime . '] [' . $this->startMem . ' bytes] Start';
   
  }
   
foreach ($this->entries as $entry) { foreach ($this->entries as $entry) {
$data[] = '[' . $entry['time'] . '] [' . ($entry['time'] - $this->startTime) . ' sec since start] [' . ($entry['time'] - $lastTime) . ' sec since last] [' . $entry['mem'] . ' bytes] [' . ($entry['mem'] - $this->startMem) . ' bytes since start] [' . ($entry['mem'] - $lastMem) . ' bytes since last] ' . $entry['msg']; if ($this->benchmark) {
$lastTime = $entry['time']; $data[] = 'DEBUG: [' . $entry['time'] . '] [' . ($entry['time'] - $this->startTime) . ' sec since start] [' . ($entry['time'] - $lastTime) . ' sec since last] [' . $entry['mem'] . ' bytes] [' . ($entry['mem'] - $this->startMem) . ' bytes since start] [' . ($entry['mem'] - $lastMem) . ' bytes since last] ' . $entry['msg'];
$lastMem = $entry['mem']; $lastTime = $entry['time'];
  $lastMem = $entry['mem'];
  } else {
  $data[] = 'DEBUG: ' . $entry['msg'];
  }
} }
   
$data[] = '[' . $endTime . '] [' . ($endTime - $this->startTime) . ' sec since start] [' . ($endTime - $lastTime) . ' sec since last] [' . $endMem . ' bytes] [' . ($endMem - $this->startMem) . ' bytes since start] [' . ($endMem - $lastMem) . ' bytes since last] End'; if ($this->benchmark) {
  $data[] = '[' . $endTime . '] [' . ($endTime - $this->startTime) . ' sec since start] [' . ($endTime - $lastTime) . ' sec since last] [' . $endMem . ' bytes] [' . ($endMem - $this->startMem) . ' bytes since start] [' . ($endMem - $lastMem) . ' bytes since last] End';
  }
} }
   
return $data; return $data;
} }
   
} }
   
<?php <?php
/** /**
* GitPHP Project * GitPHP Project
* *
* Represents a single git project * Represents a single git project
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php'); require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Commit.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Commit.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Head.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Head.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Tag.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Tag.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Pack.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Pack.class.php');
   
/** /**
* Project class * Project class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_Project class GitPHP_Project
{ {
   
  /* internal variables {{{1*/
   
/** /**
* projectRoot * projectRoot
* *
* Stores the project root internally * Stores the project root internally
* *
* @access protected * @access protected
*/ */
protected $projectRoot; protected $projectRoot;
   
/** /**
* project * project
* *
* Stores the project internally * Stores the project internally
* *
* @access protected * @access protected
*/ */
protected $project; protected $project;
   
  /* owner internal variables {{{2*/
   
/** /**
* owner * owner
* *
* Stores the owner internally * Stores the owner internally
* *
* @access protected * @access protected
*/ */
protected $owner = ""; protected $owner = "";
   
/** /**
* ownerRead * ownerRead
* *
* Stores whether the file owner has been read * Stores whether the file owner has been read
* *
* @access protected * @access protected
*/ */
protected $ownerRead = false; protected $ownerRead = false;
   
  /*}}}2*/
   
  /* description internal variables {{{2*/
   
/** /**
* description * description
* *
* Stores the description internally * Stores the description internally
* *
* @access protected * @access protected
*/ */
protected $description; protected $description;
   
/** /**
* readDescription * readDescription
* *
* Stores whether the description has been * Stores whether the description has been
* read from the file yet * read from the file yet
* *
* @access protected * @access protected
*/ */
protected $readDescription = false; protected $readDescription = false;
   
  /*}}}2*/
   
/** /**
* category * category
* *
* Stores the category internally * Stores the category internally
* *
* @access protected * @access protected
*/ */
protected $category = ''; protected $category = '';
   
  /* epoch internal variables {{{2*/
   
/** /**
* epoch * epoch
* *
* Stores the project epoch internally * Stores the project epoch internally
* *
* @access protected * @access protected
*/ */
protected $epoch; protected $epoch;
   
/** /**
* epochRead * epochRead
* *
* Stores whether the project epoch has been read yet * Stores whether the project epoch has been read yet
* *
* @access protected * @access protected
*/ */
protected $epochRead = false; protected $epochRead = false;
   
  /*}}}2*/
   
  /* HEAD internal variables {{{2*/
   
/** /**
* head * head
* *
* Stores the head hash internally * Stores the head hash internally
* *
* @access protected * @access protected
*/ */
protected $head; protected $head;
   
/** /**
* readHeadRef * readHeadRef
* *
* Stores whether the head ref has been read yet * Stores whether the head ref has been read yet
* *
* @access protected * @access protected
*/ */
protected $readHeadRef = false; protected $readHeadRef = false;
   
  /*}}}*/
   
  /* ref internal variables {{{2*/
   
/** /**
* tags * tags
* *
* Stores the tags for the project * Stores the tags for the project
* *
* @access protected * @access protected
*/ */
protected $tags = array(); protected $tags = array();
   
/** /**
* heads * heads
* *
* Stores the heads for the project * Stores the heads for the project
* *
* @access protected * @access protected
*/ */
protected $heads = array(); protected $heads = array();
   
/** /**
* readRefs * readRefs
* *
* Stores whether refs have been read yet * Stores whether refs have been read yet
* *
* @access protected * @access protected
*/ */
protected $readRefs = false; protected $readRefs = false;
   
  /*}}}2*/
   
  /* url internal variables {{{2*/
   
/** /**
* cloneUrl * cloneUrl
* *
* Stores the clone url internally * Stores the clone url internally
* *
* @access protected * @access protected
*/ */
protected $cloneUrl = null; protected $cloneUrl = null;
   
/** /**
* pushUrl * pushUrl
* *
* Stores the push url internally * Stores the push url internally
* *
* @access protected * @access protected
*/ */
protected $pushUrl = null; protected $pushUrl = null;
   
  /*}}}2*/
   
  /* bugtracker internal variables {{{2*/
   
/** /**
* bugUrl * bugUrl
* *
* Stores the bug url internally * Stores the bug url internally
* *
* @access protected * @access protected
*/ */
protected $bugUrl = null; protected $bugUrl = null;
   
/** /**
* bugPattern * bugPattern
* *
* Stores the bug pattern internally * Stores the bug pattern internally
* *
* @access protected * @access protected
*/ */
protected $bugPattern = null; protected $bugPattern = null;
   
  /*}}}2*/
   
/** /**
* commitCache * commitCache
* *
* Caches fetched commit objects in case of * Caches fetched commit objects in case of
* repeated requests for the same object * repeated requests for the same object
* *
* @access protected * @access protected
*/ */
protected $commitCache = array(); protected $commitCache = array();
   
  /* packfile internal variables {{{2*/
   
/** /**
* packs * packs
* *
* Stores the list of packs * Stores the list of packs
* *
* @access protected * @access protected
*/ */
protected $packs = array(); protected $packs = array();
   
/** /**
* packsRead * packsRead
* *
* Stores whether packs have been read * Stores whether packs have been read
* *
* @access protected * @access protected
*/ */
protected $packsRead = false; protected $packsRead = false;
   
  /*}}}2*/
   
  /*}}}1*/
   
  /* class methods {{{1*/
   
/** /**
* __construct * __construct
* *
* Class constructor * Class constructor
* *
* @access public * @access public
* @param string $projectRoot project root * @param string $projectRoot project root
* @param string $project project * @param string $project project
* @throws Exception if project is invalid or outside of projectroot * @throws Exception if project is invalid or outside of projectroot
*/ */
public function __construct($projectRoot, $project) public function __construct($projectRoot, $project)
{ {
$this->projectRoot = GitPHP_Util::AddSlash($projectRoot); $this->projectRoot = GitPHP_Util::AddSlash($projectRoot);
$this->SetProject($project); $this->SetProject($project);
} }
   
  /*}}}1*/
   
  /* accessors {{{1*/
   
  /* project accessors {{{2*/
   
  /**
  * GetProject
  *
  * Gets the project
  *
  * @access public
  * @return string the project
  */
  public function GetProject()
  {
  return $this->project;
  }
   
/** /**
* SetProject * SetProject
* *
* Attempts to set the project * Attempts to set the project
* *
* @access private * @access private
* @throws Exception if project is invalid or outside of projectroot * @throws Exception if project is invalid or outside of projectroot
*/ */
private function SetProject($project) private function SetProject($project)
{ {
$realProjectRoot = realpath($this->projectRoot); $realProjectRoot = realpath($this->projectRoot);
$path = $this->projectRoot . $project; $path = $this->projectRoot . $project;
$fullPath = realpath($path); $fullPath = realpath($path);
   
if (!is_dir($fullPath)) { if (!is_dir($fullPath)) {
throw new Exception(sprintf(__('%1$s is not a directory'), $project)); throw new Exception(sprintf(__('%1$s is not a directory'), $project));
} }
   
if (!is_file($fullPath . '/HEAD')) { if (!is_file($fullPath . '/HEAD')) {
throw new Exception(sprintf(__('%1$s is not a git repository'), $project)); throw new Exception(sprintf(__('%1$s is not a git repository'), $project));
} }
   
if (preg_match('/(^|\/)\.{0,2}(\/|$)/', $project)) { if (preg_match('/(^|\/)\.{0,2}(\/|$)/', $project)) {
throw new Exception(sprintf(__('%1$s is attempting directory traversal'), $project)); throw new Exception(sprintf(__('%1$s is attempting directory traversal'), $project));
} }
   
$pathPiece = substr($fullPath, 0, strlen($realProjectRoot)); $pathPiece = substr($fullPath, 0, strlen($realProjectRoot));
   
if ((!is_link($path)) && (strcmp($pathPiece, $realProjectRoot) !== 0)) { if ((!is_link($path)) && (strcmp($pathPiece, $realProjectRoot) !== 0)) {
throw new Exception(sprintf(__('%1$s is outside of the projectroot'), $project)); throw new Exception(sprintf(__('%1$s is outside of the projectroot'), $project));
} }
   
$this->project = $project; $this->project = $project;
   
} }
   
  /*}}}2*/
   
  /**
  * GetSlug
  *
  * Gets the project as a filename/url friendly slug
  *
  * @access public
  * @return string the slug
  */
  public function GetSlug()
  {
  $project = $this->project;
   
  if (substr($project, -4) == '.git')
  $project = substr($project, 0, -4);
   
  return GitPHP_Util::MakeSlug($project);
  }
   
  /**
  * GetPath
  *
  * Gets the full project path
  *
  * @access public
  * @return string project path
  */
  public function GetPath()
  {
  return $this->projectRoot . $this->project;
  }
   
  /* owner accessors {{{2 */
   
/** /**
* GetOwner * GetOwner
* *
* Gets the project's owner * Gets the project's owner
* *
* @access public * @access public
* @return string project owner * @return string project owner
*/ */
public function GetOwner() public function GetOwner()
{ {
if (empty($this->owner) && !$this->ownerRead) { if (empty($this->owner) && !$this->ownerRead) {
$this->ReadOwner(); $this->ReadOwner();
} }
return $this->owner; return $this->owner;
} }
   
/** /**
* ReadOwner * ReadOwner
* *
* Reads the project owner * Reads the project owner
* *
* @access protected * @access protected
*/ */
protected function ReadOwner() protected function ReadOwner()
{ {
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
$this->ReadOwnerGit(); $this->ReadOwnerGit();
} else { } else {
$this->ReadOwnerRaw(); $this->ReadOwnerRaw();
} }
   
if (empty($this->owner) && function_exists('posix_getpwuid')) { if (empty($this->owner) && function_exists('posix_getpwuid')) {
$uid = fileowner($this->GetPath()); $uid = fileowner($this->GetPath());
if ($uid !== false) { if ($uid !== false) {
$data = posix_getpwuid($uid); $data = posix_getpwuid($uid);
if (isset($data['gecos']) && !empty($data['gecos'])) { if (isset($data['gecos']) && !empty($data['gecos'])) {
$this->owner = $data['gecos']; $this->owner = $data['gecos'];
} elseif (isset($data['name']) && !empty($data['name'])) { } elseif (isset($data['name']) && !empty($data['name'])) {
$this->owner = $data['name']; $this->owner = $data['name'];
} }
} }
} }
   
$this->ownerRead = true; $this->ownerRead = true;
} }
   
/** /**
* ReadOwnerGit * ReadOwnerGit
* *
* Reads the project owner using the git executable * Reads the project owner using the git executable
* *
* @access private * @access private
*/ */
private function ReadOwnerGit() private function ReadOwnerGit()
{ {
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = 'gitweb.owner'; $args[] = 'gitweb.owner';
$this->owner = $exe->Execute(GIT_CONFIG, $args); $this->owner = $exe->Execute(GIT_CONFIG, $args);
unset($exe); unset($exe);
} }
   
/** /**
* ReadOwnerRaw * ReadOwnerRaw
* *
* Reads the project owner using the raw config file * Reads the project owner using the raw config file
* *
* @access private * @access private
*/ */
private function ReadOwnerRaw() private function ReadOwnerRaw()
{ {
// not worth writing a full config parser right now // not worth writing a full config parser right now
   
if (!file_exists($this->GetPath() . '/config')) if (!file_exists($this->GetPath() . '/config'))
return; return;
   
$configData = explode("\n", file_get_contents($this->GetPath() . '/config')); $configData = explode("\n", file_get_contents($this->GetPath() . '/config'));
   
$gitwebSection = false; $gitwebSection = false;
foreach ($configData as $configLine) { foreach ($configData as $configLine) {
$trimmed = trim($configLine); $trimmed = trim($configLine);
if (empty($trimmed)) { if (empty($trimmed)) {
continue; continue;
} }
   
if (preg_match('/^\[(.+)\]$/', $trimmed, $regs)) { if (preg_match('/^\[(.+)\]$/', $trimmed, $regs)) {
// section header // section header
$gitwebSection = ($regs[1] == 'gitweb'); $gitwebSection = ($regs[1] == 'gitweb');
} else if ($gitwebSection) { } else if ($gitwebSection) {
$eq = strpos($trimmed, '='); $eq = strpos($trimmed, '=');
if ($eq === false) { if ($eq === false) {
continue; continue;
} }
   
$key = trim(substr($trimmed, 0, $eq)); $key = trim(substr($trimmed, 0, $eq));
if ($key == 'owner') { if ($key == 'owner') {
$this->owner = trim(substr($trimmed, $eq+1)); $this->owner = trim(substr($trimmed, $eq+1));
break; break;
} }
} }
} }
} }
   
/** /**
* SetOwner * SetOwner
* *
* Sets the project's owner (from an external source) * Sets the project's owner (from an external source)
* *
* @access public * @access public
* @param string $owner the owner * @param string $owner the owner
*/ */
public function SetOwner($owner) public function SetOwner($owner)
{ {
$this->owner = $owner; $this->owner = $owner;
} }
   
/** /*}}}2*/
* GetProject  
* /* projectroot accessors {{{2*/
* Gets the project  
*  
* @access public  
* @return string the project  
*/  
public function GetProject()  
{  
return $this->project;  
}  
   
/** /**
* GetProjectRoot * GetProjectRoot
* *
* Gets the project root * Gets the project root
* *
* @access public * @access public
* @return string the project root * @return string the project root
*/ */
public function GetProjectRoot() public function GetProjectRoot()
{ {
return $this->projectRoot; return $this->projectRoot;
} }
   
/** /*}}}2*/
* GetSlug  
* /* description accessors {{{2*/
* Gets the project as a filename/url friendly slug  
*  
* @access public  
* @return string the slug  
*/  
public function GetSlug()  
{  
$project = $this->project;  
   
if (substr($project, -4) == '.git')  
$project = substr($project, 0, -4);  
   
return GitPHP_Util::MakeSlug($project);  
}  
   
/**  
* GetPath  
*  
* Gets the full project path  
*  
* @access public  
* @return string project path  
*/  
public function GetPath()  
{  
return $this->projectRoot . $this->project;  
}  
   
/** /**
* GetDescription * GetDescription
* *
* Gets the project description * Gets the project description
* *
* @access public * @access public
* @param $trim length to trim description to (0 for no trim) * @param $trim length to trim description to (0 for no trim)
* @return string project description * @return string project description
*/ */
public function GetDescription($trim = 0) public function GetDescription($trim = 0)
{ {
if (!$this->readDescription) { if (!$this->readDescription) {
$this->description = file_get_contents($this->GetPath() . '/description'); if (file_exists($this->GetPath() . '/description')) {
  $this->description = file_get_contents($this->GetPath() . '/description');
  }
  $this->readDescription = true;
} }
if (($trim > 0) && (strlen($this->description) > $trim)) { if (($trim > 0) && (strlen($this->description) > $trim)) {
return substr($this->description, 0, $trim) . '…'; return substr($this->description, 0, $trim) . '…';
} }
   
return $this->description; return $this->description;
} }
   
/** /**
* SetDescription * SetDescription
* *
* Overrides the project description * Overrides the project description
* *
* @access public * @access public
* @param string $descr description * @param string $descr description
*/ */
public function SetDescription($descr) public function SetDescription($descr)
{ {
$this->description = $descr; $this->description = $descr;
$this->readDescription = true; $this->readDescription = true;
} }
   
  /*}}}2*/
   
/** /**
* GetDaemonEnabled * GetDaemonEnabled
* *
* Returns whether gitdaemon is allowed for this project * Returns whether gitdaemon is allowed for this project
* *
* @access public * @access public
* @return boolean git-daemon-export-ok? * @return boolean git-daemon-export-ok?
*/ */
public function GetDaemonEnabled() public function GetDaemonEnabled()
{ {
return file_exists($this->GetPath() . '/git-daemon-export-ok'); return file_exists($this->GetPath() . '/git-daemon-export-ok');
} }
   
  /* category accessors {{{2*/
   
/** /**
* GetCategory * GetCategory
* *
* Gets the project's category * Gets the project's category
* *
* @access public * @access public
* @return string category * @return string category
*/ */
public function GetCategory() public function GetCategory()
{ {
return $this->category; return $this->category;
} }
   
/** /**
* SetCategory * SetCategory
* *
* Sets the project's category * Sets the project's category
* *
* @access public * @access public
* @param string $category category * @param string $category category
*/ */
public function SetCategory($category) public function SetCategory($category)
{ {
$this->category = $category; $this->category = $category;
} }
   
  /*}}}2*/
   
  /* clone url accessors {{{2*/
   
/** /**
* GetCloneUrl * GetCloneUrl
* *
* Gets the clone URL for this repository, if specified * Gets the clone URL for this repository, if specified
* *
* @access public * @access public
* @return string clone url * @return string clone url
*/ */
public function GetCloneUrl() public function GetCloneUrl()
{ {
if ($this->cloneUrl !== null) if ($this->cloneUrl !== null)
return $this->cloneUrl; return $this->cloneUrl;
   
$cloneurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('cloneurl', ''), false); $cloneurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('cloneurl', ''), false);
if (!empty($cloneurl)) if (!empty($cloneurl))
$cloneurl .= $this->project; $cloneurl .= $this->project;
   
return $cloneurl; return $cloneurl;
} }
   
/** /**
* SetCloneUrl * SetCloneUrl
* *
* Overrides the clone URL for this repository * Overrides the clone URL for this repository
* *
* @access public * @access public
* @param string $cUrl clone url * @param string $cUrl clone url
*/ */
public function SetCloneUrl($cUrl) public function SetCloneUrl($cUrl)
{ {
$this->cloneUrl = $cUrl; $this->cloneUrl = $cUrl;
} }
   
  /*}}}2*/
   
  /* push url accessors {{{2*/
   
/** /**
* GetPushUrl * GetPushUrl
* *
* Gets the push URL for this repository, if specified * Gets the push URL for this repository, if specified
* *
* @access public * @access public
* @return string push url * @return string push url
*/ */
public function GetPushUrl() public function GetPushUrl()
{ {
if ($this->pushUrl !== null) if ($this->pushUrl !== null)
return $this->pushUrl; return $this->pushUrl;
   
$pushurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('pushurl', ''), false); $pushurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('pushurl', ''), false);
if (!empty($pushurl)) if (!empty($pushurl))
$pushurl .= $this->project; $pushurl .= $this->project;
   
return $pushurl; return $pushurl;
} }
   
/** /**
* SetPushUrl * SetPushUrl
* *
* Overrides the push URL for this repository * Overrides the push URL for this repository
* *
* @access public * @access public
* @param string $pUrl push url * @param string $pUrl push url
*/ */
public function SetPushUrl($pUrl) public function SetPushUrl($pUrl)
{ {
$this->pushUrl = $pUrl; $this->pushUrl = $pUrl;
} }
   
  /*}}}2*/
   
  /* bugtracker accessors {{{2*/
   
/** /**
* GetBugUrl * GetBugUrl
* *
* Gets the bug URL for this repository, if specified * Gets the bug URL for this repository, if specified
* *
* @access public * @access public
* @return string bug url * @return string bug url
*/ */
public function GetBugUrl() public function GetBugUrl()
{ {
if ($this->bugUrl != null) if ($this->bugUrl != null)
return $this->bugUrl; return $this->bugUrl;
   
return GitPHP_Config::GetInstance()->GetValue('bugurl', ''); return GitPHP_Config::GetInstance()->GetValue('bugurl', '');
} }
   
/** /**
* SetBugUrl * SetBugUrl
* *
* Overrides the bug URL for this repository * Overrides the bug URL for this repository
* *
* @access public * @access public
* @param string $bUrl bug url * @param string $bUrl bug url
*/ */
public function SetBugUrl($bUrl) public function SetBugUrl($bUrl)
{ {
$this->bugUrl = $bUrl; $this->bugUrl = $bUrl;
} }
   
/** /**
* GetBugPattern * GetBugPattern
* *
* Gets the bug pattern for this repository, if specified * Gets the bug pattern for this repository, if specified
* *
* @access public * @access public
* @return string bug pattern * @return string bug pattern
*/ */
public function GetBugPattern() public function GetBugPattern()
{ {
if ($this->bugPattern != null) if ($this->bugPattern != null)
return $this->bugPattern; return $this->bugPattern;
   
return GitPHP_Config::GetInstance()->GetValue('bugpattern', ''); return GitPHP_Config::GetInstance()->GetValue('bugpattern', '');
} }
   
/** /**
* SetBugPattern * SetBugPattern
* *
* Overrides the bug pattern for this repository * Overrides the bug pattern for this repository
* *
* @access public * @access public
* @param string $bPat bug pattern * @param string $bPat bug pattern
*/ */
public function SetBugPattern($bPat) public function SetBugPattern($bPat)
{ {
$this->bugPattern = $bPat; $this->bugPattern = $bPat;
} }
   
  /*}}}2*/
   
  /* HEAD accessors {{{2*/
   
/** /**
* GetHeadCommit * GetHeadCommit
* *
* Gets the head commit for this project * Gets the head commit for this project
* Shortcut for getting the tip commit of the HEAD branch * Shortcut for getting the tip commit of the HEAD branch
* *
* @access public * @access public
* @return mixed head commit * @return mixed head commit
*/ */
public function GetHeadCommit() public function GetHeadCommit()
{ {
if (!$this->readHeadRef) if (!$this->readHeadRef)
$this->ReadHeadCommit(); $this->ReadHeadCommit();
   
return $this->GetCommit($this->head); return $this->GetCommit($this->head);
} }
   
/** /**
* ReadHeadCommit * ReadHeadCommit
* *
* Reads the head commit hash * Reads the head commit hash
* *
* @access protected * @access protected
*/ */
public function ReadHeadCommit() public function ReadHeadCommit()
{ {
$this->readHeadRef = true; $this->readHeadRef = true;
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
$this->ReadHeadCommitGit(); $this->ReadHeadCommitGit();
} else { } else {
$this->ReadHeadCommitRaw(); $this->ReadHeadCommitRaw();
} }
} }
   
/** /**
* ReadHeadCommitGit * ReadHeadCommitGit
* *
* Read head commit using git executable * Read head commit using git executable
* *
* @access private * @access private
*/ */
private function ReadHeadCommitGit() private function ReadHeadCommitGit()
{ {
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--verify'; $args[] = '--verify';
$args[] = 'HEAD'; $args[] = 'HEAD';
$this->head = trim($exe->Execute(GIT_REV_PARSE, $args)); $this->head = trim($exe->Execute(GIT_REV_PARSE, $args));
} }
   
/** /**
* ReadHeadCommitRaw * ReadHeadCommitRaw
* *
* Read head commit using raw git head pointer * Read head commit using raw git head pointer
* *
* @access private * @access private
*/ */
private function ReadHeadCommitRaw() private function ReadHeadCommitRaw()
{ {
$head = trim(file_get_contents($this->GetPath() . '/HEAD')); $head = trim(file_get_contents($this->GetPath() . '/HEAD'));
if (preg_match('/^([0-9A-Fa-f]{40})$/', $head, $regs)) { if (preg_match('/^([0-9A-Fa-f]{40})$/', $head, $regs)) {
/* Detached HEAD */ /* Detached HEAD */
$this->head = $regs[1]; $this->head = $regs[1];
} else if (preg_match('/^ref: (.+)$/', $head, $regs)) { } else if (preg_match('/^ref: (.+)$/', $head, $regs)) {
/* standard pointer to head */ /* standard pointer to head */
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
if (isset($this->heads[$regs[1]])) { if (isset($this->heads[$regs[1]])) {
$this->head = $this->heads[$regs[1]]->GetHash(); $this->head = $this->heads[$regs[1]]->GetHash();
} }
} }
} }
   
  /*}}}2*/
   
  /* epoch accessors {{{2*/
   
  /**
  * GetEpoch
  *
  * Gets this project's epoch
  * (time of last change)
  *
  * @access public
  * @return integer timestamp
  */
  public function GetEpoch()
  {
  if (!$this->epochRead)
  $this->ReadEpoch();
   
  return $this->epoch;
  }
   
  /**
  * GetAge
  *
  * Gets this project's age
  * (time since most recent change)
  *
  * @access public
  * @return integer age
  */
  public function GetAge()
  {
  if (!$this->epochRead)
  $this->ReadEpoch();
   
  return time() - $this->epoch;
  }
   
  /**
  * ReadEpoch
  *
  * Reads this project's epoch
  * (timestamp of most recent change)
  *
  * @access private
  */
  private function ReadEpoch()
  {
  $this->epochRead = true;
   
  if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
  $this->ReadEpochGit();
  } else {
  $this->ReadEpochRaw();
  }
  }
   
  /**
  * ReadEpochGit
  *
  * Reads this project's epoch using git executable
  *
  * @access private
  */
  private function ReadEpochGit()
  {
  $exe = new GitPHP_GitExe($this);
   
  $args = array();
  $args[] = '--format="%(committer)"';
  $args[] = '--sort=-committerdate';
  $args[] = '--count=1';
  $args[] = 'refs/heads';
   
  $epochstr = trim($exe->Execute(GIT_FOR_EACH_REF, $args));
   
  if (preg_match('/ (\d+) [-+][01]\d\d\d$/', $epochstr, $regs)) {
  $this->epoch = $regs[1];
  }
   
  unset($exe);
  }
   
  /**
  * ReadEpochRaw
  *
  * Reads this project's epoch using raw objects
  *
  * @access private
  */
  private function ReadEpochRaw()
  {
  if (!$this->readRefs)
  $this->ReadRefList();
   
  $epoch = 0;
  foreach ($this->heads as $head) {
  $commit = $head->GetCommit();
  if ($commit) {
  if ($commit->GetCommitterEpoch() > $epoch) {
  $epoch = $commit->GetCommitterEpoch();
  }
  }
  }
  if ($epoch > 0) {
  $this->epoch = $epoch;
  }
  }
   
  /*}}}2*/
   
  /*}}}1*/
   
  /* data loading methods {{{1*/
   
  /* commit loading methods {{{2*/
   
/** /**
* GetCommit * GetCommit
* *
* Get a commit for this project * Get a commit for this project
* *
* @access public * @access public
*/ */
public function GetCommit($hash) public function GetCommit($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
if ($hash === 'HEAD') if ($hash === 'HEAD')
return $this->GetHeadCommit(); return $this->GetHeadCommit();
   
if (substr_compare($hash, 'refs/heads/', 0, 11) === 0) { if (substr_compare($hash, 'refs/heads/', 0, 11) === 0) {
$head = $this->GetHead(substr($hash, 11)); $head = $this->GetHead(substr($hash, 11));
if ($head != null) if ($head != null)
return $head->GetCommit(); return $head->GetCommit();
return null; return null;
} else if (substr_compare($hash, 'refs/tags/', 0, 10) === 0) { } else if (substr_compare($hash, 'refs/tags/', 0, 10) === 0) {
$tag = $this->GetTag(substr($hash, 10)); $tag = $this->GetTag(substr($hash, 10));
if ($tag != null) { if ($tag != null) {
$obj = $tag->GetCommit(); $obj = $tag->GetCommit();
if ($obj != null) { if ($obj != null) {
return $obj; return $obj;
} }
} }
return null; return null;
} }
   
if (preg_match('/[0-9a-f]{40}/i', $hash)) { if (preg_match('/[0-9a-f]{40}/i', $hash)) {
   
if (!isset($this->commitCache[$hash])) { if (!isset($this->commitCache[$hash])) {
$cacheKey = 'project|' . $this->project . '|commit|' . $hash; $cacheKey = 'project|' . $this->project . '|commit|' . $hash;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) if ($cached)
$this->commitCache[$hash] = $cached; $this->commitCache[$hash] = $cached;
else else
$this->commitCache[$hash] = new GitPHP_Commit($this, $hash); $this->commitCache[$hash] = new GitPHP_Commit($this, $hash);
} }
   
return $this->commitCache[$hash]; return $this->commitCache[$hash];
   
} }
   
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
if (isset($this->heads['refs/heads/' . $hash])) if (isset($this->heads['refs/heads/' . $hash]))
return $this->heads['refs/heads/' . $hash]->GetCommit(); return $this->heads['refs/heads/' . $hash]->GetCommit();
   
if (isset($this->tags['refs/tags/' . $hash])) if (isset($this->tags['refs/tags/' . $hash]))
return $this->tags['refs/tags/' . $hash]->GetCommit(); return $this->tags['refs/tags/' . $hash]->GetCommit();
   
return null; return null;
} }
   
/** /*}}}2*/
* CompareProject  
* /* ref loading methods {{{2*/
* Compares two projects by project name  
*  
* @access public  
* @static  
* @param mixed $a first project  
* @param mixed $b second project  
* @return integer comparison result  
*/  
public static function CompareProject($a, $b)  
{  
$catCmp = strcmp($a->GetCategory(), $b->GetCategory());  
if ($catCmp !== 0)  
return $catCmp;  
   
return strcmp($a->GetProject(), $b->GetProject());  
}  
   
/**  
* CompareDescription  
*  
* Compares two projects by description  
*  
* @access public  
* @static  
* @param mixed $a first project  
* @param mixed $b second project  
* @return integer comparison result  
*/  
public static function CompareDescription($a, $b)  
{  
$catCmp = strcmp($a->GetCategory(), $b->GetCategory());  
if ($catCmp !== 0)  
return $catCmp;  
   
return strcmp($a->GetDescription(), $b->GetDescription());  
}  
   
/**  
* CompareOwner  
*  
* Compares two projects by owner  
*  
* @access public  
* @static  
* @param mixed $a first project  
* @param mixed $b second project  
* @return integer comparison result  
*/  
public static function CompareOwner($a, $b)  
{  
$catCmp = strcmp($a->GetCategory(), $b->GetCategory());  
if ($catCmp !== 0)  
return $catCmp;  
   
return strcmp($a->GetOwner(), $b->GetOwner());  
}  
   
/**  
* CompareAge  
*  
* Compares two projects by age  
*  
* @access public  
* @static  
* @param mixed $a first project  
* @param mixed $b second project  
* @return integer comparison result  
*/  
public static function CompareAge($a, $b)  
{  
$catCmp = strcmp($a->GetCategory(), $b->GetCategory());  
if ($catCmp !== 0)  
return $catCmp;  
   
if ($a->GetAge() === $b->GetAge())  
return 0;  
return ($a->GetAge() < $b->GetAge() ? -1 : 1);  
}  
   
/** /**
* GetRefs * GetRefs
* *
* Gets the list of refs for the project * Gets the list of refs for the project
* *
* @access public * @access public
* @param string $type type of refs to get * @param string $type type of refs to get
* @return array array of refs * @return array array of refs
*/ */
public function GetRefs($type = '') public function GetRefs($type = '')
{ {
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
if ($type == 'tags') { if ($type == 'tags') {
return $this->tags; return $this->tags;
} else if ($type == 'heads') { } else if ($type == 'heads') {
return $this->heads; return $this->heads;
} }
   
return array_merge($this->heads, $this->tags); return array_merge($this->heads, $this->tags);
} }
   
/** /**
* ReadRefList * ReadRefList
* *
* Reads the list of refs for this project * Reads the list of refs for this project
* *
* @access protected * @access protected
*/ */
protected function ReadRefList() protected function ReadRefList()
{ {
$this->readRefs = true; $this->readRefs = true;
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
$this->ReadRefListGit(); $this->ReadRefListGit();
} else { } else {
$this->ReadRefListRaw(); $this->ReadRefListRaw();
} }
} }
   
/** /**
* ReadRefListGit * ReadRefListGit
* *
* Reads the list of refs for this project using the git executable * Reads the list of refs for this project using the git executable
* *
* @access private * @access private
*/ */
private function ReadRefListGit() private function ReadRefListGit()
{ {
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--heads'; $args[] = '--heads';
$args[] = '--tags'; $args[] = '--tags';
$args[] = '--dereference'; $args[] = '--dereference';
$ret = $exe->Execute(GIT_SHOW_REF, $args); $ret = $exe->Execute(GIT_SHOW_REF, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match('/^([0-9a-fA-F]{40}) refs\/(tags|heads)\/([^^]+)(\^{})?$/', $line, $regs)) { if (preg_match('/^([0-9a-fA-F]{40}) refs\/(tags|heads)\/([^^]+)(\^{})?$/', $line, $regs)) {
try { try {
$key = 'refs/' . $regs[2] . '/' . $regs[3]; $key = 'refs/' . $regs[2] . '/' . $regs[3];
if ($regs[2] == 'tags') { if ($regs[2] == 'tags') {
if ((!empty($regs[4])) && ($regs[4] == '^{}')) { if ((!empty($regs[4])) && ($regs[4] == '^{}')) {
$derefCommit = $this->GetCommit($regs[1]); $derefCommit = $this->GetCommit($regs[1]);
if ($derefCommit && isset($this->tags[$key])) { if ($derefCommit && isset($this->tags[$key])) {
$this->tags[$key]->SetCommit($derefCommit); $this->tags[$key]->SetCommit($derefCommit);
} }
} else if (!isset($this->tags[$key])) { } else if (!isset($this->tags[$key])) {
$this->tags[$key] = $this->LoadTag($regs[3], $regs[1]); $this->tags[$key] = $this->LoadTag($regs[3], $regs[1]);
} }
} else if ($regs[2] == 'heads') { } else if ($regs[2] == 'heads') {
$this->heads[$key] = new GitPHP_Head($this, $regs[3], $regs[1]); $this->heads[$key] = new GitPHP_Head($this, $regs[3], $regs[1]);
} }
} catch (Exception $e) { } catch (Exception $e) {
} }
} }
} }
} }
   
/** /**
* ReadRefListRaw * ReadRefListRaw
* *
* Reads the list of refs for this project using the raw git files * Reads the list of refs for this project using the raw git files
* *
* @access private * @access private
*/ */
private function ReadRefListRaw() private function ReadRefListRaw()
{ {
$pathlen = strlen($this->GetPath()) + 1; $pathlen = strlen($this->GetPath()) + 1;
   
// read loose heads // read loose heads
$heads = $this->ListDir($this->GetPath() . '/refs/heads'); $heads = $this->ListDir($this->GetPath() . '/refs/heads');
for ($i = 0; $i < count($heads); $i++) { for ($i = 0; $i < count($heads); $i++) {
$key = trim(substr($heads[$i], $pathlen), "/\\"); $key = trim(substr($heads[$i], $pathlen), "/\\");
   
if (isset($this->heads[$key])) { if (isset($this->heads[$key])) {
continue; continue;
} }
   
$hash = trim(file_get_contents($heads[$i])); $hash = trim(file_get_contents($heads[$i]));
if (preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) { if (preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) {
$head = substr($key, strlen('refs/heads/')); $head = substr($key, strlen('refs/heads/'));
$this->heads[$key] = new GitPHP_Head($this, $head, $hash); $this->heads[$key] = new GitPHP_Head($this, $head, $hash);
} }
} }
   
// read loose tags // read loose tags
$tags = $this->ListDir($this->GetPath() . '/refs/tags'); $tags = $this->ListDir($this->GetPath() . '/refs/tags');
for ($i = 0; $i < count($tags); $i++) { for ($i = 0; $i < count($tags); $i++) {
$key = trim(substr($tags[$i], $pathlen), "/\\"); $key = trim(substr($tags[$i], $pathlen), "/\\");
   
if (isset($this->tags[$key])) { if (isset($this->tags[$key])) {
continue; continue;
} }
   
$hash = trim(file_get_contents($tags[$i])); $hash = trim(file_get_contents($tags[$i]));
if (preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) { if (preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) {
$tag = substr($key, strlen('refs/tags/')); $tag = substr($key, strlen('refs/tags/'));
$this->tags[$key] = $this->LoadTag($tag, $hash); $this->tags[$key] = $this->LoadTag($tag, $hash);
} }
} }
   
// check packed refs // check packed refs
if (file_exists($this->GetPath() . '/packed-refs')) { if (file_exists($this->GetPath() . '/packed-refs')) {
$packedRefs = explode("\n", file_get_contents($this->GetPath() . '/packed-refs')); $packedRefs = explode("\n", file_get_contents($this->GetPath() . '/packed-refs'));
   
$lastRef = null; $lastRef = null;
foreach ($packedRefs as $ref) { foreach ($packedRefs as $ref) {
   
if (preg_match('/^\^([0-9A-Fa-f]{40})$/', $ref, $regs)) { if (preg_match('/^\^([0-9A-Fa-f]{40})$/', $ref, $regs)) {
// dereference of previous ref // dereference of previous ref
if (($lastRef != null) && ($lastRef instanceof GitPHP_Tag)) { if (($lastRef != null) && ($lastRef instanceof GitPHP_Tag)) {
$derefCommit = $this->GetCommit($regs[1]); $derefCommit = $this->GetCommit($regs[1]);
if ($derefCommit) { if ($derefCommit) {
$lastRef->SetCommit($derefCommit); $lastRef->SetCommit($derefCommit);
} }
} }
} }
   
$lastRef = null; $lastRef = null;
   
if (preg_match('/^([0-9A-Fa-f]{40}) refs\/(tags|heads)\/(.+)$/', $ref, $regs)) { if (preg_match('/^([0-9A-Fa-f]{40}) refs\/(tags|heads)\/(.+)$/', $ref, $regs)) {
// standard tag/head // standard tag/head
$key = 'refs/' . $regs[2] . '/' . $regs[3]; $key = 'refs/' . $regs[2] . '/' . $regs[3];
if ($regs[2] == 'tags') { if ($regs[2] == 'tags') {
if (!isset($this->tags[$key])) { if (!isset($this->tags[$key])) {
$lastRef = $this->LoadTag($regs[3], $regs[1]); $lastRef = $this->LoadTag($regs[3], $regs[1]);
$this->tags[$key] = $lastRef; $this->tags[$key] = $lastRef;
} }
} else if ($regs[2] == 'heads') { } else if ($regs[2] == 'heads') {
if (!isset($this->heads[$key])) { if (!isset($this->heads[$key])) {
$this->heads[$key] = new GitPHP_Head($this, $regs[3], $regs[1]); $this->heads[$key] = new GitPHP_Head($this, $regs[3], $regs[1]);
} }
} }
} }
} }
} }
} }
   
/** /**
* ListDir * ListDir
* *
* Recurses into a directory and lists files inside * Recurses into a directory and lists files inside
* *
* @access private * @access private
* @param string $dir directory * @param string $dir directory
* @return array array of filenames * @return array array of filenames
*/ */
private function ListDir($dir) private function ListDir($dir)
{ {
$files = array(); $files = array();
if ($dh = opendir($dir)) { if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
if (($file == '.') || ($file == '..')) { if (($file == '.') || ($file == '..')) {
continue; continue;
} }
$fullFile = $dir . '/' . $file; $fullFile = $dir . '/' . $file;
if (is_dir($fullFile)) { if (is_dir($fullFile)) {
$subFiles = $this->ListDir($fullFile); $subFiles = $this->ListDir($fullFile);
if (count($subFiles) > 0) { if (count($subFiles) > 0) {
$files = array_merge($files, $subFiles); $files = array_merge($files, $subFiles);
} }
} else { } else {
$files[] = $fullFile; $files[] = $fullFile;
} }
} }
} }
return $files; return $files;
} }
   
  /*}}}2*/
   
  /* tag loading methods {{{2*/
   
/** /**
* GetTags * GetTags
* *
* Gets list of tags for this project by age descending * Gets list of tags for this project by age descending
* *
* @access public * @access public
* @param integer $count number of tags to load * @param integer $count number of tags to load
* @return array array of tags * @return array array of tags
*/ */
public function GetTags($count = 0) public function GetTags($count = 0)
{ {
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
return $this->GetTagsGit($count); return $this->GetTagsGit($count);
} else { } else {
return $this->GetTagsRaw($count); return $this->GetTagsRaw($count);
} }
} }
   
/** /**
* GetTagsGit * GetTagsGit
* *
* Gets list of tags for this project by age descending using git executable * Gets list of tags for this project by age descending using git executable
* *
* @access private * @access private
* @param integer $count number of tags to load * @param integer $count number of tags to load
* @return array array of tags * @return array array of tags
*/ */
private function GetTagsGit($count = 0) private function GetTagsGit($count = 0)
{ {
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--sort=-creatordate'; $args[] = '--sort=-creatordate';
$args[] = '--format="%(refname)"'; $args[] = '--format="%(refname)"';
if ($count > 0) { if ($count > 0) {
$args[] = '--count=' . $count; $args[] = '--count=' . $count;
} }
$args[] = '--'; $args[] = '--';
$args[] = 'refs/tags'; $args[] = 'refs/tags';
$ret = $exe->Execute(GIT_FOR_EACH_REF, $args); $ret = $exe->Execute(GIT_FOR_EACH_REF, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
$tags = array(); $tags = array();
   
foreach ($lines as $ref) { foreach ($lines as $ref) {
if (isset($this->tags[$ref])) { if (isset($this->tags[$ref])) {
$tags[] = $this->tags[$ref]; $tags[] = $this->tags[$ref];
} }
} }
   
return $tags; return $tags;
} }
   
/** /**
* GetTagsRaw * GetTagsRaw
* *
* Gets list of tags for this project by age descending using raw git objects * Gets list of tags for this project by age descending using raw git objects
* *
* @access private * @access private
* @param integer $count number of tags to load * @param integer $count number of tags to load
* @return array array of tags * @return array array of tags
*/ */
private function GetTagsRaw($count = 0) private function GetTagsRaw($count = 0)
{ {
$tags = $this->tags; $tags = $this->tags;
usort($tags, array('GitPHP_Tag', 'CompareCreationEpoch')); usort($tags, array('GitPHP_Tag', 'CompareCreationEpoch'));
   
if (($count > 0) && (count($tags) > $count)) { if (($count > 0) && (count($tags) > $count)) {
$tags = array_slice($tags, 0, $count); $tags = array_slice($tags, 0, $count);
} }
   
return $tags; return $tags;
} }
   
/** /**
* GetTag * GetTag
* *
* Gets a single tag * Gets a single tag
* *
* @access public * @access public
* @param string $tag tag to find * @param string $tag tag to find
* @return mixed tag object * @return mixed tag object
*/ */
public function GetTag($tag) public function GetTag($tag)
{ {
if (empty($tag)) if (empty($tag))
return null; return null;
   
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
$key = 'refs/tags/' . $tag; $key = 'refs/tags/' . $tag;
   
if (!isset($this->tags[$key])) { if (!isset($this->tags[$key])) {
$this->tags[$key] = $this->LoadTag($tag); $this->tags[$key] = $this->LoadTag($tag);
} }
   
return $this->tags[$key]; return $this->tags[$key];
} }
   
/** /**
* LoadTag * LoadTag
* *
* Attempts to load a cached tag, or creates a new object * Attempts to load a cached tag, or creates a new object
* *
* @access private * @access private
* @param string $tag tag to find * @param string $tag tag to find
* @return mixed tag object * @return mixed tag object
*/ */
private function LoadTag($tag, $hash = '') private function LoadTag($tag, $hash = '')
{ {
if (empty($tag)) if (empty($tag))
return; return;
   
$cacheKey = 'project|' . $this->project . '|tag|' . $tag; $cacheKey = 'project|' . $this->project . '|tag|' . $tag;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) { if ($cached) {
return $cached; return $cached;
} else { } else {
return new GitPHP_Tag($this, $tag, $hash); return new GitPHP_Tag($this, $tag, $hash);
} }
} }
   
  /*}}}2*/
   
  /* head loading methods {{{2*/
   
/** /**
* GetHeads * GetHeads
* *
* Gets list of heads for this project by age descending * Gets list of heads for this project by age descending
* *
* @access public * @access public
* @param integer $count number of tags to load * @param integer $count number of tags to load
* @return array array of heads * @return array array of heads
*/ */
public function GetHeads($count = 0) public function GetHeads($count = 0)
{ {
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
return $this->GetHeadsGit($count); return $this->GetHeadsGit($count);
} else { } else {
return $this->GetHeadsRaw($count); return $this->GetHeadsRaw($count);
} }
} }
   
/** /**
* GetHeadsGit * GetHeadsGit
* *
* Gets the list of sorted heads using the git executable * Gets the list of sorted heads using the git executable
* *
* @access private * @access private
* @param integer $count number of tags to load * @param integer $count number of tags to load
* @return array array of heads * @return array array of heads
*/ */
private function GetHeadsGit($count = 0) private function GetHeadsGit($count = 0)
{ {
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--sort=-committerdate'; $args[] = '--sort=-committerdate';
$args[] = '--format="%(refname)"'; $args[] = '--format="%(refname)"';
if ($count > 0) { if ($count > 0) {
$args[] = '--count=' . $count; $args[] = '--count=' . $count;
} }
$args[] = '--'; $args[] = '--';
$args[] = 'refs/heads'; $args[] = 'refs/heads';
$ret = $exe->Execute(GIT_FOR_EACH_REF, $args); $ret = $exe->Execute(GIT_FOR_EACH_REF, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
$heads = array(); $heads = array();
   
foreach ($lines as $ref) { foreach ($lines as $ref) {
if (isset($this->heads[$ref])) { if (isset($this->heads[$ref])) {
$heads[] = $this->heads[$ref]; $heads[] = $this->heads[$ref];
} }
} }
   
return $heads; return $heads;
} }
   
/** /**
* GetHeadsRaw * GetHeadsRaw
* *
* Gets the list of sorted heads using raw git objects * Gets the list of sorted heads using raw git objects
* *
* @access private * @access private
* @param integer $count number of tags to load * @param integer $count number of tags to load
* @return array array of heads * @return array array of heads
*/ */
private function GetHeadsRaw($count = 0) private function GetHeadsRaw($count = 0)
{ {
$heads = $this->heads; $heads = $this->heads;
usort($heads, array('GitPHP_Head', 'CompareAge')); usort($heads, array('GitPHP_Head', 'CompareAge'));
   
if (($count > 0) && (count($heads) > $count)) { if (($count > 0) && (count($heads) > $count)) {
$heads = array_slice($heads, 0, $count); $heads = array_slice($heads, 0, $count);
} }
return $heads; return $heads;
} }
   
/** /**
* GetHead * GetHead
* *
* Gets a single head * Gets a single head
* *
* @access public * @access public
* @param string $head head to find * @param string $head head to find
* @return mixed head object * @return mixed head object
*/ */
public function GetHead($head) public function GetHead($head)
{ {
if (empty($head)) if (empty($head))
return null; return null;
   
if (!$this->readRefs) if (!$this->readRefs)
$this->ReadRefList(); $this->ReadRefList();
   
$key = 'refs/heads/' . $head; $key = 'refs/heads/' . $head;
   
if (!isset($this->heads[$key])) { if (!isset($this->heads[$key])) {
$this->heads[$key] = new GitPHP_Head($this, $head); $this->heads[$key] = new GitPHP_Head($this, $head);
} }
   
return $this->heads[$key]; return $this->heads[$key];
} }
   
  /*}}}2*/
   
  /* log methods {{{2*/
   
/** /**
* GetLogHash * GetLogHash
* *
* Gets log entries as an array of hashes * Gets log entries as an array of hashes
* *
* @access private * @access private
* @param string $hash hash to start the log at * @param string $hash hash to start the log at
* @param integer $count number of entries to get * @param integer $count number of entries to get
* @param integer $skip number of entries to skip * @param integer $skip number of entries to skip
* @return array array of hashes * @return array array of hashes
*/ */
private function GetLogHash($hash, $count = 50, $skip = 0) private function GetLogHash($hash, $count = 50, $skip = 0)
{ {
return $this->RevList($hash, $count, $skip); return $this->RevList($hash, $count, $skip);
} }
   
/** /**
* GetLog * GetLog
* *
* Gets log entries as an array of commit objects * Gets log entries as an array of commit objects
* *
* @access public * @access public
* @param string $hash hash to start the log at * @param string $hash hash to start the log at
* @param integer $count number of entries to get * @param integer $count number of entries to get
* @param integer $skip number of entries to skip * @param integer $skip number of entries to skip
* @return array array of commit objects * @return array array of commit objects
*/ */
public function GetLog($hash, $count = 50, $skip = 0) public function GetLog($hash, $count = 50, $skip = 0)
{ {
if (GitPHP_Config::GetInstance()->GetValue('compat', false) || ($skip > GitPHP_Config::GetInstance()->GetValue('largeskip', 200)) ) { if (GitPHP_Config::GetInstance()->GetValue('compat', false) || ($skip > GitPHP_Config::GetInstance()->GetValue('largeskip', 200)) ) {
return $this->GetLogGit($hash, $count, $skip); return $this->GetLogGit($hash, $count, $skip);
} else { } else {
return $this->GetLogRaw($hash, $count, $skip); return $this->GetLogRaw($hash, $count, $skip);
} }
} }
   
/** /**
* GetLogGit * GetLogGit
* *
* Gets log entries using git exe * Gets log entries using git exe
* *
* @access private * @access private
* @param string $hash hash to start the log at * @param string $hash hash to start the log at
* @param integer $count number of entries to get * @param integer $count number of entries to get
* @param integer $skip number of entries to skip * @param integer $skip number of entries to skip
* @return array array of commit objects * @return array array of commit objects
*/ */
private function GetLogGit($hash, $count = 50, $skip = 0) private function GetLogGit($hash, $count = 50, $skip = 0)
{ {
$log = $this->GetLogHash($hash, $count, $skip); $log = $this->GetLogHash($hash, $count, $skip);
$len = count($log); $len = count($log);
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
$log[$i] = $this->GetCommit($log[$i]); $log[$i] = $this->GetCommit($log[$i]);
} }
return $log; return $log;
} }
   
/** /**
* GetLogRaw * GetLogRaw
* *
* Gets log entries using raw git objects * Gets log entries using raw git objects
* Based on history walking code from glip * Based on history walking code from glip
* *
* @access private * @access private
*/ */
private function GetLogRaw($hash, $count = 50, $skip = 0) private function GetLogRaw($hash, $count = 50, $skip = 0)
{ {
$total = $count + $skip; $total = $count + $skip;
   
$inc = array(); $inc = array();
$num = 0; $num = 0;
$queue = array($this->GetCommit($hash)); $queue = array($this->GetCommit($hash));
while (($commit = array_shift($queue)) !== null) { while (($commit = array_shift($queue)) !== null) {
$parents = $commit->GetParents(); $parents = $commit->GetParents();
foreach ($parents as $parent) { foreach ($parents as $parent) {
if (!isset($inc[$parent->GetHash()])) { if (!isset($inc[$parent->GetHash()])) {
$inc[$parent->GetHash()] = 1; $inc[$parent->GetHash()] = 1;
$queue[] = $parent; $queue[] = $parent;
$num++; $num++;
} else { } else {
$inc[$parent->GetHash()]++; $inc[$parent->GetHash()]++;
} }
} }
if ($num >= $total) if ($num >= $total)
break; break;
} }
   
$queue = array($this->GetCommit($hash)); $queue = array($this->GetCommit($hash));
$log = array(); $log = array();
$num = 0; $num = 0;
while (($commit = array_pop($queue)) !== null) { while (($commit = array_pop($queue)) !== null) {
array_push($log, $commit); array_push($log, $commit);
$num++; $num++;
if ($num == $total) { if ($num == $total) {
break; break;
} }
$parents = $commit->GetParents(); $parents = $commit->GetParents();
foreach ($parents as $parent) { foreach ($parents as $parent) {
if (isset($inc[$parent->GetHash()])) { if (isset($inc[$parent->GetHash()])) {
if (--$inc[$parent->GetHash()] == 0) { if (--$inc[$parent->GetHash()] == 0) {
$queue[] = $parent; $queue[] = $parent;
} }
} }
} }
} }
   
if ($skip > 0) { if ($skip > 0) {
$log = array_slice($log, $skip, $count); $log = array_slice($log, $skip, $count);
} }
usort($log, array('GitPHP_Commit', 'CompareAge')); usort($log, array('GitPHP_Commit', 'CompareAge'));
return $log; return $log;
} }
   
  /*}}}2*/
   
  /* blob loading methods {{{2*/
   
/** /**
* GetBlob * GetBlob
* *
* Gets a blob from this project * Gets a blob from this project
* *
* @access public * @access public
* @param string $hash blob hash * @param string $hash blob hash
*/ */
public function GetBlob($hash) public function GetBlob($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
$cacheKey = 'project|' . $this->project . '|blob|' . $hash; $cacheKey = 'project|' . $this->project . '|blob|' . $hash;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) if ($cached)
return $cached; return $cached;
   
return new GitPHP_Blob($this, $hash); return new GitPHP_Blob($this, $hash);
} }
   
  /*}}}2*/
   
  /* tree loading methods {{{2*/
   
/** /**
* GetTree * GetTree
* *
* Gets a tree from this project * Gets a tree from this project
* *
* @access public * @access public
* @param string $hash tree hash * @param string $hash tree hash
*/ */
public function GetTree($hash) public function GetTree($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
$cacheKey = 'project|' . $this->project . '|tree|' . $hash; $cacheKey = 'project|' . $this->project . '|tree|' . $hash;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) if ($cached)
return $cached; return $cached;
   
return new GitPHP_Tree($this, $hash); return new GitPHP_Tree($this, $hash);
} }
   
/** /*}}}2*/
* SearchCommit  
* /* raw object loading methods {{{2*/
* Gets a list of commits with commit messages matching the given pattern  
*  
* @access public  
* @param string $pattern search pattern  
* @param string $hash hash to start searching from  
* @param integer $count number of results to get  
* @param integer $skip number of results to skip  
* @return array array of matching commits  
*/  
public function SearchCommit($pattern, $hash = 'HEAD', $count = 50, $skip = 0)  
{  
if (empty($pattern))  
return;  
   
$args = array();  
   
$exe = new GitPHP_GitExe($this);  
if ($exe->CanIgnoreRegexpCase())  
$args[] = '--regexp-ignore-case';  
unset($exe);  
   
$args[] = '--grep=\'' . $pattern . '\'';  
   
$ret = $this->RevList($hash, $count, $skip, $args);  
$len = count($ret);  
   
for ($i = 0; $i < $len; ++$i) {  
$ret[$i] = $this->GetCommit($ret[$i]);  
}  
return $ret;  
}  
   
/**  
* SearchAuthor  
*  
* Gets a list of commits with authors matching the given pattern  
*  
* @access public  
* @param string $pattern search pattern  
* @param string $hash hash to start searching from  
* @param integer $count number of results to get  
* @param integer $skip number of results to skip  
* @return array array of matching commits  
*/  
public function SearchAuthor($pattern, $hash = 'HEAD', $count = 50, $skip = 0)  
{  
if (empty($pattern))  
return;  
   
$args = array();  
   
$exe = new GitPHP_GitExe($this);  
if ($exe->CanIgnoreRegexpCase())  
$args[] = '--regexp-ignore-case';  
unset($exe);  
   
$args[] = '--author=\'' . $pattern . '\'';  
   
$ret = $this->RevList($hash, $count, $skip, $args);  
$len = count($ret);  
   
for ($i = 0; $i < $len; ++$i) {  
$ret[$i] = $this->GetCommit($ret[$i]);  
}  
return $ret;  
}  
   
/**  
* SearchCommitter  
*  
* Gets a list of commits with committers matching the given pattern  
*  
* @access public  
* @param string $pattern search pattern  
* @param string $hash hash to start searching from  
* @param integer $count number of results to get  
* @param integer $skip number of results to skip  
* @return array array of matching commits  
*/  
public function SearchCommitter($pattern, $hash = 'HEAD', $count = 50, $skip = 0)  
{  
if (empty($pattern))  
return;  
   
$args = array();  
   
$exe = new GitPHP_GitExe($this);  
if ($exe->CanIgnoreRegexpCase())  
$args[] = '--regexp-ignore-case';  
unset($exe);  
   
$args[] = '--committer=\'' . $pattern . '\'';  
   
$ret = $this->RevList($hash, $count, $skip, $args);  
$len = count($ret);  
   
for ($i = 0; $i < $len; ++$i) {  
$ret[$i] = $this->GetCommit($ret[$i]);  
}  
return $ret;  
}  
   
/**  
* RevList  
*  
* Common code for using rev-list command  
*  
* @access private  
* @param string $hash hash to list from  
* @param integer $count number of results to get  
* @param integer $skip number of results to skip  
* @param array $args args to give to rev-list  
* @return array array of hashes  
*/  
private function RevList($hash, $count = 50, $skip = 0, $args = array())  
{  
if ($count < 1)  
return;  
   
$exe = new GitPHP_GitExe($this);  
   
$canSkip = true;  
   
if ($skip > 0)  
$canSkip = $exe->CanSkip();  
   
if ($canSkip) {  
$args[] = '--max-count=' . $count;  
if ($skip > 0) {  
$args[] = '--skip=' . $skip;  
}  
} else {  
$args[] = '--max-count=' . ($count + $skip);  
}  
   
$args[] = $hash;  
   
$revlist = explode("\n", $exe->Execute(GIT_REV_LIST, $args));  
   
if (!$revlist[count($revlist)-1]) {  
/* the last newline creates a null entry */  
array_splice($revlist, -1, 1);  
}  
   
if (($skip > 0) && (!$exe->CanSkip())) {  
return array_slice($revlist, $skip, $count);  
}  
   
return $revlist;  
}  
   
/**  
* GetEpoch  
*  
* Gets this project's epoch  
* (time of last change)  
*  
* @access public  
* @return integer timestamp  
*/  
public function GetEpoch()  
{  
if (!$this->epochRead)  
$this->ReadEpoch();  
   
return $this->epoch;  
}  
   
/**  
* GetAge  
*  
* Gets this project's age  
* (time since most recent change)  
*  
* @access public  
* @return integer age  
*/  
public function GetAge()  
{  
if (!$this->epochRead)  
$this->ReadEpoch();  
   
return time() - $this->epoch;  
}  
   
/**  
* ReadEpoch  
*  
* Reads this project's epoch  
* (timestamp of most recent change)  
*  
* @access private  
*/  
private function ReadEpoch()  
{  
$this->epochRead = true;  
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {  
$this->ReadEpochGit();  
} else {  
$this->ReadEpochRaw();  
}  
}  
   
/**  
* ReadEpochGit  
*  
* Reads this project's epoch using git executable  
*  
* @access private  
*/  
private function ReadEpochGit()  
{  
$exe = new GitPHP_GitExe($this);  
   
$args = array();  
$args[] = '--format="%(committer)"';  
$args[] = '--sort=-committerdate';  
$args[] = '--count=1';  
$args[] = 'refs/heads';  
   
$epochstr = trim($exe->Execute(GIT_FOR_EACH_REF, $args));  
   
if (preg_match('/ (\d+) [-+][01]\d\d\d$/', $epochstr, $regs)) {  
$this->epoch = $regs[1];  
}  
   
unset($exe);  
}  
   
/**  
* ReadEpochRaw  
*  
* Reads this project's epoch using raw objects  
*  
* @access private  
*/  
private function ReadEpochRaw()  
{  
if (!$this->readRefs)  
$this->ReadRefList();  
   
$epoch = 0;  
foreach ($this->heads as $head) {  
$commit = $head->GetCommit();  
if ($commit) {  
if ($commit->GetCommitterEpoch() > $epoch) {  
$epoch = $commit->GetCommitterEpoch();  
}  
}  
}  
if ($epoch > 0) {  
$this->epoch = $epoch;  
}  
}  
   
/** /**
* GetObject * GetObject
* *
* Gets the raw content of an object * Gets the raw content of an object
* *
* @access public * @access public
* @param string $hash object hash * @param string $hash object hash
* @return string object data * @return string object data
*/ */
public function GetObject($hash, &$type = 0) public function GetObject($hash, &$type = 0)
{ {
if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) { if (!preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) {
return false; return false;
} }
   
// first check if it's unpacked // first check if it's unpacked
$path = $this->GetPath() . '/objects/' . substr($hash, 0, 2) . '/' . substr($hash, 2); $path = $this->GetPath() . '/objects/' . substr($hash, 0, 2) . '/' . substr($hash, 2);
if (file_exists($path)) { if (file_exists($path)) {
list($header, $data) = explode("\0", gzuncompress(file_get_contents($path)), 2); list($header, $data) = explode("\0", gzuncompress(file_get_contents($path)), 2);
sscanf($header, "%s %d", $typestr, $size); sscanf($header, "%s %d", $typestr, $size);
switch ($typestr) { switch ($typestr) {
case 'commit': case 'commit':
$type = GitPHP_Pack::OBJ_COMMIT; $type = GitPHP_Pack::OBJ_COMMIT;
break; break;
case 'tree': case 'tree':
$type = GitPHP_Pack::OBJ_TREE; $type = GitPHP_Pack::OBJ_TREE;
break; break;
case 'blob': case 'blob':
$type = GitPHP_Pack::OBJ_BLOB; $type = GitPHP_Pack::OBJ_BLOB;
break; break;
case 'tag': case 'tag':
$type = GitPHP_Pack::OBJ_TAG; $type = GitPHP_Pack::OBJ_TAG;
break; break;
} }
return $data; return $data;
} }
   
if (!$this->packsRead) { if (!$this->packsRead) {
$this->ReadPacks(); $this->ReadPacks();
} }
   
// then try packs // then try packs
foreach ($this->packs as $pack) { foreach ($this->packs as $pack) {
$data = $pack->GetObject($hash, $type); $data = $pack->GetObject($hash, $type);
if ($data !== false) { if ($data !== false) {
return $data; return $data;
} }
} }
   
return false; return false;
} }
   
/** /**
* ReadPacks * ReadPacks
* *
* Read the list of packs in the repository * Read the list of packs in the repository
* *
* @access private * @access private
*/ */
private function ReadPacks() private function ReadPacks()
{ {
$dh = opendir($this->GetPath() . '/objects/pack'); $dh = opendir($this->GetPath() . '/objects/pack');
if ($dh !== false) { if ($dh !== false) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
if (preg_match('/^pack-([0-9A-Fa-f]{40})\.idx$/', $file, $regs)) { if (preg_match('/^pack-([0-9A-Fa-f]{40})\.idx$/', $file, $regs)) {
$this->packs[] = new GitPHP_Pack($this, $regs[1]); $this->packs[] = new GitPHP_Pack($this, $regs[1]);
} }
} }
} }
$this->packsRead = true; $this->packsRead = true;
} }
   
  /*}}}2*/
   
  /*}}}1*/
   
  /* search methods {{{1*/
   
  /**
  * SearchCommit
  *
  * Gets a list of commits with commit messages matching the given pattern
  *
  * @access public
  * @param string $pattern search pattern
  * @param string $hash hash to start searching from
  * @param integer $count number of results to get
  * @param integer $skip number of results to skip
  * @return array array of matching commits
  */
  public function SearchCommit($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
  {
  if (empty($pattern))
  return;
   
  $args = array();
   
  $exe = new GitPHP_GitExe($this);
  if ($exe->CanIgnoreRegexpCase())
  $args[] = '--regexp-ignore-case';
  unset($exe);
   
  $args[] = '--grep=\'' . $pattern . '\'';
   
  $ret = $this->RevList($hash, $count, $skip, $args);
  $len = count($ret);
   
  for ($i = 0; $i < $len; ++$i) {
  $ret[$i] = $this->GetCommit($ret[$i]);
  }
  return $ret;
  }
   
  /**
  * SearchAuthor
  *
  * Gets a list of commits with authors matching the given pattern
  *
  * @access public
  * @param string $pattern search pattern
  * @param string $hash hash to start searching from
  * @param integer $count number of results to get
  * @param integer $skip number of results to skip
  * @return array array of matching commits
  */
  public function SearchAuthor($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
  {
  if (empty($pattern))
  return;
   
  $args = array();
   
  $exe = new GitPHP_GitExe($this);
  if ($exe->CanIgnoreRegexpCase())
  $args[] = '--regexp-ignore-case';
  unset($exe);
   
  $args[] = '--author=\'' . $pattern . '\'';
   
  $ret = $this->RevList($hash, $count, $skip, $args);
  $len = count($ret);
   
  for ($i = 0; $i < $len; ++$i) {
  $ret[$i] = $this->GetCommit($ret[$i]);
  }
  return $ret;
  }
   
  /**
  * SearchCommitter
  *
  * Gets a list of commits with committers matching the given pattern
  *
  * @access public
  * @param string $pattern search pattern
  * @param string $hash hash to start searching from
  * @param integer $count number of results to get
  * @param integer $skip number of results to skip
  * @return array array of matching commits
  */
  public function SearchCommitter($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
  {
  if (empty($pattern))
  return;
   
  $args = array();
   
  $exe = new GitPHP_GitExe($this);
  if ($exe->CanIgnoreRegexpCase())
  $args[] = '--regexp-ignore-case';
  unset($exe);
   
  $args[] = '--committer=\'' . $pattern . '\'';
   
  $ret = $this->RevList($hash, $count, $skip, $args);
  $len = count($ret);
   
  for ($i = 0; $i < $len; ++$i) {
  $ret[$i] = $this->GetCommit($ret[$i]);
  }
  return $ret;
  }
   
  /*}}}1*/
   
  /* private utilities {{{1*/
   
  /**
  * RevList
  *
  * Common code for using rev-list command
  *
  * @access private
  * @param string $hash hash to list from
  * @param integer $count number of results to get
  * @param integer $skip number of results to skip
  * @param array $args args to give to rev-list
  * @return array array of hashes
  */
  private function RevList($hash, $count = 50, $skip = 0, $args = array())
  {
  if ($count < 1)
  return;
   
  $exe = new GitPHP_GitExe($this);
   
  $canSkip = true;
   
  if ($skip > 0)
  $canSkip = $exe->CanSkip();
   
  if ($canSkip) {
  $args[] = '--max-count=' . $count;
  if ($skip > 0) {
  $args[] = '--skip=' . $skip;
  }
  } else {
  $args[] = '--max-count=' . ($count + $skip);
  }
   
  $args[] = $hash;
   
  $revlist = explode("\n", $exe->Execute(GIT_REV_LIST, $args));
   
  if (!$revlist[count($revlist)-1]) {
  /* the last newline creates a null entry */
  array_splice($revlist, -1, 1);
  }
   
  if (($skip > 0) && (!$exe->CanSkip())) {
  return array_slice($revlist, $skip, $count);
  }
   
  return $revlist;
  }
   
  /*}}}1*/
   
  /* static utilities {{{1*/
   
  /**
  * CompareProject
  *
  * Compares two projects by project name
  *
  * @access public
  * @static
  * @param mixed $a first project
  * @param mixed $b second project
  * @return integer comparison result
  */
  public static function CompareProject($a, $b)
  {
  $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
  if ($catCmp !== 0)
  return $catCmp;
   
  return strcmp($a->GetProject(), $b->GetProject());
  }
   
  /**
  * CompareDescription
  *
  * Compares two projects by description
  *
  * @access public
  * @static
  * @param mixed $a first project
  * @param mixed $b second project
  * @return integer comparison result
  */
  public static function CompareDescription($a, $b)
  {
  $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
  if ($catCmp !== 0)
  return $catCmp;
   
  return strcmp($a->GetDescription(), $b->GetDescription());
  }
   
  /**
  * CompareOwner
  *
  * Compares two projects by owner
  *
  * @access public
  * @static
  * @param mixed $a first project
  * @param mixed $b second project
  * @return integer comparison result
  */
  public static function CompareOwner($a, $b)
  {
  $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
  if ($catCmp !== 0)
  return $catCmp;
   
  return strcmp($a->GetOwner(), $b->GetOwner());
  }
   
  /**
  * CompareAge
  *
  * Compares two projects by age
  *
  * @access public
  * @static
  * @param mixed $a first project
  * @param mixed $b second project
  * @return integer comparison result
  */
  public static function CompareAge($a, $b)
  {
  $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
  if ($catCmp !== 0)
  return $catCmp;
   
  if ($a->GetAge() === $b->GetAge())
  return 0;
  return ($a->GetAge() < $b->GetAge() ? -1 : 1);
  }
   
  /*}}}1*/
   
} }
   
<?php <?php
/** /**
* GitPHP ProjectListArray * GitPHP ProjectListArray
* *
* Lists all projects in a multidimensional array * Lists all projects in a multidimensional array
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php'); require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Project.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
   
/** /**
* ProjectListArray class * ProjectListArray class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_ProjectListArray extends GitPHP_ProjectListBase class GitPHP_ProjectListArray extends GitPHP_ProjectListBase
{ {
   
/** /**
* __construct * __construct
* *
* constructor * constructor
* *
* @param mixed $projectArray array to read * @param mixed $projectArray array to read
* @throws Exception if parameter is not an array * @throws Exception if parameter is not an array
* @access public * @access public
*/ */
public function __construct($projectArray) public function __construct($projectArray)
{ {
if (!is_array($projectArray)) { if (!is_array($projectArray)) {
throw new Exception('An array of projects is required'); throw new Exception('An array of projects is required');
} }
   
$this->projectConfig = $projectArray; $this->projectConfig = $projectArray;
   
parent::__construct(); parent::__construct();
} }
   
/** /**
* PopulateProjects * PopulateProjects
* *
* Populates the internal list of projects * Populates the internal list of projects
* *
* @access protected * @access protected
* @throws Exception if file cannot be read * @throws Exception if file cannot be read
*/ */
protected function PopulateProjects() protected function PopulateProjects()
{ {
$projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot')); $projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot'));
   
foreach ($this->projectConfig as $proj => $projData) { foreach ($this->projectConfig as $proj => $projData) {
try { try {
if (is_string($projData)) { if (is_string($projData)) {
// Just flat array of project paths // Just flat array of project paths
$projObj = new GitPHP_Project($projectRoot, $projData); $projObj = new GitPHP_Project($projectRoot, $projData);
$this->projects[$projData] = $projObj; $this->projects[$projData] = $projObj;
} else if (is_array($projData)) { } else if (is_array($projData)) {
if (is_string($proj) && !empty($proj)) { if (is_string($proj) && !empty($proj)) {
// Project key pointing to data array // Project key pointing to data array
$projObj = new GitPHP_Project($projectRoot, $proj); $projObj = new GitPHP_Project($projectRoot, $proj);
$this->projects[$proj] = $projObj; $this->projects[$proj] = $projObj;
$this->ApplyProjectSettings($proj, $projData); $this->ApplyProjectSettings($proj, $projData);
} else if (isset($projData['project'])) { } else if (isset($projData['project'])) {
// List of data arrays with projects inside // List of data arrays with projects inside
$projObj = new GitPHP_Project($projectRoot, $projData['project']); $projObj = new GitPHP_Project($projectRoot, $projData['project']);
$this->projects[$projData['project']] = $projObj; $this->projects[$projData['project']] = $projObj;
$this->ApplyProjectSettings(null, $projData); $this->ApplyProjectSettings(null, $projData);
} }
} }
} catch (Exception $e) { } catch (Exception $e) {
  GitPHP_Log::GetInstance()->Log($e->getMessage());
} }
} }
} }
   
} }
   
<?php <?php
/** /**
* GitPHP ProjectListArrayLegacy * GitPHP ProjectListArrayLegacy
* *
* Lists all projects in a multidimensional array * Lists all projects in a multidimensional array
* Legacy array format * Legacy array format
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php'); require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Project.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
   
define('GITPHP_NO_CATEGORY', 'none'); define('GITPHP_NO_CATEGORY', 'none');
   
/** /**
* ProjectListArrayLegacy class * ProjectListArrayLegacy class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_ProjectListArrayLegacy extends GitPHP_ProjectListBase class GitPHP_ProjectListArrayLegacy extends GitPHP_ProjectListBase
{ {
   
/** /**
* __construct * __construct
* *
* constructor * constructor
* *
* @param mixed $projectArray array to read * @param mixed $projectArray array to read
* @throws Exception if parameter is not an array * @throws Exception if parameter is not an array
* @access public * @access public
*/ */
public function __construct($projectArray) public function __construct($projectArray)
{ {
if (!is_array($projectArray)) { if (!is_array($projectArray)) {
throw new Exception('An array of projects is required.'); throw new Exception('An array of projects is required.');
} }
   
$this->projectConfig = $projectArray; $this->projectConfig = $projectArray;
   
parent::__construct(); parent::__construct();
} }
   
/** /**
* PopulateProjects * PopulateProjects
* *
* Populates the internal list of projects * Populates the internal list of projects
* *
* @access protected * @access protected
* @throws Exception if file cannot be read * @throws Exception if file cannot be read
*/ */
protected function PopulateProjects() protected function PopulateProjects()
{ {
$projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot')); $projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot'));
   
foreach ($this->projectConfig as $cat => $plist) { foreach ($this->projectConfig as $cat => $plist) {
if (is_array($plist)) { if (is_array($plist)) {
foreach ($plist as $pname => $ppath) { foreach ($plist as $pname => $ppath) {
try { try {
$projObj = new GitPHP_Project($projectRoot, $ppath); $projObj = new GitPHP_Project($projectRoot, $ppath);
if ($cat != GITPHP_NO_CATEGORY) if ($cat != GITPHP_NO_CATEGORY)
$projObj->SetCategory($cat); $projObj->SetCategory($cat);
$this->projects[$ppath] = $projObj; $this->projects[$ppath] = $projObj;
} catch (Exception $e) { } catch (Exception $e) {
  GitPHP_Log::GetInstance()->Log($e->getMessage());
} }
} }
} }
} }
} }
   
} }
   
<?php <?php
/** /**
* GitPHP ProjectListDirectory * GitPHP ProjectListDirectory
* *
* Lists all projects in a given directory * Lists all projects in a given directory
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_INCLUDEDIR . 'Config.class.php'); require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php'); require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Project.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
   
/** /**
* ProjectListDirectory class * ProjectListDirectory class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_ProjectListDirectory extends GitPHP_ProjectListBase class GitPHP_ProjectListDirectory extends GitPHP_ProjectListBase
{ {
/** /**
* projectDir * projectDir
* *
* Stores projectlist directory internally * Stores projectlist directory internally
* *
* @access protected * @access protected
*/ */
protected $projectDir; protected $projectDir;
   
/** /**
* __construct * __construct
* *
* constructor * constructor
* *
* @param string $projectDir directory to search * @param string $projectDir directory to search
* @throws Exception if parameter is not a directory * @throws Exception if parameter is not a directory
* @access public * @access public
*/ */
public function __construct($projectDir) public function __construct($projectDir)
{ {
if (!is_dir($projectDir)) { if (!is_dir($projectDir)) {
throw new Exception(sprintf(__('%1$s is not a directory'), $projectDir)); throw new Exception(sprintf(__('%1$s is not a directory'), $projectDir));
} }
   
$this->projectDir = GitPHP_Util::AddSlash($projectDir); $this->projectDir = GitPHP_Util::AddSlash($projectDir);
   
parent::__construct(); parent::__construct();
} }
   
/** /**
* PopulateProjects * PopulateProjects
* *
* Populates the internal list of projects * Populates the internal list of projects
* *
* @access protected * @access protected
*/ */
protected function PopulateProjects() protected function PopulateProjects()
{ {
$this->RecurseDir($this->projectDir); $this->RecurseDir($this->projectDir);
} }
   
/** /**
* RecurseDir * RecurseDir
* *
* Recursively searches for projects * Recursively searches for projects
* *
* @param string $dir directory to recurse into * @param string $dir directory to recurse into
*/ */
private function RecurseDir($dir) private function RecurseDir($dir)
{ {
if (!is_dir($dir)) if (!is_dir($dir))
return; return;
   
  GitPHP_Log::GetInstance()->Log(sprintf('Searching directory %1$s', $dir));
   
if ($dh = opendir($dir)) { if ($dh = opendir($dir)) {
$trimlen = strlen($this->projectDir) + 1; $trimlen = strlen($this->projectDir) + 1;
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
$fullPath = $dir . '/' . $file; $fullPath = $dir . '/' . $file;
if ((strpos($file, '.') !== 0) && is_dir($fullPath)) { if ((strpos($file, '.') !== 0) && is_dir($fullPath)) {
if (is_file($fullPath . '/HEAD')) { if (is_file($fullPath . '/HEAD')) {
  GitPHP_Log::GetInstance()->Log(sprintf('Found project %1$s', $fullPath));
$projectPath = substr($fullPath, $trimlen); $projectPath = substr($fullPath, $trimlen);
try { try {
$proj = new GitPHP_Project($this->projectDir, $projectPath); $proj = new GitPHP_Project($this->projectDir, $projectPath);
$proj->SetCategory(trim(substr($dir, strlen($this->projectDir)), '/')); $proj->SetCategory(trim(substr($dir, strlen($this->projectDir)), '/'));
if ((!GitPHP_Config::GetInstance()->GetValue('exportedonly', false)) || $proj->GetDaemonEnabled()) { if ((!GitPHP_Config::GetInstance()->GetValue('exportedonly', false)) || $proj->GetDaemonEnabled()) {
$this->projects[$projectPath] = $proj; $this->projects[$projectPath] = $proj;
} }
} catch (Exception $e) { } catch (Exception $e) {
  GitPHP_Log::GetInstance()->Log($e->getMessage());
} }
} else { } else {
$this->RecurseDir($fullPath); $this->RecurseDir($fullPath);
} }
  } else {
  GitPHP_Log::GetInstance()->Log(sprintf('Skipping %1$s', $fullPath));
} }
} }
closedir($dh); closedir($dh);
} }
} }
   
} }
   
<?php <?php
/** /**
* GitPHP ProjectListFile * GitPHP ProjectListFile
* *
* Lists all projects in a given file * Lists all projects in a given file
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_INCLUDEDIR . 'Config.class.php'); require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php'); require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Project.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
   
/** /**
* ProjectListFile class * ProjectListFile class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_ProjectListFile extends GitPHP_ProjectListBase class GitPHP_ProjectListFile extends GitPHP_ProjectListBase
{ {
/** /**
* __construct * __construct
* *
* constructor * constructor
* *
* @param string $projectFile file to read * @param string $projectFile file to read
* @throws Exception if parameter is not a readable file * @throws Exception if parameter is not a readable file
* @access public * @access public
*/ */
public function __construct($projectFile) public function __construct($projectFile)
{ {
if (!(is_string($projectFile) && is_file($projectFile))) { if (!(is_string($projectFile) && is_file($projectFile))) {
throw new Exception(sprintf(__('%1$s is not a file'), $projectFile)); throw new Exception(sprintf(__('%1$s is not a file'), $projectFile));
} }
   
$this->projectConfig = $projectFile; $this->projectConfig = $projectFile;
   
parent::__construct(); parent::__construct();
} }
   
/** /**
* PopulateProjects * PopulateProjects
* *
* Populates the internal list of projects * Populates the internal list of projects
* *
* @access protected * @access protected
* @throws Exception if file cannot be read * @throws Exception if file cannot be read
*/ */
protected function PopulateProjects() protected function PopulateProjects()
{ {
if (!($fp = fopen($this->projectConfig, 'r'))) { if (!($fp = fopen($this->projectConfig, 'r'))) {
throw new Exception(sprintf(__('Failed to open project list file %1$s'), $this->projectConfig)); throw new Exception(sprintf(__('Failed to open project list file %1$s'), $this->projectConfig));
} }
   
$projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot')); $projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot'));
   
while (!feof($fp) && ($line = fgets($fp))) { while (!feof($fp) && ($line = fgets($fp))) {
if (preg_match('/^([^\s]+)(\s.+)?$/', $line, $regs)) { if (preg_match('/^([^\s]+)(\s.+)?$/', $line, $regs)) {
if (is_file($projectRoot . $regs[1] . '/HEAD')) { if (is_file($projectRoot . $regs[1] . '/HEAD')) {
try { try {
$projObj = new GitPHP_Project($projectRoot, $regs[1]); $projObj = new GitPHP_Project($projectRoot, $regs[1]);
if (isset($regs[2]) && !empty($regs[2])) { if (isset($regs[2]) && !empty($regs[2])) {
$projOwner = trim($regs[2]); $projOwner = trim($regs[2]);
if (!empty($projOwner)) { if (!empty($projOwner)) {
$projObj->SetOwner($projOwner); $projObj->SetOwner($projOwner);
} }
} }
$this->projects[$regs[1]] = $projObj; $this->projects[$regs[1]] = $projObj;
} catch (Exception $e) { } catch (Exception $e) {
  GitPHP_Log::GetInstance()->Log($e->getMessage());
} }
  } else {
  GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not a git project', $projectRoot . $regs[1]));
} }
} }
} }
   
fclose($fp); fclose($fp);
} }
   
} }
   
<?php <?php
/** /**
* GitPHP ProjectListScmManager * GitPHP ProjectListScmManager
* *
* Lists all projects in an scm-manager config file * Lists all projects in an scm-manager config file
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2011 Christopher Han * @copyright Copyright (c) 2011 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_INCLUDEDIR . 'Config.class.php'); require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php'); require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Project.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
   
/** /**
* ProjectListScmManager class * ProjectListScmManager class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_ProjectListScmManager extends GitPHP_ProjectListBase class GitPHP_ProjectListScmManager extends GitPHP_ProjectListBase
{ {
/** /**
* __construct * __construct
* *
* constructor * constructor
* *
* @param string $projectFile file to read * @param string $projectFile file to read
* @throws Exception if parameter is not a readable file * @throws Exception if parameter is not a readable file
* @access public * @access public
*/ */
public function __construct($projectFile) public function __construct($projectFile)
{ {
if (!(is_string($projectFile) && is_file($projectFile))) { if (!(is_string($projectFile) && is_file($projectFile))) {
throw new Exception(sprintf(__('%1$s is not a file'), $projectFile)); throw new Exception(sprintf(__('%1$s is not a file'), $projectFile));
} }
   
$this->projectConfig = $projectFile; $this->projectConfig = $projectFile;
   
parent::__construct(); parent::__construct();
} }
   
/** /**
* PopulateProjects * PopulateProjects
* *
* Populates the internal list of projects * Populates the internal list of projects
* *
* @access protected * @access protected
* @throws Exception if file cannot be read * @throws Exception if file cannot be read
*/ */
protected function PopulateProjects() protected function PopulateProjects()
{ {
$projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot')); $projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot'));
   
$use_errors = libxml_use_internal_errors(true); $use_errors = libxml_use_internal_errors(true);
   
$xml = simplexml_load_file($this->projectConfig); $xml = simplexml_load_file($this->projectConfig);
   
libxml_clear_errors(); libxml_clear_errors();
libxml_use_internal_errors($use_errors); libxml_use_internal_errors($use_errors);
   
if (!$xml) { if (!$xml) {
throw new Exception(sprintf('Could not load SCM manager config %1$s', $this->projectConfig)); throw new Exception(sprintf('Could not load SCM manager config %1$s', $this->projectConfig));
} }
   
foreach ($xml->repositories->repository as $repository) { foreach ($xml->repositories->repository as $repository) {
   
if ($repository->type != 'git') if ($repository->type != 'git') {
  GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not a git project', $repository->name));
continue; continue;
if ($repository->public != 'true') }
   
  if ($repository->public != 'true') {
  GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not public', $repository->name));
continue; continue;
  }
   
$projName = trim($repository->name); $projName = trim($repository->name);
if (empty($projName)) if (empty($projName))
continue; continue;
   
if (is_file($projectRoot . $projName . '/HEAD')) { if (is_file($projectRoot . $projName . '/HEAD')) {
try { try {
$projObj = new GitPHP_Project($projectRoot, $projName); $projObj = new GitPHP_Project($projectRoot, $projName);
$projOwner = trim($repository->contact); $projOwner = trim($repository->contact);
if (!empty($projOwner)) { if (!empty($projOwner)) {
$projObj->SetOwner($projOwner); $projObj->SetOwner($projOwner);
} }
$projDesc = trim($repository->description); $projDesc = trim($repository->description);
if (!empty($projDesc)) { if (!empty($projDesc)) {
$projObj->SetDescription($projDesc); $projObj->SetDescription($projDesc);
} }
$this->projects[$projName] = $projObj; $this->projects[$projName] = $projObj;
} catch (Exception $e) { } catch (Exception $e) {
  GitPHP_Log::GetInstance()->Log($e->getMessage());
} }
  } else {
  GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not a git project', $projName));
} }
} }
} }
   
/** /**
* IsSCMManager * IsSCMManager
* *
* Tests if this file is an SCM manager config file * Tests if this file is an SCM manager config file
* *
* @access protected * @access protected
* @returns true if file is an SCM manager config * @returns true if file is an SCM manager config
*/ */
public static function IsSCMManager($file) public static function IsSCMManager($file)
{ {
if (empty($file)) if (empty($file))
return false; return false;
   
if (!(is_string($file) && is_file($file))) if (!(is_string($file) && is_file($file)))
return false; return false;
   
$use_errors = libxml_use_internal_errors(true); $use_errors = libxml_use_internal_errors(true);
   
$xml = simplexml_load_file($file); $xml = simplexml_load_file($file);
   
libxml_clear_errors(); libxml_clear_errors();
libxml_use_internal_errors($use_errors); libxml_use_internal_errors($use_errors);
   
if (!$xml) if (!$xml)
return false; return false;
   
if ($xml->getName() !== 'repository-db') if ($xml->getName() !== 'repository-db')
return false; return false;
   
return true; return true;
} }
   
} }
   
file:a/index.php -> file:b/index.php
<?php <?php
/** /**
* GitPHP * GitPHP
* *
* Index * Index
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
*/ */
   
/** /**
* Define start time / memory for benchmarking * Define start time / memory for benchmarking
*/ */
define('GITPHP_START_TIME', microtime(true)); define('GITPHP_START_TIME', microtime(true));
define('GITPHP_START_MEM', memory_get_usage()); define('GITPHP_START_MEM', memory_get_usage());
   
/** /**
* Define some paths * Define some paths
*/ */
define('GITPHP_BASEDIR', dirname(__FILE__) . '/'); define('GITPHP_BASEDIR', dirname(__FILE__) . '/');
define('GITPHP_CONFIGDIR', GITPHP_BASEDIR . 'config/'); define('GITPHP_CONFIGDIR', GITPHP_BASEDIR . 'config/');
define('GITPHP_INCLUDEDIR', GITPHP_BASEDIR . 'include/'); define('GITPHP_INCLUDEDIR', GITPHP_BASEDIR . 'include/');
define('GITPHP_GITOBJECTDIR', GITPHP_INCLUDEDIR . 'git/'); define('GITPHP_GITOBJECTDIR', GITPHP_INCLUDEDIR . 'git/');
define('GITPHP_CONTROLLERDIR', GITPHP_INCLUDEDIR . 'controller/'); define('GITPHP_CONTROLLERDIR', GITPHP_INCLUDEDIR . 'controller/');
define('GITPHP_CACHEDIR', GITPHP_INCLUDEDIR . 'cache/'); define('GITPHP_CACHEDIR', GITPHP_INCLUDEDIR . 'cache/');
define('GITPHP_LOCALEDIR', GITPHP_BASEDIR . 'locale/'); define('GITPHP_LOCALEDIR', GITPHP_BASEDIR . 'locale/');
   
include_once(GITPHP_INCLUDEDIR . 'version.php'); include_once(GITPHP_INCLUDEDIR . 'version.php');
   
require_once(GITPHP_INCLUDEDIR . 'Util.class.php'); require_once(GITPHP_INCLUDEDIR . 'Util.class.php');
   
require_once(GITPHP_INCLUDEDIR . 'Config.class.php'); require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
   
require_once(GITPHP_INCLUDEDIR . 'Resource.class.php'); require_once(GITPHP_INCLUDEDIR . 'Resource.class.php');
   
require_once(GITPHP_INCLUDEDIR . 'Log.class.php'); require_once(GITPHP_INCLUDEDIR . 'Log.class.php');
   
require_once(GITPHP_GITOBJECTDIR . 'ProjectList.class.php'); require_once(GITPHP_GITOBJECTDIR . 'ProjectList.class.php');
   
require_once(GITPHP_INCLUDEDIR . 'MessageException.class.php'); require_once(GITPHP_INCLUDEDIR . 'MessageException.class.php');
   
require_once(GITPHP_CONTROLLERDIR . 'Controller.class.php'); require_once(GITPHP_CONTROLLERDIR . 'Controller.class.php');
   
require_once(GITPHP_CACHEDIR . 'Cache.class.php'); require_once(GITPHP_CACHEDIR . 'Cache.class.php');
   
// Need this include for the compression constants used in the config file // Need this include for the compression constants used in the config file
require_once(GITPHP_GITOBJECTDIR . 'Archive.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Archive.class.php');
   
// Test these executables early // Test these executables early
require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php'); require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
require_once(GITPHP_GITOBJECTDIR . 'DiffExe.class.php'); require_once(GITPHP_GITOBJECTDIR . 'DiffExe.class.php');
   
date_default_timezone_set('UTC'); date_default_timezone_set('UTC');
   
   
/* /*
* Set the locale based on the user's preference * Set the locale based on the user's preference
*/ */
if ((!isset($_COOKIE[GITPHP_LOCALE_COOKIE])) || empty($_COOKIE[GITPHP_LOCALE_COOKIE])) { if ((!isset($_COOKIE[GITPHP_LOCALE_COOKIE])) || empty($_COOKIE[GITPHP_LOCALE_COOKIE])) {
   
/* /*
* User's first time here, try by HTTP_ACCEPT_LANGUAGE * User's first time here, try by HTTP_ACCEPT_LANGUAGE
*/ */
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$httpAcceptLang = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); $httpAcceptLang = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
$preferredLocale = GitPHP_Resource::FindPreferredLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']); $preferredLocale = GitPHP_Resource::FindPreferredLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
if (!empty($preferredLocale)) { if (!empty($preferredLocale)) {
setcookie(GITPHP_LOCALE_COOKIE, $preferredLocale, time()+GITPHP_LOCALE_COOKIE_LIFETIME); setcookie(GITPHP_LOCALE_COOKIE, $preferredLocale, time()+GITPHP_LOCALE_COOKIE_LIFETIME);
GitPHP_Resource::Instantiate($preferredLocale); GitPHP_Resource::Instantiate($preferredLocale);
} }
} }
   
if (!GitPHP_Resource::Instantiated()) { if (!GitPHP_Resource::Instantiated()) {
/* /*
* Create a dummy cookie to prevent browser delay * Create a dummy cookie to prevent browser delay
*/ */
setcookie(GITPHP_LOCALE_COOKIE, 0, time()+GITPHP_LOCALE_COOKIE_LIFETIME); setcookie(GITPHP_LOCALE_COOKIE, 0, time()+GITPHP_LOCALE_COOKIE_LIFETIME);
} }
   
} else if (isset($_GET['l']) && !empty($_GET['l'])) { } else if (isset($_GET['l']) && !empty($_GET['l'])) {
   
/* /*
* User picked something * User picked something
*/ */
setcookie(GITPHP_LOCALE_COOKIE, $_GET['l'], time()+GITPHP_LOCALE_COOKIE_LIFETIME); setcookie(GITPHP_LOCALE_COOKIE, $_GET['l'], time()+GITPHP_LOCALE_COOKIE_LIFETIME);
GitPHP_Resource::Instantiate($_GET['l']); GitPHP_Resource::Instantiate($_GET['l']);
   
} else if (isset($_COOKIE[GITPHP_LOCALE_COOKIE]) && !empty($_COOKIE[GITPHP_LOCALE_COOKIE])) { } else if (isset($_COOKIE[GITPHP_LOCALE_COOKIE]) && !empty($_COOKIE[GITPHP_LOCALE_COOKIE])) {
   
/* /*
* Returning visitor with a preference * Returning visitor with a preference
*/ */
GitPHP_Resource::Instantiate($_COOKIE[GITPHP_LOCALE_COOKIE]); GitPHP_Resource::Instantiate($_COOKIE[GITPHP_LOCALE_COOKIE]);
   
} }
   
   
try { try {
   
/* /*
* Configuration * Configuration
*/ */
GitPHP_Config::GetInstance()->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php'); GitPHP_Config::GetInstance()->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php');
   
/* /*
* Use the default language in the config if user has no preference * Use the default language in the config if user has no preference
* with en_US as the fallback * with en_US as the fallback
*/ */
if (!GitPHP_Resource::Instantiated()) { if (!GitPHP_Resource::Instantiated()) {
GitPHP_Resource::Instantiate(GitPHP_Config::GetInstance()->GetValue('locale', 'en_US')); GitPHP_Resource::Instantiate(GitPHP_Config::GetInstance()->GetValue('locale', 'en_US'));
} }
   
/* /*
* Debug * Debug
*/ */
if (GitPHP_Log::GetInstance()->GetEnabled()) { if (GitPHP_Log::GetInstance()->GetEnabled()) {
GitPHP_Log::GetInstance()->SetStartTime(GITPHP_START_TIME); GitPHP_Log::GetInstance()->SetStartTime(GITPHP_START_TIME);
GitPHP_Log::GetInstance()->SetStartMemory(GITPHP_START_MEM); GitPHP_Log::GetInstance()->SetStartMemory(GITPHP_START_MEM);
} }
   
if (!GitPHP_Config::GetInstance()->GetValue('projectroot', null)) { if (!GitPHP_Config::GetInstance()->GetValue('projectroot', null)) {
throw new GitPHP_MessageException(__('A projectroot must be set in the config'), true, 500); throw new GitPHP_MessageException(__('A projectroot must be set in the config'), true, 500);
} }
   
/* /*
* Check for required executables * Check for required executables
*/ */
$exe = new GitPHP_GitExe(null); $exe = new GitPHP_GitExe(null);
if (!$exe->Valid()) { if (!$exe->Valid()) {
throw new GitPHP_MessageException(sprintf(__('Could not run the git executable "%1$s". You may need to set the "%2$s" config value.'), $exe->GetBinary(), 'gitbin'), true, 500); throw new GitPHP_MessageException(sprintf(__('Could not run the git executable "%1$s". You may need to set the "%2$s" config value.'), $exe->GetBinary(), 'gitbin'), true, 500);
} }
if (!function_exists('xdiff_string_diff')) { if (!function_exists('xdiff_string_diff')) {
$exe = new GitPHP_DiffExe(); $exe = new GitPHP_DiffExe();
if (!$exe->Valid()) { if (!$exe->Valid()) {
throw new GitPHP_MessageException(sprintf(__('Could not run the diff executable "%1$s". You may need to set the "%2$s" config value.'), $exe->GetBinary(), 'diffbin'), true, 500); throw new GitPHP_MessageException(sprintf(__('Could not run the diff executable "%1$s". You may need to set the "%2$s" config value.'), $exe->GetBinary(), 'diffbin'), true, 500);
} }
} }
unset($exe); unset($exe);
   
/* /*
* Project list * Project list
*/ */
if (file_exists(GITPHP_CONFIGDIR . 'projects.conf.php')) { if (file_exists(GITPHP_CONFIGDIR . 'projects.conf.php')) {
GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'projects.conf.php', false); GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'projects.conf.php', false);
} else { } else {
GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'gitphp.conf.php', true); GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'gitphp.conf.php', true);
} }
   
$controller = GitPHP_Controller::GetController((isset($_GET['a']) ? $_GET['a'] : null)); $controller = GitPHP_Controller::GetController((isset($_GET['a']) ? $_GET['a'] : null));
if ($controller) { if ($controller) {
$controller->RenderHeaders(); $controller->RenderHeaders();
$controller->Render(); $controller->Render();
} }
   
} catch (Exception $e) { } catch (Exception $e) {
   
if (GitPHP_Config::GetInstance()->GetValue('debug', false)) { if (GitPHP_Config::GetInstance()->GetValue('debug', false)) {
throw $e; throw $e;
} }
   
if (!GitPHP_Resource::Instantiated()) { if (!GitPHP_Resource::Instantiated()) {
/* /*
* In case an error was thrown before instantiating * In case an error was thrown before instantiating
* the resource manager * the resource manager
*/ */
GitPHP_Resource::Instantiate('en_US'); GitPHP_Resource::Instantiate('en_US');
} }
   
require_once(GITPHP_CONTROLLERDIR . 'Controller_Message.class.php'); require_once(GITPHP_CONTROLLERDIR . 'Controller_Message.class.php');
$controller = new GitPHP_Controller_Message(); $controller = new GitPHP_Controller_Message();
$controller->SetParam('message', $e->getMessage()); $controller->SetParam('message', $e->getMessage());
if ($e instanceof GitPHP_MessageException) { if ($e instanceof GitPHP_MessageException) {
$controller->SetParam('error', $e->Error); $controller->SetParam('error', $e->Error);
$controller->SetParam('statuscode', $e->StatusCode); $controller->SetParam('statuscode', $e->StatusCode);
} else { } else {
$controller->SetParam('error', true); $controller->SetParam('error', true);
} }
$controller->RenderHeaders(); $controller->RenderHeaders();
$controller->Render(); $controller->Render();
   
} }
   
if (GitPHP_Log::GetInstance()->GetEnabled()) { if (GitPHP_Log::GetInstance()->GetEnabled()) {
$entries = GitPHP_Log::GetInstance()->GetEntries(); $entries = GitPHP_Log::GetInstance()->GetEntries();
foreach ($entries as $logline) { foreach ($entries as $logline) {
echo "\n" . $logline; echo "<br />\n" . $logline;
} }
} }
   
?> ?>
   
comments