php生成网站截图

组件下载: http://wkhtmltopdf.org/
Ubuntu中文支持: apt-get install openssl build-essential xorg libssl-dev libxrender-dev fonts-arphic-bsmi00lp fonts-arphic-gbsn00lp xfonts-intl-chinese

测试命令:./wkhtmltoimage baidu.com baidu.png

php中使用:

  1. // api.php
  2. <?php
  3. $url = isset($_GET['url']) ? $_GET['url'] : die('url param is require');
  4. $cache = __DIR__ .'/cache/' . md5($url) . '.png';
  5. if (!is_file($cache)) {
  6. // 这里写组件的存放位置, 主要要给文件可执行权限 chmod 777 wkhtmltoimage
  7. $shell = __DIR__ . '/shell/wkhtmltox/bin/wkhtmltoimage '. $url .' '. $cache;
  8. shell_exec($shell);
  9. }
  10. header("Content-Type: image/png");
  11. $im = imagecreatefrompng($cache);
  12. imagesavealpha($im, true);
  13. imagepng($im);
  14. imagedestroy($im);

然后访问 http://demo.zhaishuaigan.cn/php/html2img/api.php?url=baidu.com

解决手机网页click事件的300毫秒延时问题

这里我不讨论为什么手机端有300毫秒延时, 只说解决方法, 需要知道为什么的小伙伴可以自己百度.

直接上解决方法

  1. 下载引入fastclick.js : https://github.com/ftlabs/fastclick
  2. 在网页加在后执行 FastClick.attach(document.body);
  3. 没有了, 尽情享受手机端快速相应的体验吧

php内置服务器使用方法

1 启动Web服务器

  1. $ cd ~/public_html
  2. $ php -S localhost:8000

终端输出信息:

  1. PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
  2. Listening on localhost:8000
  3. Document root is /home/me/public_html
  4. Press Ctrl-C to quit

当请求了 http://localhost:8000/http://localhost:8000/myscript.html 地址后,终端输出类似如下的信息:

  1. PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
  2. Listening on localhost:8000
  3. Document root is /home/me/public_html
  4. Press Ctrl-C to quit.
  5. [Thu Jul 21 10:48:48 2011] ::1:39144 GET /favicon.ico - Request read
  6. [Thu Jul 21 10:48:50 2011] ::1:39146 GET / - Request read
  7. [Thu Jul 21 10:48:50 2011] ::1:39147 GET /favicon.ico - Request read
  8. [Thu Jul 21 10:48:52 2011] ::1:39148 GET /myscript.html - Request read
  9. [Thu Jul 21 10:48:52 2011] ::1:39149 GET /favicon.ico - Request read

2 启动web服务器时指定文档的根目录

  1. $ cd ~/public_html
  2. $ php -S localhost:8000 -t foo/

终端显示信息:

  1. PHP 5.4.0 Development Server started at Thu Jul 21 10:50:26 2011
  2. Listening on localhost:8000
  3. Document root is /home/me/public_html/foo
  4. Press Ctrl-C to quit

如果你在启动命令行后面附加一个php脚本文件,那这个文件将会被当成一个“路由器”脚本。这个脚本将负责所有的HTTP请求,如果这个脚本执行时返回FALSE,则被请求的资源会正常的返回。如果不是FALSE,浏览里显示的将会是这个脚本产生的内容。

3 使用路由器脚本

在这个例子中,对图片的请求会返回相应的图片,但对HTML文件的请求会显示“Welcome to PHP”:

  1. <?php
  2. // router.php
  3. if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"])) {
  4. return false; // serve the requested resource as-is.
  5. } else {
  6. echo "<p>Welcome to PHP</p>";
  7. }
  8. ?>

执行:

  1. $ php -S localhost:8000 router.php

4 判断是否是在使用内置web服务器

通过程序判断来调整同一个PHP路由器脚本在内置Web服务器中和在生产服务器中的不同行为:

  1. <?php
  2. // router.php
  3. if (php_sapi_name() == 'cli-server') {
  4. /* route static assets and return false */
  5. }
  6. /* go on with normal index.php operations */
  7. ?>

执行:

  1. $ php -S localhost:8000 router.php

这个内置的web服务器能识别一些标准的MIME类型资源,它们的扩展有:.css, .gif, .htm, .html, .jpe, .jpeg, .jpg, .js, .png, .svg, and .txt。对.htm 和 .svg 扩展到支持是在PHP 5.4.4之后才支持的。

5 处理不支持的文件类型

如果你希望这个Web服务器能够正确的处理不被支持的MIME文件类型,这样做:

  1. <?php
  2. // router.php
  3. $path = pathinfo($_SERVER["SCRIPT_FILENAME"]);
  4. if ($path["extension"] == "ogg") {
  5. header("Content-Type: video/ogg");
  6. readfile($_SERVER["SCRIPT_FILENAME"]);
  7. }
  8. else {
  9. return FALSE;
  10. }
  11. ?>

执行:

  1. $ php -S localhost:8000 router.php

如果你希望能远程的访问这个内置的web服务器,你的启动命令需要改成下面这样:

6 远程访问这个内置Web服务器

  1. $ php -S 0.0.0.0:8000

这样你就可以通过 8000 端口远程的访问这个内置的web服务器了。

php-angular模板引擎

源码地址: https://github.com/php-angular/php-angular

仿angularjs的php模板引擎

目前实现了一下几种标签和用法

标签

  1. php-if
  2. php-for
  3. php-foreach
  4. php-repeat
  5. php-show
  6. php-hide
  7. php-include
  8. php-init
  9. php-exec

变量输出
{$var}
{$array.name}
{$array[‘name’]}
{:func()}
{$var ? ‘’ : ‘’}

结合框架使用

具体的框架驱动可以在 /drivers 目录中找到

直接使用方法 /test/index.php

  1. <?php
  2. require '../lib/angular.php';
  3. // 配置
  4. $config = array(
  5. 'tpl_path' => './view/',
  6. 'tpl_suffix' => '.html',
  7. 'cache_path' => './cache/',
  8. 'attr' => 'php-',
  9. 'debug' => true,
  10. );
  11. // 实例化
  12. $view = new Angular($config);
  13. // 数据
  14. $data = array(
  15. 'title' => 'Hello PHP Angular',
  16. 'list' => array(
  17. array('name' => 'name_1', 'email' => 'email_1@qq.com'),
  18. array('name' => 'name_2', 'email' => 'email_2@qq.com'),
  19. array('name' => 'name_3', 'email' => 'email_3@qq.com'),
  20. array('name' => 'name_4', 'email' => 'email_4@qq.com'),
  21. array('name' => 'name_5', 'email' => 'email_5@qq.com'),
  22. ),
  23. );
  24. // 向模板引擎设置数据
  25. $view->assign($data);
  26. // 输出解析结果
  27. $view->display('index');
  28. // 获取输出结果
  29. // $view->fetch('index');

模板实例 /test/view/index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>php-angular</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <style type="text/css">
  8. .box {
  9. padding: 10px;
  10. font-size: 12px;
  11. margin: 10px 5px;
  12. background: #CCC;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <div class="box" php-show="$title">{$title}</div>
  18. <div class="box" php-hide="$title">如果title的值为空, 则可以显示这条消息, 否则不显示</div>
  19. <div class="box">
  20. <span>foreach by [1,2,3,4,5]</span>
  21. <ul>
  22. <li php-foreach="[1,2,3,4,5] as $i">foreach {$i}</li>
  23. </ul>
  24. </div>
  25. <div class="box">
  26. <span>repeat by [1,2,3,4,5]</span>
  27. <ul>
  28. <li php-repeat="[1,2,3,4,5] as $i">foreach {$i}</li>
  29. </ul>
  30. </div>
  31. <div class="box" php-show="$list">
  32. <span>foreach by $list as $item</span>
  33. <ul>
  34. <li php-foreach="$list as $item">name:{$item.name} -- email: {$item.email}</li>
  35. </ul>
  36. </div>
  37. <div class="box" php-show="$list">
  38. <span>repeat by $list as $item</span>
  39. <ul>
  40. <li php-repeat="$list as $item">name:{$item.name} -- email: {$item.email}</li>
  41. </ul>
  42. </div>
  43. <div class="box" php-if="$list">
  44. <span>foreach by $list as $key => $item</span>
  45. <ul>
  46. <li php-foreach="$list as $key => $item">{$key} -- name:{$item.name} -- email: {$item.email}</li>
  47. </ul>
  48. </div>
  49. <div class="box">
  50. <span>for by ($i = 1; $i <= 10; $i++;)</span>
  51. <ul>
  52. <li php-for="$i = 1; $i <= 10; $i++">for {$i}</li>
  53. </ul>
  54. </div>
  55. <div class="box" php-if="$list">
  56. <span>$list 不为空</span>
  57. </div>
  58. </body>
  59. </html>

解析结果 /test/cache/6a992d5529f459a44fee58c733255e86.php

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>php-angular</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <style type="text/css">
  8. .box {
  9. padding: 10px;
  10. font-size: 12px;
  11. margin: 10px 5px;
  12. background: #CCC;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <?php if ($title) { ?><div class="box" ><?php echo $title; ?></div><?php } ?>
  18. <?php if (!($title)) { ?><div class="box" >如果title的值为空, 则可以显示这条消息, 否则不显示</div><?php } ?>
  19. <div class="box">
  20. <span>foreach by [1,2,3,4,5]</span>
  21. <ul>
  22. <?php foreach ([1,2,3,4,5] as $i) { ?><li >foreach <?php echo $i; ?></li><?php } ?>
  23. </ul>
  24. </div>
  25. <div class="box">
  26. <span>repeat by [1,2,3,4,5]</span>
  27. <ul>
  28. <?php foreach ([1,2,3,4,5] as $i) { ?><li >foreach <?php echo $i; ?></li><?php } ?>
  29. </ul>
  30. </div>
  31. <?php if ($list) { ?><div class="box" >
  32. <span>foreach by $list as $item</span>
  33. <ul>
  34. <?php foreach ($list as $item) { ?><li >name:<?php echo $item["name"]; ?> -- email: <?php echo $item["email"]; ?></li><?php } ?>
  35. </ul>
  36. </div><?php } ?>
  37. <?php if ($list) { ?><div class="box" >
  38. <span>repeat by $list as $item</span>
  39. <ul>
  40. <?php foreach ($list as $item) { ?><li >name:<?php echo $item["name"]; ?> -- email: <?php echo $item["email"]; ?></li><?php } ?>
  41. </ul>
  42. </div><?php } ?>
  43. <?php if ($list) { ?><div class="box" >
  44. <span>foreach by $list as $key => $item</span>
  45. <ul>
  46. <?php foreach ($list as $key => $item) { ?><li ><?php echo $key; ?> -- name:<?php echo $item["name"]; ?> -- email: <?php echo $item["email"]; ?></li><?php } ?>
  47. </ul>
  48. </div><?php } ?>
  49. <div class="box">
  50. <span>for by ($i = 1; $i <= 10; $i++;)</span>
  51. <ul>
  52. <?php for ($i = 1; $i <= 10; $i++) { ?><li >for <?php echo $i; ?></li><?php } ?>
  53. </ul>
  54. </div>
  55. <?php if ($list) { ?><div class="box" >
  56. <span>$list 不为空</span>
  57. </div><?php } ?>
  58. </body>
  59. </html>

写了个jquery的自动填充form插件

最近发现每次做后台编辑数据的时候, 给表单设置默认值都很麻烦, 这次干脆写了一个jquery插件, 直接自动填充表单, 省去php一个一个的写, 这样代码可读性也好了很多.

jquery.autofill.js

  1. jQuery.fn.extend({
  2. autofill: function (data) {
  3. if (!data || !$.isPlainObject(data)) {
  4. return;
  5. }
  6. for (var name in data) {
  7. if ($.isArray(data[name])) {
  8. var field = $('[name="' + name + '[]"]', this);
  9. field.each(function () {
  10. if (data[name].indexOf($(this).val()) >= 0) {
  11. $(this).attr('checked', 'checked');
  12. }
  13. });
  14. } else {
  15. var field = $('[name="' + name + '"]', this);
  16. field.each(function () {
  17. switch ($(this).attr('type')) {
  18. case 'radio':
  19. if ($(this).val() === data[name].toString()) {
  20. $(this).attr('checked', 'checked');
  21. }
  22. break;
  23. default:
  24. $(this).val(data[name]);
  25. break;
  26. }
  27. });
  28. }
  29. }
  30. }
  31. });

使用方法 ./test.php

  1. <!DOCTYPE HTML>
  2. <html lang="en-US">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <link href="css/bootstrap.css" rel="stylesheet" type="text/css"/>
  7. </head>
  8. <body>
  9. <div class="container">
  10. <form action="?" method="post" id="form">
  11. 姓名: <input type="text" name="name" id="" />
  12. <br />
  13. 年龄: <input type="number" name="age" id="" />
  14. <br />
  15. 性别:
  16. <input type="radio" name="sex" value="1" id="" />
  17. <input type="radio" name="sex" value="0" id="" />
  18. <br />
  19. 省份:
  20. <select name="area" id="">
  21. <option value="-1">选择省份</option>
  22. <option value="shanghai">上海</option>
  23. <option value="beijing">北京</option>
  24. <option value="henan">河南</option>
  25. </select>
  26. <br />
  27. 爱好:
  28. <input type="checkbox" name="hobby[]" value="book" id="" /> 看书
  29. <input type="checkbox" name="hobby[]" value="coding" id="" /> 写代码
  30. <input type="checkbox" name="hobby[]" value="games" id="" /> 打游戏<br />
  31. 简介:
  32. <textarea name="content"></textarea>
  33. <br />
  34. <input type="submit" value="提交" />
  35. </form>
  36. <!-- 这里是数据 -->
  37. <div class="hidden" id="data">
  38. <?php
  39. $data = [
  40. 'name' => 'shuai',
  41. 'age' => '25',
  42. 'area' => 'henan',
  43. 'sex' => 0,
  44. 'hobby' => ['book', 'games'],
  45. 'content' => 'hello world',
  46. ];
  47. echo json_encode($data);
  48. ?>
  49. </div>
  50. </div>
  51. <script src="jquery.min.js" type="text/javascript"></script>
  52. <script src="jquery.autofill.js" type="text/javascript"></script>
  53. <script type="text/javascript">
  54. $(function () {
  55. var data = $.parseJSON($('#data').text());
  56. $('#form').autofill(data);
  57. });
  58. </script>
  59. </body>
  60. </html>

访问

写了一个ThinkPHP的模板引擎, 仿angular的, 简单版

前段时间学习angularjs, 里面的模板思想和实现方法很酷, 就心血来潮, 想实现一个php版的, 今天试着写了一下, 发现貌似可以, 具体看源码.

./ThinkPHP/Library/Think/Template/Driver/Angular.class.php

  1. <?php
  2. namespace Think\Template\Driver;
  3. use Think\Storage;
  4. /**
  5. * Angular模板引擎驱动
  6. */
  7. class Angular {
  8. private $config = array();
  9. private $tpl_var = array();
  10. /**
  11. * 架构函数
  12. */
  13. public function __construct() {
  14. $this->config['cache_path'] = C('CACHE_PATH');
  15. $this->config['tpl_dir'] = THEME_PATH;
  16. $this->config['cache_path'] = C('CACHE_PATH');
  17. $this->config['template_suffix'] = C('TMPL_TEMPLATE_SUFFIX');
  18. $this->config['cache_suffix'] = C('TMPL_CACHFILE_SUFFIX');
  19. $this->config['tmpl_cache'] = C('TMPL_CACHE_ON');
  20. $this->config['cache_time'] = C('TMPL_CACHE_TIME');
  21. $this->config['attr'] = 'tp-';
  22. }
  23. /**
  24. * 编译模板
  25. * @param type $tpl_file 模板文件
  26. * @param type $tpl_var 模板变量
  27. */
  28. public function fetch($tpl_file, $tpl_var) {
  29. $this->tpl_var = $tpl_var;
  30. $tpl_file = $this->load_template($tpl_file);
  31. Storage::load($tpl_file, $tpl_var, null, 'tpl');
  32. }
  33. /**
  34. * 加载主模板并缓存
  35. * @param string $tpl_file 模板文件名
  36. * @return string 缓存的模板文件名
  37. */
  38. public function load_template($tpl_file) {
  39. if (is_file($tpl_file)) {
  40. // 读取模板文件内容
  41. $tpl_content = file_get_contents($tpl_file);
  42. } else {
  43. $tpl_content = $tpl_file;
  44. }
  45. // 根据模版文件名定位缓存文件
  46. $tpl_cache_file = $this->config['cache_path'] . md5($tpl_file) . $this->config['cache_suffix'];
  47. if (Storage::has($tpl_cache_file) && !APP_DEBUG && $this->config['tmpl_cache']) {
  48. return $tpl_cache_file;
  49. }
  50. // 编译模板内容
  51. $tpl_content = $this->compiler($tpl_content);
  52. Storage::put($tpl_cache_file, trim($tpl_content), 'tpl');
  53. return $tpl_cache_file;
  54. }
  55. /**
  56. * 编译模板内容
  57. * @param string $tpl_content 模板内容
  58. * @return string 编译后端php混编代码
  59. */
  60. protected function compiler($tpl_content) {
  61. //模板解析
  62. $tpl_content = $this->parse($tpl_content);
  63. // 添加安全代码
  64. $tpl_content = '<?php if (!defined(\'THINK_PATH\')) exit();?>' . $tpl_content;
  65. // 优化生成的php代码
  66. $tpl_content = str_replace('?><?php', '', $tpl_content);
  67. return strip_whitespace($tpl_content);
  68. }
  69. /**
  70. * 解析模板标签属性
  71. * @param string $content 要模板代码
  72. * @return string 解析后的模板代码
  73. */
  74. public function parse($content) {
  75. while (true) {
  76. $sub = $this->match($content);
  77. if ($sub) {
  78. $method = 'parse_' . $sub['attr'];
  79. if (method_exists($this, $method)) {
  80. $content = $this->$method($content, $sub);
  81. } else {
  82. E("模板属性" . $this->config['attr'] . $sub['attr'] . '没有对应的解析规则');
  83. break;
  84. }
  85. } else {
  86. break;
  87. }
  88. }
  89. $content = $this->parse_value($content);
  90. return $content;
  91. }
  92. /**
  93. * 解析include属性
  94. * @param string $content 源模板内容
  95. * @param array $match 一个正则匹配结果集, 包含 html, value, attr
  96. * @return string 解析后的模板内容
  97. */
  98. private function parse_include($content, $match) {
  99. $tpl_name = $match['value'];
  100. if (substr($tpl_name, 0, 1) == '$') {
  101. //支持加载变量文件名
  102. $tpl_name = $this->get(substr($tpl_name, 1));
  103. }
  104. $array = explode(',', $tpl_name);
  105. $parse_str = '';
  106. foreach ($array as $tpl) {
  107. if (empty($tpl))
  108. continue;
  109. if (false === strpos($tpl, $this->config['template_suffix'])) {
  110. // 解析规则为 模块@主题/控制器/操作
  111. $tpl = T($tpl);
  112. }
  113. // 获取模板文件内容
  114. $parse_str .= file_get_contents($tpl);
  115. }
  116. return str_replace($match['html'], $parse_str, $content);
  117. }
  118. /**
  119. * 解析if属性
  120. * @param string $content 源模板内容
  121. * @param array $match 一个正则匹配结果集, 包含 html, value, attr
  122. * @return string 解析后的模板内容
  123. */
  124. private function parse_if($content, $match) {
  125. $new = "<?php if ({$match['value']}) { ?>";
  126. $new .= str_replace($match['exp'], '', $match['html']);
  127. $new .= '<?php } ?>';
  128. return str_replace($match['html'], $new, $content);
  129. }
  130. /**
  131. * 解析repeat属性
  132. * @param string $content 源模板内容
  133. * @param array $match 一个正则匹配结果集, 包含 html, value, attr
  134. * @return string 解析后的模板内容
  135. */
  136. private function parse_repeat($content, $match) {
  137. $new = "<?php foreach ({$match['value']}) { ?>";
  138. $new .= str_replace($match['exp'], '', $match['html']);
  139. $new .= '<?php } ?>';
  140. return str_replace($match['html'], $new, $content);
  141. }
  142. /**
  143. * 解析show属性
  144. * @param string $content 源模板内容
  145. * @param array $match 一个正则匹配结果集, 包含 html, value, attr
  146. * @return string 解析后的模板内容
  147. */
  148. private function parse_show($content, $match) {
  149. $new = "<?php if ({$match['value']}) { ?>";
  150. $new .= str_replace($match['exp'], '', $match['html']);
  151. $new .= '<?php } ?>';
  152. return str_replace($match['html'], $new, $content);
  153. }
  154. /**
  155. * 解析hide属性
  156. * @param string $content 源模板内容
  157. * @param array $match 一个正则匹配结果集, 包含 html, value, attr
  158. * @return string 解析后的模板内容
  159. */
  160. private function parse_hide($content, $match) {
  161. $new = "<?php if (!({$match['value']})) { ?>";
  162. $new .= str_replace($match['exp'], '', $match['html']);
  163. $new .= '<?php } ?>';
  164. return str_replace($match['html'], $new, $content);
  165. }
  166. /**
  167. * 解析普通变量和函数{$title}{:function_name}
  168. * @param string $content 源模板内容
  169. * @return string 解析后的模板内容
  170. */
  171. private function parse_value($content) {
  172. $content = preg_replace('/\{(\$.*?)\}/', '<?php echo \1 ?>', $content);
  173. $content = preg_replace('/\{\:(.*?)\}/', '<?php echo \1 ?>', $content);
  174. return $content;
  175. }
  176. /**
  177. * 获取第一个表达式
  178. * @param string $content 要解析的模板内容
  179. * @return array 一个匹配的标签数组
  180. */
  181. private function match($content) {
  182. $reg = '#<(?<tag>[\w]+)[^>]*?\s(?<exp>' . preg_quote($this->config['attr']) . '(?<attr>[\w]+)=([\'"])(?<value>[^\4]*?)\4)[^>]*>#s';
  183. $match = null;
  184. if (!preg_match($reg, $content, $match)) {
  185. return null;
  186. }
  187. $sub = $match[0];
  188. $tag = $match['tag'];
  189. /* 如果是但标签, 就直接返回 */
  190. if (substr($sub, -2) == '/>') {
  191. $match['html'] = $match[0];
  192. return $match;
  193. }
  194. /* 查找完整标签 */
  195. $start_tag_len = strlen($tag) + 1; // <div
  196. $end_tag_len = strlen($tag) + 3; // </div>
  197. $start_tag_count = 0;
  198. $content_len = strlen($content);
  199. $pos = strpos($content, $sub);
  200. $start_pos = $pos + strlen($sub);
  201. while ($start_pos < $content_len) {
  202. $is_start_tag = substr($content, $start_pos, $start_tag_len) == '<' . $tag;
  203. $is_end_tag = substr($content, $start_pos, $end_tag_len) == "</$tag>";
  204. if ($is_start_tag) {
  205. $start_tag_count++;
  206. }
  207. if ($is_end_tag) {
  208. $start_tag_count--;
  209. }
  210. if ($start_tag_count < 0) {
  211. $match['html'] = substr($content, $pos, $start_pos - $pos + $end_tag_len);
  212. return $match;
  213. }
  214. $start_pos++;
  215. }
  216. return null;
  217. }
  218. }

./Application/Home/Controller/TestController.class.php

  1. <?php
  2. namespace Home\Controller;
  3. use Think\Controller;
  4. class TestController extends Controller {
  5. public function index() {
  6. C('SHOW_PAGE_TRACE', true);
  7. C('TMPL_ENGINE_TYPE', 'Angular');
  8. $data = array();
  9. $data['title'] = '标题';
  10. $data['nav'] = array(
  11. array('title' => '首页', 'url' => '/'),
  12. array('title' => '文章', 'url' => '/article'),
  13. array('title' => '图片', 'url' => '/pic'),
  14. array('title' => '新闻', 'url' => '/news'),
  15. );
  16. $data['count'] = 6;
  17. $data['list'] = array(
  18. array('id' => 1, 'title' => '这是标题1', 'create_time' => strtotime('-5 seconds')),
  19. array('id' => 2, 'title' => '这是标题2', 'create_time' => strtotime('-4 seconds')),
  20. array('id' => 3, 'title' => '这是标题3', 'create_time' => strtotime('-3 seconds')),
  21. array('id' => 4, 'title' => '这是标题4', 'create_time' => strtotime('-2 seconds')),
  22. array('id' => 5, 'title' => '这是标题5', 'create_time' => strtotime('-1 seconds')),
  23. array('id' => 6, 'title' => '这是标题6', 'create_time' => NOW_TIME),
  24. );
  25. $this->assign($data);
  26. $this->display('index');
  27. }
  28. }

./Application/Home/View/Test/index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Angular 模板测试 - {$title}</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <style>
  8. * {
  9. margin: 0px;
  10. padding: 0px;
  11. font-size: 12px;
  12. color: #333;
  13. line-height: 20px;
  14. }
  15. a {
  16. color: #33F;
  17. text-decoration: none;
  18. }
  19. a:hover {
  20. color: #f00;
  21. text-decoration: underline;
  22. }
  23. .center {
  24. text-align: center;
  25. }
  26. h1{
  27. font-size: 30px;
  28. line-height: 50px;
  29. }
  30. .nav {
  31. line-height: 30px;
  32. }
  33. .nav a{
  34. padding: 0px;
  35. margin: 0px 20px;
  36. }
  37. .main table{
  38. width: 500px;
  39. margin: 0px auto;
  40. }
  41. table {
  42. border: 1px solid #666;
  43. }
  44. table td,
  45. table th{
  46. border: 1px solid #666;
  47. line-height: 20px;
  48. padding: 0px 5px;
  49. }
  50. table th{
  51. background: #CCC;
  52. }
  53. #footer p{
  54. text-align: center;
  55. line-height: 30px;
  56. }
  57. </style>
  58. </head>
  59. <body>
  60. <div class="header">
  61. <h1 class="center">Angular 模板测试 - {$title}</h1>
  62. <div class="nav center" tp-if="$nav">
  63. <a tp-repeat="$nav as $vo" href="{$vo['url']}">{$vo['title']}</a>
  64. </div>
  65. </div>
  66. <div class="main">
  67. <table>
  68. <tr>
  69. <th>编号</th>
  70. <th>标题</th>
  71. <th>创建时间</th>
  72. <th>操作</th>
  73. </tr>
  74. <tr tp-if="$list" tp-repeat="$list as $vo">
  75. <td>{$vo['id']}</td>
  76. <td>{$vo['title']}</td>
  77. <td>{:date('Y-m-d H:i:s', $vo['create_time'])}</td>
  78. <td><a href="#del={$vo['id']}">删除</a></td>
  79. </tr>
  80. <tr tp-if="$count">
  81. <td colspan="4" class="center">共 {$count} 条数据</td>
  82. </tr>
  83. <tr tp-hide="$list">
  84. <td colspan="4" class="center">没有数据</td>
  85. </tr>
  86. </table>
  87. </div>
  88. <div tp-include="footer"></div>
  89. </body>
  90. </html>
  1. <footer id="footer">
  2. <div class="foot-warp">
  3. <p>
  4. © 2015 {:C('SITE_TITLE')} zhaishuaigan@qq.com    豫ICP备13012601号
  5. </p>
  6. </div>
  7. </footer>

运行/Test/index, 显示结果

目前只是实现了简单的解析, 还需要进一步完善, 比如配置啊, 扩展更多的标签啊什么的.

PHP取远程文件绝对路径

写采集程序的时候, 需要根据 当前页 和 下一页 计算出 下一页的绝对地址,写了一个常用的转换函数, 方便以后采集转换地址。

第一个参数是相对页面的url, 第二个参数是采集到的下一页地址, 然后就会返回一个带有http的下一个地址。

  1. <?php
  2. function get_abs_url($url, $filename) {
  3. // 如果是http|https|ftp|//开头的
  4. if (preg_match('/^[a-zA-Z]*\:?\/\//', $filename)) {
  5. return $filename;
  6. }
  7. // 如果是 /common/css/index.css
  8. if (substr($filename, 0, 1) == '/') {
  9. preg_match('/^[^\/]*\/\/[^\/]+/', $url, $domain);
  10. return $domain[0] . $filename;
  11. }
  12. // 去掉问号和#号后面的字符, 删除文件名, 只保留目录
  13. $url = strstr($url, '?', true) ? strstr($url, '?', true) :
  14. (strstr($url, '#', true) ? strstr($url, '#', true) : $url);
  15. $url = preg_replace('/[^\/]*$/', '', $url);
  16. // 如果是 ../../common/css/index.css
  17. if (substr($filename, 0, 3) == '../') {
  18. $filename = $url . $filename;
  19. while (preg_match('/[^\/\.]+\/\.\.\//', $filename)) {
  20. $filename = preg_replace('/[^\/\.]+\/\.\.\//', '', $filename);
  21. }
  22. return $filename;
  23. }
  24. // 如果是 ./common/css/index.css
  25. if (substr($filename, 0, 2) == './') {
  26. return str_replace('/./', '/', $url . $filename);
  27. }
  28. return $url . $filename;
  29. }

实现了下面几种模式:

  1. echo get_abs_url('http://www.baidu.com/blog/shuai/article/1.html?php=php#hello', '../../../common/css.css');
  2. echo '<br />';
  3. echo get_abs_url('http://www.baidu.com/blog/shuai/article/1.html?php=php#hello', 'http://static.baidu.com/common/css.css');
  4. echo '<br />';
  5. echo get_abs_url('http://www.baidu.com/blog/shuai/article/1.html?php=php#hello', '/common/css.css');
  6. echo '<br />';
  7. echo get_abs_url('http://www.baidu.com/blog/shuai/article/1.html?php=php#hello', './common/css.css');
  8. echo '<br />';
  9. echo get_abs_url('http://www.baidu.com/blog/shuai/article/1.html?php=php#hello', 'css.css');

在线版的php编辑器

phpIDE
一个php版的在线网页开发工具
直接把IDE目录放到你的网站根目录, 就可以通过访问 /IDE/index.html 来在线编辑你的网站代码.

在线IDE操作说明:

  1. 点击文件 打开编辑.
  2. 点击目录 进入目录.
  3. 按住shift+鼠标点选文件 可以删除文件或目录.
  4. 按住ctrl+鼠标点选文件 可以重命名和移动文件或目录.
  5. 在编辑器界面, 按下ctrl+,可以调出编辑器设置.
  6. 上传zip文件后, 点击可以在线解压.
  7. /IDE/config.php文件, 可以配置根目录, 只读文件或列表, 隐藏文件或目录列表, 有效防止误操作.
  8. 具体文档可以到 http://phpide.coding.io/IDE/doc 中查看
  9. 下载地址 http://ide.zhaishuaigan.cn

注: 在线编辑器并没有做过多的权限验证, 请不要搞破坏, 会导致别人无法测试.

sublime text 3 安装后要做的事

1. 安装package control

快捷键: Ctrl + 反撇号, 然后在弹框中输入以下代码回车
如果安装错误可到这个网址查找解决方法: https://packagecontrol.io/installation

  1. import urllib.request,os,hashlib; h = 'df21e130d211cfc94d9b0905775a7c0f' + '1e3d39e33b79698005270310898eea76'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

2. 注册

  1. —– BEGIN LICENSE —–
  2. Michael Barnes
  3. Single User License
  4. EA7E-821385
  5. 8A353C41 872A0D5C DF9B2950 AFF6F667
  6. C458EA6D 8EA3C286 98D1D650 131A97AB
  7. AA919AEC EF20E143 B361B1E7 4C8B7F04
  8. B085E65E 2F5F5360 8489D422 FB8FC1AA
  9. 93F6323C FD7F7544 3F39C318 D95E6480
  10. FCCC7561 8A4A1741 68FA4223 ADCEDE07
  11. 200C25BE DBBC4855 C4CFB774 C5EC138C
  12. 0FEC1CEF D9DCECEC D3A5DAD1 01316C36
  13. —— END LICENSE ——

3. Eclipce快捷键映射

菜单 -> preferences -> 按键绑定 - 用户

  1. [{
  2. "keys": ["shift+enter"],
  3. "command": "run_macro_file",
  4. "args": {
  5. "file": "Packages/Default/Add Line.sublime-macro"
  6. }
  7. }, {
  8. "keys": ["alt+up"],
  9. "command": "swap_line_up"
  10. }, {
  11. "keys": ["alt+down"],
  12. "command": "swap_line_down"
  13. }, {
  14. "keys": ["ctrl+alt+j"],
  15. "command": "join_lines"
  16. }, {
  17. "keys": ["ctrl+alt+down"],
  18. "command": "duplicate_line"
  19. }, {
  20. "keys": ["shift+ctrl+r"],
  21. "command": "show_overlay",
  22. "args": {
  23. "overlay": "goto",
  24. "show_files": true
  25. }
  26. }, {
  27. "keys": ["ctrl+shift+s"],
  28. "command": "save_all"
  29. }, {
  30. "keys": ["ctrl+l"],
  31. "command": "show_overlay",
  32. "args": {
  33. "overlay": "goto",
  34. "text": ":"
  35. }
  36. }, {
  37. "keys": ["shift+ctrl+f4"],
  38. "command": "close_all"
  39. }, {
  40. "keys": ["shift+ctrl+y"],
  41. "command": "lower_case"
  42. }, {
  43. "keys": ["shift+ctrl+x"],
  44. "command": "upper_case"
  45. }, {
  46. "keys": ["ctrl+d"],
  47. "command": "run_macro_file",
  48. "args": {
  49. "file": "Packages/Default/Delete Line.sublime-macro"
  50. }
  51. }]

4. 汉化

运行SublimeText3
点击 菜单 Preferneces -> Browse Packages
会打开 X:\Program Files\Sublime Text\Data\Packages 目录,点击向上并找到X:\Program Files\Sublime Text\Data\Installed Packages目录,从附件中下载汉化包并解压,复制Default.sublime-package到这个目录,无需重启即可直接看到汉化效果。
汉化包下载地址: http://img.xiumu.org/blog-uploads/2013/02/Sublime_Text_CN_3059.zip

5. 插件

  1. phpfmt
  1. {
  2. "enable_auto_align": true,
  3. "format_on_save": true,
  4. "indent_with_space": true,
  5. "option": "value",
  6. "php_bin": "C:/wamp/bin/php/php7.0.4/php.exe",
  7. "psr1": true,
  8. "psr2": true,
  9. "version": 4
  10. }

2、ConvertToUTF8
支持UTF-8编码的插件
3、Bracket Highlighter
用于匹配括号,引号和html标签。对于很长的代码很有用。安装好之后,不需要设置插件会自动生效
4、DocBlockr
DocBlockr可以自动生成PHPDoc风格的注释。它支持的语言有Javascript, PHP, ActionScript, CoffeeScript, Java, Objective C, C, C++
5、Emmet(Zen Coding)
快速生成HTML代码段的插件,强大到无与伦比,不知道的请自行google
6、SideBar Enhancements
这个插件改进了侧边栏,增加了许多功能
7、Themr
主题管理,切换主题的时候,不用自己修改配置文件了,用这个可以方便的切换主题
请参考  http://www.ladyloveit.com/sublime/developers-commonly-used-10-sublime-text-plugin

Javascript导出Excel表格

JavaScript导出Excel类

  1. var ExcelHelper = {};
  2. ExcelHelper.getBrowserName = function() {
  3. var ua = window.navigator.userAgent;
  4. //ie
  5. if (ua.indexOf("MSIE") >= 0) {
  6. return 'ie';
  7. }
  8. //firefox
  9. else if (ua.indexOf("Firefox") >= 0) {
  10. return 'Firefox';
  11. }
  12. //Chrome
  13. else if (ua.indexOf("Chrome") >= 0) {
  14. return 'Chrome';
  15. }
  16. //Opera
  17. else if (ua.indexOf("Opera") >= 0) {
  18. return 'Opera';
  19. }
  20. //Safari
  21. else if (ua.indexOf("Safari") >= 0) {
  22. return 'Safari';
  23. }
  24. }
  25. ExcelHelper.tableToExcel = (function() {
  26. var uri = 'data:application/vnd.ms-excel;base64,',
  27. template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>',
  28. base64 = function(s) {
  29. return window.btoa(unescape(encodeURIComponent(s)))
  30. },
  31. format = function(s, c) {
  32. return s.replace(/{(\w+)}/g,
  33. function(m, p) {
  34. return c[p];
  35. })
  36. }
  37. return function(table, name) {
  38. var ctx = {
  39. "worksheet": name || 'Worksheet',
  40. table: table.innerHTML
  41. }
  42. window.location.href = uri + base64(format(template, ctx))
  43. }
  44. })();
  45. ExcelHelper.CreateExcelByTable = function(table) {
  46. var bn = this.getBrowserName();
  47. if (bn == "ie") {
  48. var ax = new ActiveXObject("Excel.Application");
  49. var wb = ax.Workbooks.Add();
  50. var sheet = wb.Worksheets(1);
  51. var tr = document.body.createTextRange();
  52. tr.moveToElementText(table);
  53. tr.select();
  54. tr.execCommand("Copy");
  55. sheet.Paste();
  56. ax.Visible = true;
  57. var si = null;
  58. var cleanup = function() {
  59. if (si) {
  60. window.clearInterval(si);
  61. }
  62. }
  63. try {
  64. var fname = ax.Application.GetSaveAsFilename("Excel.xls", "Excel Spreadsheets (*.xls), *.xls");
  65. } catch (e) {
  66. print("Nested catch caught " + e);
  67. } finally {
  68. wb.SaveAs(fname);
  69. var savechanges = false;
  70. wb.Close(savechanges);
  71. ax.Quit();
  72. ax = null;
  73. si = window.setInterval(cleanup, 1);
  74. }
  75. } else {
  76. this.tableToExcel(table);
  77. }
  78. }

测试代码

  1. var table = document.getElementById('table_id');
  2. ExcelHelper.CreateExcelByTable(table);