Add unit test for project load git strategy
[gitphp.git] / include / git / GitExe.class.php
blob:a/include/git/GitExe.class.php -> blob:b/include/git/GitExe.class.php
<?php <?php
   
/** /**
* git cat-file constant * git cat-file constant
*/ */
define('GIT_CAT_FILE','cat-file'); define('GIT_CAT_FILE','cat-file');
   
/** /**
* git diff-tree constant * git diff-tree constant
*/ */
define('GIT_DIFF_TREE','diff-tree'); define('GIT_DIFF_TREE','diff-tree');
   
/** /**
* git ls-tree constant * git ls-tree constant
*/ */
define('GIT_LS_TREE','ls-tree'); define('GIT_LS_TREE','ls-tree');
   
/** /**
* git rev-list constant * git rev-list constant
*/ */
define('GIT_REV_LIST','rev-list'); define('GIT_REV_LIST','rev-list');
   
/** /**
* git rev-parse constant * git rev-parse constant
*/ */
define('GIT_REV_PARSE','rev-parse'); define('GIT_REV_PARSE','rev-parse');
   
/** /**
* git show-ref constant * git show-ref constant
*/ */
define('GIT_SHOW_REF','show-ref'); define('GIT_SHOW_REF','show-ref');
   
/** /**
* git archive constant * git archive constant
*/ */
define('GIT_ARCHIVE','archive'); define('GIT_ARCHIVE','archive');
   
/** /**
* git grep constant * git grep constant
*/ */
define('GIT_GREP','grep'); define('GIT_GREP','grep');
   
/** /**
* git blame constant * git blame constant
*/ */
define('GIT_BLAME','blame'); define('GIT_BLAME','blame');
   
/** /**
* git name-rev constant * git name-rev constant
*/ */
define('GIT_NAME_REV','name-rev'); define('GIT_NAME_REV','name-rev');
   
/** /**
* git for-each-ref constant * git for-each-ref constant
*/ */
define('GIT_FOR_EACH_REF','for-each-ref'); define('GIT_FOR_EACH_REF','for-each-ref');
   
/** /**
* Class to wrap git executable * Class to wrap git executable
* *
* @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
*/ */
class GitPHP_GitExe implements GitPHP_Observable_Interface class GitPHP_GitExe implements GitPHP_Observable_Interface
{ {
   
/** /**
* The binary path * The binary path
* *
* @var string * @var string
*/ */
protected $binary; protected $binary;
/** /**
* The binary version * The binary version
* *
* @var string * @var string
*/ */
protected $version; protected $version;
   
/** /**
* Whether the version has been read * Whether the version has been read
* *
* @var boolean * @var boolean
*/ */
protected $versionRead = false; protected $versionRead = false;
   
/** /**
* Observers * Observers
* *
* @var GitPHP_Observer_Interface[] * @var GitPHP_Observer_Interface[]
*/ */
protected $observers = array(); protected $observers = array();
   
/** /**
  * Whether the exec function is allowed by the install
  *
  * @var null|boolean
  */
  protected $execAllowed = null;
   
  /**
  * Whether the shell_exec function is allowed by the install
  *
  * @var null|boolean
  */
  protected $shellExecAllowed = null;
   
  /**
  * Whether the popen function is allowed by the install
  *
  * @var null|boolean
  */
  protected $popenAllowed = null;
   
  /**
* Constructor * Constructor
* *
* @param string $binary path to git binary * @param string $binary path to git binary
*/ */
public function __construct($binary = '') public function __construct($binary = '')
{ {
if (empty($binary)) { if (empty($binary)) {
$binary = GitPHP_GitExe::DefaultBinary(); $binary = GitPHP_GitExe::DefaultBinary();
} }
$this->binary = $binary; $this->binary = $binary;
} }
   
/** /**
* Executes a command * Executes a command
* *
* @param string $projectPath path to project * @param string $projectPath path to project
* @param string $command the command to execute * @param string $command the command to execute
* @param string[] $args arguments * @param string[] $args arguments
* @return string result of command * @return string result of command
*/ */
public function Execute($projectPath, $command, $args) public function Execute($projectPath, $command, $args)
{ {
  if ($this->shellExecAllowed === null) {
  $this->shellExecAllowed = GitPHP_Util::FunctionAllowed('shell_exec');
  if (!$this->shellExecAllowed) {
  throw new GitPHP_DisabledFunctionException('shell_exec');
  }
  }
   
$fullCommand = $this->CreateCommand($projectPath, $command, $args); $fullCommand = $this->CreateCommand($projectPath, $command, $args);
   
$this->Log('Begin executing "' . $fullCommand . '"'); $this->Log('Begin executing "' . $fullCommand . '"');
   
$ret = shell_exec($fullCommand); $ret = shell_exec($fullCommand);
   
$this->Log('Finish executing "' . $fullCommand . '"' . $this->Log('Finish executing "' . $fullCommand . '"' .
"\nwith result: " . $ret); "\nwith result: " . $ret);
   
return $ret; return $ret;
} }
   
/** /**
* Opens a resource to a command * Opens a resource to a command
* *
* @param string $projectPath path to project * @param string $projectPath path to project
* @param string $command the command to execute * @param string $command the command to execute
* @param string[] $args arguments * @param string[] $args arguments
* @param string $mode process open mode * @param string $mode process open mode
* @return resource process handle * @return resource process handle
*/ */
public function Open($projectPath, $command, $args, $mode = 'r') public function Open($projectPath, $command, $args, $mode = 'r')
{ {
  if ($this->popenAllowed === null) {
  $this->popenAllowed = GitPHP_Util::FunctionAllowed('popen');
  if (!$this->popenAllowed) {
  throw new GitPHP_DisabledFunctionException('popen');
  }
  }
   
$fullCommand = $this->CreateCommand($projectPath, $command, $args); $fullCommand = $this->CreateCommand($projectPath, $command, $args);
   
return popen($fullCommand, $mode); return popen($fullCommand, $mode);
} }
   
/** /**
* Creates a command * Creates a command
* *
* @param string $projectPath path to project * @param string $projectPath path to project
* @param string $command the command to execute * @param string $command the command to execute
* @param string[] $args arguments * @param string[] $args arguments
* @return string full executable string * @return string full executable string
*/ */
protected function CreateCommand($projectPath, $command, $args) protected function CreateCommand($projectPath, $command, $args)
{ {
$gitDir = ''; $gitDir = '';
if (!empty($projectPath)) { if (!empty($projectPath)) {
$gitDir = '--git-dir=' . $projectPath; $gitDir = '--git-dir=' . escapeshellarg($projectPath);
} }
return $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args); return $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args);
} }
   
/** /**
* Gets the binary for this executable * Gets the binary for this executable
* *
* @return string binary * @return string binary
*/ */
public function GetBinary() public function GetBinary()
{ {
return $this->binary; return $this->binary;
} }
   
/** /**
* Gets the version of the git binary * Gets the version of the git binary
* *
* @return string version * @return string version
*/ */
public function GetVersion() public function GetVersion()
{ {
if (!$this->versionRead) if (!$this->versionRead)
$this->ReadVersion(); $this->ReadVersion();
   
return $this->version; return $this->version;
} }
   
/** /**
* Reads the git version * Reads the git version
*/ */
protected function ReadVersion() protected function ReadVersion()
{ {
  if ($this->shellExecAllowed === null) {
  $this->shellExecAllowed = GitPHP_Util::FunctionAllowed('shell_exec');
  if (!$this->shellExecAllowed) {
  throw new GitPHP_DisabledFunctionException('shell_exec');
  }
  }
   
$this->versionRead = true; $this->versionRead = true;
   
$this->version = ''; $this->version = '';
   
$versionCommand = $this->binary . ' --version'; $versionCommand = $this->binary . ' --version';
$ret = trim(shell_exec($versionCommand)); $ret = trim(shell_exec($versionCommand));
if (preg_match('/^git version ([0-9\.]+)$/i', $ret, $regs)) { if (preg_match('/^git version ([0-9\.]+)$/i', $ret, $regs)) {
$this->version = $regs[1]; $this->version = $regs[1];
} }
} }
   
/** /**
* Tests if this version of git can skip through the revision list * Tests if this version of git can skip through the revision list
* *
* @return boolean true if we can skip * @return boolean true if we can skip
*/ */
public function CanSkip() public function CanSkip()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* Skip only appears in git >= 1.5.0 */ /* Skip only appears in git >= 1.5.0 */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5))) {
return false; return false;
} }
} }
   
return true; return true;
} }
   
/** /**
* Tests if this version of git can show the size of a blob when listing a tree * Tests if this version of git can show the size of a blob when listing a tree
* *
* @return true if we can show sizes * @return true if we can show sizes
*/ */
public function CanShowSizeInTree() public function CanShowSizeInTree()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* /*
* ls-tree -l only appears in git 1.5.3 * ls-tree -l only appears in git 1.5.3
* (technically 1.5.3-rc0 but i'm not getting that fancy) * (technically 1.5.3-rc0 but i'm not getting that fancy)
*/ */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) {
return false; return false;
} }
} }
   
return true; return true;
   
} }
   
/** /**
* Tests if this version of git has the regexp tuning option to ignore regexp case * Tests if this version of git has the regexp tuning option to ignore regexp case
* *
* @return true if we can ignore regexp case * @return true if we can ignore regexp case
*/ */
public function CanIgnoreRegexpCase() public function CanIgnoreRegexpCase()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* /*
* regexp-ignore-case only appears in git 1.5.3 * regexp-ignore-case only appears in git 1.5.3
*/ */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) {
return false; return false;
} }
} }
   
return true; return true;
} }
   
/** /**
* Tests if this executable is valid * Tests if this executable is valid
* *
* @return boolean true if valid * @return boolean true if valid
*/ */
public function Valid() public function Valid()
{ {
  if ($this->execAllowed === null) {
  $this->execAllowed = GitPHP_Util::FunctionAllowed('exec');
  if (!$this->execAllowed) {
  throw new GitPHP_DisabledFunctionException('exec');
  }
  }
   
if (empty($this->binary)) if (empty($this->binary))
return false; return false;
   
$code = 0; $code = 0;
$out = exec($this->binary . ' --version', $tmp, $code); $out = exec($this->binary . ' --version', $tmp, $code);
   
return $code == 0; return $code == 0;
} }
   
/** /**
* Add a new observer * Add a new observer
* *
* @param GitPHP_Observer_Interface $observer observer * @param GitPHP_Observer_Interface $observer observer
*/ */
public function AddObserver($observer) public function AddObserver($observer)
{ {
if (!$observer) if (!$observer)
return; return;
   
if (array_search($observer, $this->observers) !== false) if (array_search($observer, $this->observers) !== false)
return; return;
   
$this->observers[] = $observer; $this->observers[] = $observer;
} }
   
/** /**
* Remove an observer * Remove an observer
* *
* @param GitPHP_Observer_Interface $observer observer * @param GitPHP_Observer_Interface $observer observer
*/ */
public function RemoveObserver($observer) public function RemoveObserver($observer)
{ {
if (!$observer) if (!$observer)
return; return;
   
$key = array_search($observer, $this->observers); $key = array_search($observer, $this->observers);
   
if ($key === false) if ($key === false)
return; return;
   
unset($this->observers[$key]); unset($this->observers[$key]);
} }
   
/** /**
* Log an execution * Log an execution
* *
* @param string $message message * @param string $message message
*/ */
private function Log($message) private function Log($message)
{ {
if (empty($message)) if (empty($message))
return; return;
   
foreach ($this->observers as $observer) { foreach ($this->observers as $observer) {
$observer->ObjectChanged($this, GitPHP_Observer_Interface::LoggableChange, array($message)); $observer->ObjectChanged($this, GitPHP_Observer_Interface::LoggableChange, array($message));
} }
} }
   
/** /**
* Gets the default binary for the platform * Gets the default binary for the platform
* *
* @return string binary * @return string binary
*/ */
public static function DefaultBinary() public static function DefaultBinary()
{ {
if (GitPHP_Util::IsWindows()) { if (GitPHP_Util::IsWindows()) {
// windows // windows
   
if (GitPHP_Util::Is64Bit()) { if (GitPHP_Util::Is64Bit()) {
// match x86_64 and x64 (64 bit) // match x86_64 and x64 (64 bit)
// C:\Program Files (x86)\Git\bin\git.exe // C:\Program Files (x86)\Git\bin\git.exe
return 'C:\\Progra~2\\Git\\bin\\git.exe'; return 'C:\\Progra~2\\Git\\bin\\git.exe';
} else { } else {
// 32 bit // 32 bit
// C:\Program Files\Git\bin\git.exe // C:\Program Files\Git\bin\git.exe
return 'C:\\Progra~1\\Git\\bin\\git.exe'; return 'C:\\Progra~1\\Git\\bin\\git.exe';
} }
} else { } else {
// *nix, just use PATH // *nix, just use PATH
return 'git'; return 'git';
} }
} }
   
} }
   
comments