PageRenderTime 36ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/include/git/Blob.class.php

http://github.com/tpruvot/GitPHP
PHP | 544 lines | 262 code | 65 blank | 217 comment | 58 complexity | 8634c6762d1c410d364ee35285e16263 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-3.0, LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * GitPHP Blob
  4. *
  5. * Represents a single blob
  6. *
  7. * @author Christopher Han <xiphux@gmail.com>
  8. * @copyright Copyright (c) 2010 Christopher Han
  9. * @package GitPHP
  10. * @subpackage Git
  11. */
  12. require_once(GITPHP_GITOBJECTDIR . 'FilesystemObject.class.php');
  13. require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
  14. /**
  15. * Commit class
  16. *
  17. * @package GitPHP
  18. * @subpackage Git
  19. */
  20. class GitPHP_Blob extends GitPHP_FilesystemObject
  21. {
  22. /**
  23. * data
  24. *
  25. * Stores the file data
  26. *
  27. * @access protected
  28. */
  29. protected $data;
  30. /**
  31. * dataRead
  32. *
  33. * Stores whether data has been read
  34. *
  35. * @access protected
  36. */
  37. protected $dataRead = false;
  38. /**
  39. * size
  40. *
  41. * Stores the size
  42. *
  43. * @access protected
  44. */
  45. protected $size = null;
  46. /**
  47. * history
  48. *
  49. * Stores the history
  50. *
  51. * @access protected
  52. */
  53. protected $history = array();
  54. /**
  55. * historyRead
  56. *
  57. * Stores whether the history has been read
  58. *
  59. * @access protected
  60. */
  61. protected $historyRead = false;
  62. /**
  63. * blame
  64. *
  65. * Stores blame info
  66. *
  67. * @access protected
  68. */
  69. protected $blame = array();
  70. /**
  71. * blameRead
  72. *
  73. * Stores whether blame was read
  74. *
  75. * @access protected
  76. */
  77. protected $blameRead = false;
  78. /**
  79. * __construct
  80. *
  81. * Instantiates object
  82. *
  83. * @access public
  84. * @param mixed $project the project
  85. * @param string $hash object hash
  86. * @return mixed blob object
  87. * @throws Exception exception on invalid hash
  88. */
  89. public function __construct($project, $hash)
  90. {
  91. parent::__construct($project, $hash);
  92. }
  93. /**
  94. * GetData
  95. *
  96. * Gets the blob data
  97. *
  98. * @access public
  99. * @param boolean $explode true to explode data into an array of lines
  100. * @return string blob data
  101. */
  102. public function GetData($explode = false)
  103. {
  104. if (!$this->dataRead)
  105. $this->ReadData();
  106. if ($explode)
  107. return explode("\n", $this->data);
  108. else
  109. return $this->data;
  110. }
  111. /**
  112. * ReadData
  113. *
  114. * Reads the blob data
  115. *
  116. * @access private
  117. */
  118. private function ReadData()
  119. {
  120. $this->dataRead = true;
  121. if ($this->GetProject()->GetCompat()) {
  122. $args = array();
  123. $args[] = 'blob';
  124. $args[] = $this->hash;
  125. $this->data = GitPHP_GitExe::GetInstance()->Execute($this->GetProject()->GetPath(), GIT_CAT_FILE, $args);
  126. } else {
  127. $this->data = $this->GetProject()->GetObject($this->hash);
  128. }
  129. GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
  130. }
  131. /**
  132. * FileType
  133. *
  134. * Gets a file type from its octal mode
  135. *
  136. * @access public
  137. * @static
  138. * @param string $octMode octal mode
  139. * @param boolean $local true if caller wants localized type
  140. * @return string file type
  141. */
  142. public static function FileType($octMode, $local = false)
  143. {
  144. $mode = octdec($octMode);
  145. if (($mode & 0x4000) == 0x4000) {
  146. if ($local) {
  147. return __('directory');
  148. } else {
  149. return 'directory';
  150. }
  151. } else if (($mode & 0xA000) == 0xA000) {
  152. if ($local) {
  153. return __('symlink');
  154. } else {
  155. return 'symlink';
  156. }
  157. } else if (($mode & 0x8000) == 0x8000) {
  158. if ($local) {
  159. return __('file');
  160. } else {
  161. return 'file';
  162. }
  163. }
  164. if ($local) {
  165. return __('unknown');
  166. } else {
  167. return 'unknown';
  168. }
  169. }
  170. /**
  171. * GetSize
  172. *
  173. * Gets the blob size
  174. *
  175. * @access public
  176. * @return integer size
  177. */
  178. public function GetSize()
  179. {
  180. if ($this->size !== null) {
  181. return $this->size;
  182. }
  183. if (!$this->dataRead)
  184. $this->ReadData();
  185. return strlen($this->data);
  186. }
  187. /**
  188. * SetSize
  189. *
  190. * Sets the blob size
  191. *
  192. * @access public
  193. * @param integer $size size
  194. */
  195. public function SetSize($size)
  196. {
  197. $this->size = $size;
  198. }
  199. /**
  200. * IsBinary
  201. *
  202. * Tests if this blob is a binary file
  203. *
  204. * @access public
  205. * @return boolean true if binary file
  206. */
  207. public function IsBinary()
  208. {
  209. if (!$this->dataRead)
  210. $this->ReadData();
  211. $data = $this->data;
  212. if (strlen($this->data) > 8000)
  213. $data = substr($data, 0, 8000);
  214. return strpos($data, chr(0)) !== false;
  215. }
  216. /**
  217. * FileMime
  218. *
  219. * Get the file mimetype
  220. *
  221. * @access public
  222. * @param boolean $short true to only the type group
  223. * @return string mime
  224. */
  225. public function FileMime($short = false)
  226. {
  227. $mime = $this->FileMime_Fileinfo();
  228. if (empty($mime))
  229. $mime = $this->FileMime_File();
  230. if (empty($mime))
  231. $mime = $this->FileMime_Extension();
  232. if ((!empty($mime)) && $short) {
  233. $mime = strtok($mime, '/');
  234. }
  235. return $mime;
  236. }
  237. /**
  238. * FileMime_Fileinfo
  239. *
  240. * Get the file mimetype using fileinfo
  241. *
  242. * @access private
  243. * @return string mimetype
  244. */
  245. private function FileMime_Fileinfo()
  246. {
  247. if (!function_exists('finfo_buffer'))
  248. return '';
  249. if (!$this->dataRead)
  250. $this->ReadData();
  251. if (!$this->data)
  252. return '';
  253. $mime = '';
  254. $magicdb = GitPHP_Config::GetInstance()->GetValue('magicdb', null);
  255. if (empty($magicdb)) {
  256. if (GitPHP_Util::IsWindows()) {
  257. $magicdb = 'C:\\wamp\\php\\extras\\magic';
  258. } else {
  259. $magicdb = '/usr/share/misc/magic';
  260. }
  261. }
  262. $finfo = @finfo_open(FILEINFO_MIME, $magicdb);
  263. if ($finfo) {
  264. $mime = finfo_buffer($finfo, $this->data, FILEINFO_MIME);
  265. if ($mime && strpos($mime, '/')) {
  266. if (strpos($mime, ';')) {
  267. $mime = strtok($mime, ';');
  268. }
  269. }
  270. finfo_close($finfo);
  271. }
  272. return $mime;
  273. }
  274. /**
  275. * FileMime_File
  276. *
  277. * Get the file mimetype using file command
  278. *
  279. * @access private
  280. * @return string mimetype
  281. */
  282. private function FileMime_File()
  283. {
  284. if (GitPHP_Util::IsWindows()) {
  285. return '';
  286. }
  287. if (!$this->dataRead)
  288. $this->ReadData();
  289. if (!$this->data)
  290. return '';
  291. $descspec = array(
  292. 0 => array('pipe', 'r'),
  293. 1 => array('pipe', 'w')
  294. );
  295. $proc = proc_open('file -b --mime -', $descspec, $pipes);
  296. if (is_resource($proc)) {
  297. fwrite($pipes[0], $this->data);
  298. fclose($pipes[0]);
  299. $mime = stream_get_contents($pipes[1]);
  300. fclose($pipes[1]);
  301. proc_close($proc);
  302. if ($mime && strpos($mime, '/')) {
  303. if (strpos($mime, ';')) {
  304. $mime = strtok($mime, ';');
  305. }
  306. return $mime;
  307. }
  308. }
  309. return '';
  310. }
  311. /**
  312. * FileMime_Extension
  313. *
  314. * Get the file mimetype using the file extension
  315. *
  316. * @access private
  317. * @return string mimetype
  318. */
  319. private function FileMime_Extension()
  320. {
  321. $file = $this->GetName();
  322. if (empty($file))
  323. return '';
  324. $dotpos = strrpos($file, '.');
  325. if ($dotpos !== FALSE)
  326. $file = substr($file, $dotpos+1);
  327. switch ($file) {
  328. case 'jpg':
  329. case 'jpeg':
  330. case 'jpe':
  331. return 'image/jpeg';
  332. break;
  333. case 'gif':
  334. return 'image/gif';
  335. break;
  336. case 'png';
  337. return 'image/png';
  338. break;
  339. }
  340. return '';
  341. }
  342. /**
  343. * GetHistory
  344. *
  345. * Gets the history of this file
  346. *
  347. * @access public
  348. * @return array array of filediff changes
  349. */
  350. public function GetHistory()
  351. {
  352. if (!$this->historyRead)
  353. $this->ReadHistory();
  354. return $this->history;
  355. }
  356. /**
  357. * ReadHistory
  358. *
  359. * Reads the file history
  360. *
  361. * @access private
  362. */
  363. private function ReadHistory()
  364. {
  365. $this->historyRead = true;
  366. $args = array();
  367. if ($this->commitHash)
  368. $args[] = $this->commitHash;
  369. else
  370. $args[] = 'HEAD';
  371. $args[] = '|';
  372. $args[] = GitPHP_GitExe::GetInstance()->GetBinary();
  373. $args[] = '--git-dir=' . $this->GetProject()->GetPath();
  374. $args[] = GIT_DIFF_TREE;
  375. $args[] = '-r';
  376. $args[] = '--stdin';
  377. $args[] = '--';
  378. $args[] = $this->GetPath();
  379. $historylines = explode("\n", GitPHP_GitExe::GetInstance()->Execute($this->GetProject()->GetPath(), GIT_REV_LIST, $args));
  380. $commitHash = null;
  381. foreach ($historylines as $line) {
  382. if (preg_match('/^([0-9a-fA-F]{40})/', $line, $regs)) {
  383. $commitHash = $regs[1];
  384. } else if ($commitHash) {
  385. try {
  386. $history = new GitPHP_FileDiff($this->GetProject(), $line);
  387. $history->SetCommitHash($commitHash);
  388. $this->history[] = $history;
  389. } catch (Exception $e) {
  390. }
  391. $commitHash = null;
  392. }
  393. }
  394. }
  395. /**
  396. * GetBlame
  397. *
  398. * Gets blame info
  399. *
  400. * @access public
  401. * @return array blame array (line to commit mapping)
  402. */
  403. public function GetBlame()
  404. {
  405. if (!$this->blameRead)
  406. $this->ReadBlame();
  407. return $this->blame;
  408. }
  409. /**
  410. * ReadBlame
  411. *
  412. * Read blame info
  413. *
  414. * @access private
  415. */
  416. private function ReadBlame()
  417. {
  418. $this->blameRead = true;
  419. $args = array();
  420. $args[] = '-s';
  421. $args[] = '-l';
  422. if ($this->commitHash)
  423. $args[] = $this->commitHash;
  424. else
  425. $args[] = 'HEAD';
  426. $args[] = '--';
  427. $args[] = $this->GetPath();
  428. $blamelines = explode("\n", GitPHP_GitExe::GetInstance()->Execute($this->GetProject()->GetPath(), GIT_BLAME, $args));
  429. $lastcommit = '';
  430. foreach ($blamelines as $line) {
  431. if (preg_match('/^([0-9a-fA-F]{40})(\s+.+)?\s+([0-9]+)\)/', $line, $regs)) {
  432. if ($regs[1] != $lastcommit) {
  433. $this->blame[(int)($regs[3])] = $this->GetProject()->GetCommit($regs[1]);
  434. $lastcommit = $regs[1];
  435. }
  436. }
  437. }
  438. }
  439. /**
  440. * __sleep
  441. *
  442. * Called to prepare the object for serialization
  443. *
  444. * @access public
  445. * @return array list of properties to serialize
  446. */
  447. public function __sleep()
  448. {
  449. $properties = array('data', 'dataRead');
  450. return array_merge($properties, parent::__sleep());
  451. }
  452. /**
  453. * GetCacheKey
  454. *
  455. * Gets the cache key to use for this object
  456. *
  457. * @access public
  458. * @return string cache key
  459. */
  460. public function GetCacheKey()
  461. {
  462. return GitPHP_Blob::CacheKey($this->GetProject()->GetProject(), $this->hash);
  463. }
  464. /**
  465. * CacheKey
  466. *
  467. * Generates a blob cache key
  468. *
  469. * @access public
  470. * @static
  471. * @param string $proj project
  472. * @param string $hash hash
  473. * @return string cache key
  474. */
  475. public static function CacheKey($proj, $hash)
  476. {
  477. return 'project|' . $proj . '|blob|' . $hash;
  478. }
  479. }