1. <?php
  2. if(!defined('IS_HEARTPHP')) exit('Access Denied');
  3. /**
  4. * template.class.php 模板解析类
  5. *
  6. * @copyright (C) 20013-2015 HeartPHP
  7. * @author zhangxiaoliang <[email]zl8762385@163.com[/email]> <qq:979314>
  8. * @lastmodify 2013.04.19
  9. *
  10. * 您可以自由使用该源码,但是在使用过程中,请保留作者信息。尊重他人劳动成果就是尊重自己
  11. */
  12.  
  13. class template {
  14. private $vars = array();
  15. private $conf = '';
  16. private $tpl_name = 'index';//如果模板不存在 会查找当前 controller默认index模板
  17. private $tpl_suffix = '.html';//如果CONFIG没配置默认后缀 则显示
  18. private $tpl_compile_suffix= '.tpl.php';//编译模板路径
  19. private $template_tag_left = '<{';//模板左标签
  20. private $template_tag_right = '}>';//模板右标签
  21. private $template_c = '';//编译目录
  22. private $template_path = '';//模板完整路径 D:/PHPnow/htdocs/heartphp/tpl/ad/index/
  23. private $template_name = '';//模板名称 index.html
  24.  
  25. //定义每个模板的标签的元素
  26. private $tag_foreach = array('from', 'item', 'key');
  27. private $tag_include = array('file');//目前只支持读取模板默认路径
  28.  
  29. public function __construct($conf) {
  30. $this->conf = &$conf;
  31.  
  32. $this->template_c = $this->conf['template_config']['template_c'];//编译目录
  33. $this->_tpl_suffix = $this->tpl_suffix();
  34. }
  35.  
  36. /**
  37. * 重构str_replace
  38. * @param $search 搜索字符串
  39. * @param $replace 替换字符串
  40. * @param $content 内容
  41. * [url=home.php?mod=space&uid=987628]@Return[/url] html
  42. */
  43. private function str_replace($search, $replace, $content) {
  44. if(empty($search) || empty($replace) || empty($content)) return false;
  45. return str_replace($search, $replace, $content);
  46. }
  47.  
  48. /**
  49. * preg_match_all
  50. * @param $pattern 正则
  51. * @param $content 内容
  52. * @return array
  53. */
  54.  
  55. private function preg_match_all($pattern, $content) {
  56. if(empty($pattern) || empty($content)) core::show_error('查找模板标签失败!');
  57. preg_match_all("/".$this->template_tag_left.$pattern.$this->template_tag_right."/is", $content, $match);
  58. return $match;
  59. }
  60. /**
  61. * 模板文件后缀
  62. */
  63. public function tpl_suffix() {
  64. $tpl_suffix = empty($this->conf['template_config']['template_suffix']) ?
  65. $this->tpl_suffix :
  66. $this->conf['template_config']['template_suffix'] ;
  67. return $tpl_suffix;
  68. }
  69.  
  70. /**
  71. * 此处不解释了
  72. * @return
  73. */
  74. public function assign($key, $value) {
  75. $this->vars[$key] = $value;
  76. }
  77.  
  78. /**
  79. * 渲染页面
  80. * @param
  81. * 使用方法 1
  82. * $this->view->display('error', 'comm/');
  83. * 默认是指向TPL模版的跟目录,所以comm/就是 tpl/comm/error.html
  84. * 使用方法 2
  85. * $this->view->display('errorfile');
  86. * 默认指向控制器固定的文件夹
  87. * 例如你的域名是 http://heartphp/admin/index, 那么正确路径就是tpl/admin/index/errorfile.html
  88. * @return
  89. */
  90. public function display($filename = '', $view_path = '') {
  91. $tpl_path_arr = $this->get_tpl($filename, $view_path);//获取TPL完整路径 并且向指针传送路径以及名称
  92. if(!$tpl_path_arr) core::show_error($filename.$this->_tpl_suffix.'模板不存在');
  93.  
  94. //编译开始
  95. $this->view_path_param = $view_path;//用户传递过来的模版跟目录
  96. $this->compile();
  97.  
  98. }
  99.  
  100. /**
  101. * 编译控制器
  102. * @param
  103. * @return
  104. */
  105. private function compile() {
  106. $filepath = $this->template_path.$this->template_name;
  107. $compile_dirpath = $this->check_temp_compile();
  108. $vars_template_c_name = str_replace($this->_tpl_suffix, '', $this->template_name);
  109.  
  110. $include_file = $this->template_replace($this->read_file($filepath), $compile_dirpath, $vars_template_c_name);//解析
  111. if($include_file) {
  112. $this->read_config() && $config = $this->read_config();
  113. extract($this->vars, EXTR_SKIP);
  114. [url=home.php?mod=space&uid=48608]@include[/url] $include_file;
  115. }
  116. }
  117.  
  118. /**
  119. * 读取当前项目配置文件
  120. */
  121. protected function read_config() {
  122. if(file_exists(SYSTEM_PATH.'conf/config.php')) {
  123. @include SYSTEM_PATH.'conf/config.php';
  124. return $config;
  125. }
  126.  
  127. return false;
  128. }
  129. /**
  130. * 解析模板语法
  131. * @param $str 内容
  132. * @param $compile_dirpath 模版编译目录
  133. * @param $vars_template_c_name 模版编译文件名
  134. * @return 编译过的PHP模板文件名
  135. */
  136. private function template_replace($str, $compile_dirpath, $vars_template_c_name) {
  137. if(empty($str)) core::show_error('模板内容为空!');
  138.  
  139. //处理编译头部
  140. $compile_path = $compile_dirpath.$vars_template_c_name.$this->tpl_compile_suffix;//编译文件
  141.  
  142. if(is_file($compile_path)) {
  143. //$header_content = $this->get_compile_header($compile_path);
  144. //$compile_date = $this->get_compile_header_comment($header_content);
  145.  
  146. $tpl_filemtime = filemtime($this->template_path.$this->template_name);
  147. $compile_filemtime = filemtime($compile_path);
  148.  
  149. //echo $tpl_filemtime.'=='.date('Y-m-d H:i:s', $tpl_filemtime).'<br/>';
  150. //echo $compile_filemtime.'=='.date('Y-m-d H:i:s', $compile_filemtime);
  151. //如果文件过期编译 当模板标签有include并且有修改时 也重新编译
  152. //<{include file="public/left.html"}> 当修改include里的文件,非DEBUG模式时 如果不更改主文件 目前是不重新编译include里的文件,我在考虑是否也要更改,没想好,暂时这样,所以在开发阶段一定要开启DEBUG=1模式 要不然修改include文件无效 。 有点罗嗦,不知道表述清楚没
  153. if($tpl_filemtime > $compile_filemtime || DEBUG) {
  154. $ret_file = $this->compile_file($vars_template_c_name, $str, $compile_dirpath);
  155. } else {
  156. $ret_file = $compile_path;
  157. }
  158. } else {//编译文件不存在 创建他
  159. $ret_file = $this->compile_file($vars_template_c_name, $str, $compile_dirpath);
  160. }
  161.  
  162. return $ret_file;
  163. }
  164.  
  165. /**
  166. * 模板文件主体
  167. * @param string $str 内容
  168. * @return html
  169. */
  170. private function body_content($str) {
  171. //解析
  172. $str = $this->parse($str);
  173.  
  174. $header_comment = "Create On##".time()."|Compiled from##".$this->template_path.$this->template_name;
  175. $content = "<? if(!defined('IS_HEARTPHP')) exit('Access Denied');/*{$header_comment}*/?>\r\n$str";
  176.  
  177. return $content;
  178. }
  179.  
  180. /**
  181. * 开始解析相关模板标签
  182. * @param $content 模板内容
  183. */
  184. private function parse($content) {
  185. //foreach
  186. $content = $this->parse_foreach($content);
  187.  
  188. //include
  189. $content = $this->parse_include($content);
  190.  
  191. //if
  192. $content = $this->parse_if($content);
  193.  
  194. //elseif
  195. $content = $this->parse_elseif($content);
  196.  
  197. //模板标签公用部分
  198. $content = $this->parse_comm($content);
  199.  
  200. //转为PHP代码
  201. $content = $this->parse_php($content);
  202. return $content;
  203. }
  204.  
  205. /**
  206. * echo 如果默认直接<{$config['domain']}> 转成 <?php echo $config['domain']?>
  207. */
  208. private function parse_echo($content) {
  209.  
  210. }
  211.  
  212. /**
  213. * 转换为PHP
  214. * @param $content html 模板内容
  215. * @return html 替换好的HTML
  216. */
  217. private function parse_php($content){
  218. if(empty($content)) return false;
  219. $content = preg_replace("/".$this->template_tag_left."(.+?)".$this->template_tag_right."/is", "<?php $1 ?>", $content);
  220.  
  221. return $content;
  222. }
  223. /**
  224. * if判断语句
  225. * <{if empty($zhang)}>
  226. * zhang
  227. * <{elseif empty($liang)}>
  228. * liang
  229. * <{else}>
  230. * zhangliang
  231. * <{/if}>
  232. */
  233. private function parse_if($content) {
  234. if(empty($content)) return false;
  235. //preg_match_all("/".$this->template_tag_left."if\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  236.  
  237. $match = $this->preg_match_all("if\s+(.*?)", $content);
  238. if(!isset($match[1]) || !is_array($match[1])) return $content;
  239.  
  240. foreach($match[1] as $k => $v) {
  241. //$s = preg_split("/\s+/is", $v);
  242. //$s = array_filter($s);
  243. $content = str_replace($match[0][$k], "<?php if({$v}) { ?>", $content);
  244. }
  245.  
  246. return $content;
  247. }
  248.  
  249. private function parse_elseif($content) {
  250. if(empty($content)) return false;
  251. //preg_match_all("/".$this->template_tag_left."elseif\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  252. $match = $this->preg_match_all("elseif\s+(.*?)", $content);
  253. if(!isset($match[1]) || !is_array($match[1])) return $content;
  254.  
  255. foreach($match[1] as $k => $v) {
  256. //$s = preg_split("/\s+/is", $v);
  257. //$s = array_filter($s);
  258. $content = str_replace($match[0][$k], "<?php } elseif ({$v}) { ?>", $content);
  259. }
  260.  
  261. return $content;
  262. }
  263. /**
  264. * 解析 include include标签不是实时更新的 当主体文件更新的时候 才更新标签内容,所以想include生效 请修改一下主体文件
  265. * 记录一下 有时间开发一个当DEBUG模式的时候 每次执行删除模版编译文件
  266. * 使用方法 <{include file=""}>
  267. * @param $content 模板内容
  268. * @return html
  269. */
  270. private function parse_include($content) {
  271. if(empty($content)) return false;
  272.  
  273. //preg_match_all("/".$this->template_tag_left."include\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  274. $match = $this->preg_match_all("include\s+(.*?)", $content);
  275. if(!isset($match[1]) || !is_array($match[1])) return $content;
  276.  
  277. foreach($match[1] as $match_key => $match_value) {
  278. $a = preg_split("/\s+/is", $match_value);
  279.  
  280. $new_tag = array();
  281. //分析元素
  282. foreach($a as $t) {
  283. $b = explode('=', $t);
  284. if(in_array($b[0], $this->tag_include)) {
  285. if(!empty($b[1])) {
  286. $new_tag[$b[0]] = str_replace("\"", "", $b[1]);
  287. } else {
  288. core::show_error('模板路径不存在!');
  289. }
  290. }
  291. }
  292.  
  293. extract($new_tag);
  294. //查询模板文件
  295. foreach($this->conf['view_path'] as $v){
  296. $conf_view_tpl = $v.$file;//include 模板文件
  297. if(is_file($conf_view_tpl)) {
  298. $c = $this->read_file($conf_view_tpl);
  299.  
  300. $inc_file = str_replace($this->_tpl_suffix, '', basename($file));
  301.  
  302. $this->view_path_param = dirname($file).'/';
  303. $compile_dirpath = $this->check_temp_compile();
  304.  
  305. $include_file = $this->template_replace($c, $compile_dirpath, $inc_file);//解析
  306.  
  307. break;
  308. } else {
  309. core::show_error('模板文件不存在,请仔细检查 文件:'. $conf_view_tpl);
  310. }
  311. }
  312.  
  313. $content = str_replace($match[0][$match_key], '<?php include("'.$include_file.'")?>', $content);
  314. }
  315.  
  316. return $content;
  317.  
  318. }
  319. /**
  320. * 解析 foreach
  321. * 使用方法 <{foreach from=$lists item=value key=kk}>
  322. * @param $content 模板内容
  323. * @return html 解析后的内容
  324. */
  325. private function parse_foreach($content) {
  326. if(empty($content)) return false;
  327.  
  328. //preg_match_all("/".$this->template_tag_left."foreach\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  329. $match = $this->preg_match_all("foreach\s+(.*?)", $content);
  330. if(!isset($match[1]) || !is_array($match[1])) return $content;
  331.  
  332. foreach($match[1] as $match_key => $value) {
  333.  
  334. $split = preg_split("/\s+/is", $value);
  335. $split = array_filter($split);
  336.  
  337. $new_tag = array();
  338. foreach($split as $v) {
  339. $a = explode("=", $v);
  340. if(in_array($a[0], $this->tag_foreach)) {//此处过滤标签 不存在过滤
  341. $new_tag[$a[0]] = $a[1];
  342. }
  343. }
  344. $key = '';
  345.  
  346. extract($new_tag);
  347. $key = ($key) ? '$'.$key.' =>' : '' ;
  348. $s = '<?php foreach('.$from.' as '.$key.' $'.$item.') { ?>';
  349. $content = $this->str_replace($match[0][$match_key], $s, $content);
  350.  
  351. }
  352.  
  353. return $content;
  354. }
  355.  
  356. /**
  357. * 匹配结束 字符串
  358. */
  359. private function parse_comm($content) {
  360. $search = array(
  361. "/".$this->template_tag_left."\/foreach".$this->template_tag_right."/is",
  362. "/".$this->template_tag_left."\/if".$this->template_tag_right."/is",
  363. "/".$this->template_tag_left."else".$this->template_tag_right."/is",
  364.  
  365. );
  366.  
  367. $replace = array(
  368. "<?php } ?>",
  369. "<?php } ?>",
  370. "<?php } else { ?>"
  371. );
  372. $content = preg_replace($search, $replace, $content);
  373. return $content;
  374. }
  375. /**
  376. * 检查编译目录 如果没有创建 则递归创建目录
  377. * @param string $path 文件完整路径
  378. * @return 模板内容
  379. */
  380. private function check_temp_compile() {
  381. //$paht = $this->template_c.
  382.  
  383. $tpl_path = ($this->view_path_param) ? $this->view_path_param : $this->get_tpl_path() ;
  384. $all_tpl_apth = $this->template_c.$tpl_path;
  385.  
  386. if(!is_dir($all_tpl_apth)) {
  387. $this->create_dir($tpl_path);
  388. }
  389.  
  390. return $all_tpl_apth;
  391. }
  392. /**
  393. * 读文件
  394. * @param string $path 文件完整路径
  395. * @return 模板内容
  396. */
  397. private function read_file($path) {
  398. //$this->check_file_limits($path, 'r');
  399.  
  400. if(($r = @fopen($path, 'r')) === false) {
  401. core::show_error('模版文件没有读取或执行权限,请检查!');
  402. }
  403. $content = fread($r, filesize($path));
  404. fclose($r);
  405. return $content;
  406. }
  407.  
  408. /**
  409. * 写文件
  410. * @param string $filename 文件名
  411. * @param string $content 模板内容
  412. * @return 文件名
  413. */
  414. private function compile_file($filename, $content, $dir) {
  415. if(empty($filename)) core::show_error("{$filename} Creation failed");
  416.  
  417. $content = $this->body_content($content);//对文件内容操作
  418. //echo '开始编译了=====';
  419. $f = $dir.$filename.$this->tpl_compile_suffix;
  420.  
  421. //$this->check_file_limits($f, 'w');
  422. if(($fp = @fopen($f, 'wb')) === false) {
  423. core::show_error($f.'<br/>编译文件失败,请检查文件权限.');
  424. }
  425. //开启flock
  426. flock($fp, LOCK_EX + LOCK_NB);
  427. fwrite($fp, $content, strlen($content));
  428. flock($fp, LOCK_UN + LOCK_NB);
  429. fclose($fp);
  430.  
  431. return $f;
  432. }
  433.  
  434. /**
  435. * 这个检查文件权限函数 暂时废弃了
  436. * @param [$path] [路径]
  437. * @param [status] [w=write, r=read]
  438. */
  439. public function check_file_limits($path , $status = 'rw') {
  440. clearstatcache();
  441. if(!is_writable($path) && $status == 'w') {
  442. core::show_error("{$path}<br/>没有写入权限,请检查.");
  443. } elseif(!is_readable($path) && $status == 'r') {
  444. core::show_error("{$path}<br/>没有读取权限,请检查.");
  445. } elseif($status == 'rw') {//check wirte and read
  446. if(!is_writable($path) || !is_readable($path)) {
  447. core::show_error("{$path}<br/>没有写入或读取权限,请检查");
  448. }
  449. }
  450.  
  451. }
  452.  
  453. /**
  454. * 读取编译后模板的第一行 并分析成数组
  455. * @param string $filepath 文件路径
  456. * @param number $line 行数
  457. * @return 返回指定行数的字符串
  458. */
  459. /*
  460. private function get_compile_header($filepath, $line = 0) {
  461.  
  462. if(($file_arr = @file($filepath)) === false) {
  463. core::show_error($filepath.'<br/>读取文件失败,请检查文件权限!');
  464. }
  465. return $file_arr[0];
  466. }
  467. */
  468.  
  469. /**
  470. * 分析头部注释的日期
  471. * @param string $cotnent 编译文件头部第一行
  472. * @return 返回上一次日期
  473. */
  474. /*
  475. private function get_compile_header_comment($content) {
  476. preg_match("/\/\*(.*?)\*\//", $content, $match);
  477. if(!isset($match[1]) || empty($match[1])) core::show_error('编译错误!');
  478. $arr = explode('|', $match[1]);
  479. $arr_date = explode('##', $arr[0]);
  480.  
  481. return $arr_date[1];
  482. }
  483. */
  484. /**
  485. * 获取模板完整路径 并返回已存在文件
  486. * @param string $filename 文件名
  487. * @param string $view_path 模板路径
  488. * @return
  489. */
  490. private function get_tpl($filename, $view_path) {
  491. empty($filename) && $filename = $this->tpl_name;
  492.  
  493. //遍历模板路径
  494. foreach($this->conf['view_path'] as $path) {
  495. if($view_path) {//直接从tpl跟目录找文件
  496. $tpl_path = $path.$view_path;
  497. $view_file_path = $tpl_path.$filename.$this->_tpl_suffix;
  498. } else {//根据目录,控制器,方法开始找文件
  499. $view_file_path = ($tpl_path = $this->get_tpl_path($path)) ? $tpl_path.$filename.$this->_tpl_suffix : exit(0);
  500. }
  501.  
  502. if(is_file($view_file_path)) {
  503. //向指针传送模板路径和模板名称
  504. $this->template_path = $tpl_path;//
  505. $this->template_name = $filename.$this->_tpl_suffix;
  506. return true;
  507. } else {
  508. core::show_error($filename.$this->_tpl_suffix.'模板不存在');
  509. }
  510. }
  511. }
  512.  
  513. /**
  514. * 获取模板路径
  515. * @param string $path 主目录
  516. * @return URL D和M的拼接路径
  517. */
  518. private function get_tpl_path($path = '') {
  519. core::get_directory_name() && $path_arr[0] = core::get_directory_name();
  520. core::get_controller_name() && $path_arr[1] = core::get_controller_name();
  521. (is_array($path_arr)) ? $newpath = implode('/', $path_arr) : core::show_error('获取模板路径失败!') ;
  522.  
  523. return $path.$newpath.'/';
  524. }
  525.  
  526. /**
  527. * 创建目录
  528. * @param string $path 目录
  529. * @return
  530. */
  531. private function create_dir($path, $mode = 0777){
  532. if(is_dir($path)) return false;
  533.  
  534. $dir_arr = explode('/', $path);
  535. $dir_arr = array_filter($dir_arr);
  536.  
  537. $allpath = '';
  538. $newdir = $this->template_c;
  539.  
  540. foreach($dir_arr as $dir) {
  541. $allpath = $newdir.'/'.$dir;
  542.  
  543. if(!is_dir($allpath)) {
  544. $newdir = $allpath;
  545.  
  546. if(!@mkdir($allpath, $mode)) {
  547. core::show_error( $allpath.'<br/>创建目录失败,请检查是否有可都写权限!');
  548. }
  549. chmod($allpath, $mode);
  550. } else {
  551. $newdir = $allpath;
  552. }
  553. }
  554. return true;
  555. }
  556.  
  557. public function __destruct(){
  558. $this->vars = null;
  559. $this->view_path_param = null;
  560. }
  561.  
  562. };

自己用的框架写了一个PHP模版解析类的更多相关文章

  1. 自己用反射写的一个request.getParameter工具类

    适用范围:当我们在jsp页面需要接收很多值的时候,如果用request.getParameter(属性名)一个一个写的话那就太麻烦了,于是我想是 否能用反射写个工具类来简化这样的代码,经过1个小时的代 ...

  2. [node.js学习]为node.js写的一个操作mysql的类

    不支持一个对象在不同异步中使用,模仿的php框架 speedphp中的model模块 GaryMysql.js var mysql = require('mysql'); var pool = nul ...

  3. 自己写的一个简单的Tab类

    //------------- PS_DOM 功能函数 start----------------var PS_DOM ={ indexOf: function(arr, e){ for(var i= ...

  4. [Android面试题-7] 写出一个Java的Singleton类(即单例类)

    1.首先明确单例的概念和特点: a>单例类只能有一个实例 b>单例类必须自己创建一个自己的唯一实例 c>单例类必须为其他所有对象提供这个实例 2.单例具有几种模式,最简单的两种分别是 ...

  5. 自己写的一个分页控件类(WinForm)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; u ...

  6. 用PHP写的一个简单的分页类 1.0版

    <?php /* 分页类 用于实现对多条数据分页显示 version:1.0 author:Knight E-Mail:S.Knight.Work@gmail.com Date:2013-10- ...

  7. 用PHP写的一个简单的分页类 2.0版

    <?php /* 分页类 用于实现对多条数据分页显示 version:2.0 //基于1.0 数据库查询用mysqli实现 author:Knight E-Mail:S.Knight.Work@ ...

  8. spring框架中的一个字符串的工具类

    stringutils.hasText("字符串") 如果字符串里面的值为null, "", "   ",那么返回值为false:否则为tr ...

  9. 【Java/csv】一个CSV文件解析类(转载)

    /*下文写得不错,值得学习**/ import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayLis ...

随机推荐

  1. win下Maven安装和基本设置

    注:本文介绍 Windows 平台上 Maven 的安装.Maven 3 需要运行在 JDK1.4 以上的版本上. 非原创:原创地址 http://www.ibm.com/developerworks ...

  2. 【锋利的JQuery-学习笔记】切换网页皮肤-且保存于Cookie

    切换网页皮肤: html片段: <head> <link rel="stylesheet" href="styles/skin/skin_0.css&q ...

  3. 140227项目开发及上线过程遇到的10个问题(重点: FCK过滤替换)

    1.替换条件判断问题 String s = (String)map2.get("contentIntro"); if(s != null && s.length() ...

  4. C# 知识笔记

    HttpContext.Request.Form.ToString() 获取Form表单中的内容 /// <summary> /// 获取 GET 提交方式值 /// </summa ...

  5. [转]SQL Server建立应用程序安全性和程序角色

    转自:http://dev.yesky.com/450/7619450.shtml 2007-10-22 14:00 来源:论坛整理 作者:luolina 责任编辑:幽灵·yesky Microsof ...

  6. hdu 3441 Rotation

    总的来说,这题要2次用到polya定理. 由题目条件A*A=B*B+1,变形为(A-1)*(A+1)=K*B*B; 分别分解A-1和A+1的质因数,在合并在一起. 第一步:搜索B,对B*B的正方形涂色 ...

  7. 【Linux高频命令专题(9)】ls

    ls命令是linux下最常用的命令.ls命令就是list的缩写缺省下ls用来打印出当前目录的清单如果ls指定其他目录那么就会显示指定目录里的文件及文件夹清单. 通过ls 命令不仅可以查看linu ...

  8. MIT算法导论——第四讲.Quicksort

    本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记.所有内容均来自MIT公开课Introduction to Algorithms中Charles E. ...

  9. Visual StudioTools for Unity 使用技巧2

    在之前的博客介绍了 Visual Studio Tools for Unity的安装和使用. http://www.cnblogs.com/petto/p/3886811.html 其实这个工具还提供 ...

  10. JavaWeb项目开发案例精粹-第2章投票系统-003Dao层

    1. package com.sanqing.dao; import java.util.List; import com.sanqing.bean.Vote; import com.sanqing. ...