check_unused_lang.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <?php
  2. /**
  3. * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  4. * CC-BY License - http://creativecommons.org/licenses/by/3.0/
  5. *
  6. * @defgroup DolphinInstall Dolphin Install
  7. * @{
  8. */
  9. $aPathInfo = pathinfo(__FILE__);
  10. require_once ($aPathInfo['dirname'] . '/../inc/header.inc.php');
  11. require_once(BX_DIRECTORY_PATH_INC . 'design.inc.php');
  12. bx_import('BxDolXmlParser');
  13. /**
  14. * Command line interface for checking for unused language strings
  15. */
  16. class BxDolCheckUnusedLangsCmd
  17. {
  18. protected $_bQuiet = false;
  19. protected $_sInputFile = '';
  20. protected $_sOutputFoundFile = '';
  21. protected $_sOutputUnusedFile = '';
  22. protected $_bSortOutput = false;
  23. protected $_sModule = false;
  24. protected $_iLimit = 0;
  25. protected $_aOptionsSystem;
  26. protected $_aOptionsModule;
  27. protected $_aAlwaysTrue;
  28. public function __construct()
  29. {
  30. $this->_aAlwaysTrue = array (
  31. '/^_mma_/',
  32. '/^_ps_group_/',
  33. '/^_adm_txt_modules_/',
  34. '/^_adm_ipbl_Type/',
  35. '/^_week_/',
  36. /* wall */
  37. '/^_bx_timeline_alert_module_/',
  38. '/^_bx_timeline_alert_action_/',
  39. /* sites */
  40. '/^_bx_sites_txt_status_/',
  41. '/^_bx_sites_txt_payment_type_/',
  42. '/^_bx_sites_paypal_duration_/',
  43. /* developer */
  44. '/^_bx_dev_pgt_txt_manage_/',
  45. );
  46. $this->_aOptionsSystem = array (
  47. 'dirs' => array (
  48. '' => array ('*.php'),
  49. 'install/' => array('*.php', '*.sql'),
  50. 'studio/' => array('*.php', '*.html'),
  51. 'inc/' => array ('*.php', '*.js'),
  52. 'modules/' => array('*.php', '*.html', '*.sql'),
  53. 'template/' => array('*.php', '*.html'),
  54. ),
  55. );
  56. $this->_aOptionsModule = array (
  57. 'dirs' => array (
  58. "modules/{module_dir}/" => array ('*.php'),
  59. "modules/{module_dir}/install/sql/" => array('*.sql'),
  60. "modules/{module_dir}/classes/" => array('*.php'),
  61. "modules/{module_dir}/js/" => array ('*.js'),
  62. "modules/{module_dir}/template/" => array('*.php', '*.html'),
  63. "modules/{module_dir}/inc/" => array ('*.php'),
  64. ),
  65. );
  66. }
  67. public function main()
  68. {
  69. $a = getopt('hqsm:l:i:f:u:');
  70. if (isset($a['h']))
  71. $this->finish(0, $this->getHelp());
  72. if (isset($a['q']))
  73. $this->_bQuiet = true;
  74. if (isset($a['s']))
  75. $this->_bSortOutput = true;
  76. if (isset($a['m']))
  77. $this->_sModule = $a['m'];
  78. if (isset($a['l']))
  79. $this->_iLimit = (int)$a['l'];
  80. if (isset($a['i']))
  81. $this->_sInputFile = $a['i'];
  82. if (!$this->_sInputFile || !file_exists($this->_sInputFile))
  83. $this->finish(2, 'Input file not exist - ' . $this->_sInputFile);
  84. if (isset($a['f']))
  85. $this->_sOutputFoundFile = $a['f'];
  86. if (isset($a['u']))
  87. $this->_sOutputUnusedFile = $a['u'];
  88. $aOptions = $this->_sModule ? $this->prepareModuleConfig($this->_aOptionsModule) : $this->_aOptionsSystem;
  89. $iTimeStart = microtime ();
  90. $this->output('Processing ' . ($this->_iLimit > 0 ? "first {$this->_iLimit} strings of " : '') . $this->_sInputFile . ' ' . ($this->_sModule ? 'module(' . $this->_sModule .')' : 'system') . ' language file...');
  91. $this->output('');
  92. $oXmlParser = BxDolXmlParser::getInstance();
  93. $sXmlContent = file_get_contents($this->_sInputFile);
  94. $LANG = $oXmlParser->getValues($sXmlContent, 'string');
  95. $LANG_FOUND = array();
  96. $LANG_LOST = array();
  97. $iCount = 1;
  98. foreach ($LANG as $sKey => $sString) {
  99. if ($this->findLangKey($sKey, $aOptions))
  100. $LANG_FOUND[$sKey] = $sString;
  101. else
  102. $LANG_LOST[$sKey] = $sString;
  103. if (++$iCount > $this->_iLimit && $this->_iLimit > 0)
  104. break;
  105. }
  106. if (!$this->_bQuiet) {
  107. $this->output('Found language strings:');
  108. $this->output('-----------------------');
  109. empty($LANG_FOUND) ? $this->output('Empty') : $this->output($this->xmlExport($LANG_FOUND), false);
  110. $this->output('');
  111. }
  112. if ($this->_bSortOutput)
  113. ksort($LANG_FOUND);
  114. if ($this->_sOutputFoundFile && !empty($LANG_FOUND))
  115. file_put_contents($this->_sOutputFoundFile, $this->xmlExport($LANG_FOUND));
  116. if (!$this->_bQuiet) {
  117. $this->output('Unused language strings:');
  118. $this->output('------------------------');
  119. empty($LANG_LOST) ? $this->output('Empty') : $this->output($this->xmlExport($LANG_LOST), false);
  120. $this->output('');
  121. }
  122. if ($this->_bSortOutput)
  123. ksort($LANG_LOST);
  124. if ($this->_sOutputUnusedFile && !empty($LANG_LOST))
  125. file_put_contents($this->_sOutputUnusedFile, $this->xmlExport($LANG_LOST));
  126. $i1 = explode(' ', microtime ());
  127. $i2 = explode(' ', $iTimeStart);
  128. $iSec = round(($i1[0]+$i1[1]) - ($i2[0]+$i2[1]), 3);
  129. $this->output("Time ($iCount): " . $iSec . ' sec');
  130. if ($this->_iLimit > 0) {
  131. $iTotalSec = round(count($LANG) / $iCount * (($i1[0]+$i1[1]) - ($i2[0]+$i2[1])), 3);
  132. $this->output("Estimated Total Time (" . count($LANG) . "): " . $iTotalSec . ' sec (' . round($iTotalSec / 60.0, 3) . ' min)');
  133. }
  134. $this->finish(empty($LANG_LOST) ? 0 : 1);
  135. }
  136. protected function getHelp()
  137. {
  138. $n = 21;
  139. $s = "Usage: php check_unused_lang.php [options]\n";
  140. $s .= str_pad("\t -h", $n) . "Print this help\n";
  141. $s .= str_pad("\t -q", $n) . "Quiet\n";
  142. $s .= str_pad("\t -s", $n) . "Sort output\n";
  143. $s .= str_pad("\t -m <vendor/module>", $n) . "Process as module lang file, vendor and module folder names must be specified\n";
  144. $s .= str_pad("\t -l <number>", $n) . "Limit number of processing strings (usefull for estimating output time)\n";
  145. $s .= str_pad("\t -i <input_file>", $n) . "Input XML language file\n";
  146. $s .= str_pad("\t -f <output_file>", $n) . "Output XML file for found strings\n";
  147. $s .= str_pad("\t -u <output_file>", $n) . "Output XML file for unused strings\n";
  148. $s .= "\n";
  149. $s .= "Return codes:\n";
  150. $s .= str_pad("\t 0", 5) . "Success - no unused language strings\n";
  151. $s .= str_pad("\t 1", 5) . "Unused language strings were found\n";
  152. $s .= str_pad("\t 2", 5) . "Error occured\n";
  153. return $s;
  154. }
  155. protected function findLangKey($sKey, $aOptions)
  156. {
  157. foreach ($this->_aAlwaysTrue as $sReg)
  158. if (preg_match($sReg, $sKey))
  159. return true;
  160. foreach ($aOptions['dirs'] as $sDir => $aExt) {
  161. $sDepth = $sDir ? '' : ' -depth 1 ';
  162. if ($this->_sModule) {
  163. $aDir = explode('/', $sDir);
  164. $sDepth = !$sDir || $this->_sModule == ($aDir[(count($aDir) - 3)] . '/' . $aDir[(count($aDir) - 2)]) ? ' -depth 1 ' : '';
  165. }
  166. $sDir = BX_DIRECTORY_PATH_ROOT . $sDir;
  167. if (!file_exists($sDir))
  168. continue;
  169. foreach ($aExt as $sExt) {
  170. $sResults = `find $sDir $sDepth -name "$sExt" -exec grep "\<$sKey\>" {} \;`;
  171. if ($sResults)
  172. return true;
  173. }
  174. }
  175. return false;
  176. }
  177. protected function xmlExport($a, $sRoot = 'resources', $bCData = true)
  178. {
  179. $s = "<" . $sRoot . ">\n";
  180. foreach ($a as $sKey => $sString) {
  181. if ($bCData)
  182. $sString = '<![CDATA[' . $sString . ']]>';
  183. $s .= "\t".'<string name="' . $sKey . '">' . $sString . "</string>\n";
  184. }
  185. $s .= "</" . $sRoot . ">\n";
  186. return $s;
  187. }
  188. protected function prepareModuleConfig ($a) {
  189. if (!$this->_sModule)
  190. return $a;
  191. $aRet = array();
  192. $aRet['dirs'] = array();
  193. foreach ($a['dirs'] as $sDir => $aPatterns) {
  194. $sDir = str_replace('{module_dir}', $this->_sModule, $sDir);
  195. $aRet['dirs'][$sDir] = $aPatterns;
  196. }
  197. return $aRet;
  198. }
  199. protected function finish ($iCode, $sMsg = null, $bAddLn = true)
  200. {
  201. if (!$this->_bQuiet && null !== $sMsg)
  202. echo $sMsg . ($bAddLn ? "\n" : '');
  203. exit($iCode);
  204. }
  205. protected function output ($sMsg, $bAddLn = true)
  206. {
  207. if (!$this->_bQuiet)
  208. echo $sMsg . ($bAddLn ? "\n" : '');
  209. }
  210. }
  211. $o = new BxDolCheckUnusedLangsCmd();
  212. $o->main();
  213. /** @} */