少湖说 | 科技自媒体

互联网,科技,数码,鸿蒙

Yii2 项目自动上报Bug

原理

yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue

步骤

  • 配置SiteController中的actions方法
1
2
3
4
5
6
7
8
public function actions()
{
return [
'error' => [
'class' => 'app\helpers\web\ErrorAction',
],
];
}
  • 重写ErrorAction, 位于app\helpers\web\ErrorAction, 并修改常量URL,PRIVATE_TOKEN和ASSIGNEE_ID

如何获取project_id和assignee_id见 WIKI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
namespace app\helpers\web;

use yii;
use yii\base\Action;
use yii\base\Exception;
use yii\base\UserException;
use yii\web\HttpException;

class ErrorAction extends \yii\web\ErrorAction
{
const URL = '{host}/api/v3/projects/{project_id}/issues'; // host替换为主机地址, project_id为项目id
const PRIVATE_TOKEN = 'tD3Te-ctECeGwEHH7-ec';
const ASSIGNEE_ID = 21;

public function run()
{
if (($exception = Yii::$app->getErrorHandler()->exception) === null) {
$exception = new HttpException(404, Yii::t('yii', 'Page not found.'));
}

if ($exception instanceof HttpException) {
$code = $exception->statusCode;
} else {
$code = $exception->getCode();
}
if ($exception instanceof Exception) {
$name = $exception->getName();
} else {
$name = $this->defaultName ?: Yii::t('yii', 'Error');
}
$preCode = $code;
if ($code) {
$name .= " (#$code)";
}

if ($exception instanceof UserException) {
$message = $exception->getMessage();
} else {
$message = $this->defaultMessage ?: Yii::t('yii', 'An internal server error occurred.');
}
if ($code != '404') {
//自动向GitLab提交Bug
$url = self::URL;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'PRIVATE-TOKEN: '.self::PRIVATE_TOKEN,
));

curl_setopt($ch, CURLOPT_POSTFIELDS, [
'title' => $message,
'description' => '<blockquote>'.Yii::$app->request->getReferrer().'</blockquote>'. '<blockquote>' . Yii::$app->request->absoluteUrl . '</blockquote><br/><pre>' . $exception . '</pre>',
'assignee_id' => self::ASSIGNEE_ID,
'labels' => '捕虫器,' . $name,
]);
curl_setopt($ch, CURLOPT_HEADER, false);
// Pass TRUE or 1 if you want to wait for and catch the response against the request made
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// For Debug mode; shows up any error encountered during the operation
curl_setopt($ch, CURLOPT_VERBOSE, false);
$response = curl_exec($ch);
curl_close($ch);
}
if (Yii::$app->getRequest()->getIsAjax() || strpos($_SERVER['REQUEST_URI'], '/api/') > -1) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'message' => $message
];
} else {
return $this->controller->render($this->view ?: $this->id, [
'name' => $name,
'message' => $message,
'exception' => $exception,
]);
}
}
}

原理

先解析markdown为HTML, 然后解析出h1-h10标签, 根据h标签的前后大小, 决定ul的层级, 以此生成ul序列的嵌套

  • 如果当前H标签和前面H标签相同, 则生成<li></li>,
  • 如果比前面的大, 则生成<ul>
  • 如果比前面的小, 生成 </ul><ul><li></li>

上手

  • 选择一个markdown Parser库, 将markdown解析

$file为文件路径, $docs为解析后的html字串

1
2
3
4
$content = file_get_contents($file);
require_once('/var/www/html/vendor/erusev/parsedown/Parsedown.php');
$parsedown = new \Parsedown();
$docs = $parsedown->text($content);
  • 解析DOM, 根据H标签的前后顺序, 生成TOC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$dom = new \DOMDocument();
$docs = mb_convert_encoding($docs, 'HTML-ENTITIES', "UTF-8");
$dom->loadHTML($docs);

// The toc being generated.
$toc = '';
$curr = $last = 0;
$type = "ul";
$xpath = new \DomXPath($dom);
$t = $xpath->query('//h1|//h2|//h3|//h4|//h5|//h6');
foreach ($t as $key => $item) {
$level = ltrim($item->nodeName, 'h');
sscanf($item->nodeName, 'h%u', $curr);
// If the current level is greater than the last level indent one level
if ($curr > $last) {
if ($last == 0) {
$toc .= '<' . $type . " class='nav doc-menu affix-top' id='doc-menu' data-spy='affix'>\n";
} else {
$toc .= '<' . $type . " class='nav doc-sub-menu'>\n";
}
} // If the current level is less than the last level go up appropriate amount.
elseif ($curr < $last) {
$toc .= str_repeat('</li></' . $type . ">\n", $last - $curr) . "</li>\n";
} // If the current level is equal to the last.
else {
$toc .= "</li>\n";
}
// Get and/or set an id
if ($item->hasAttribute('id')) {
$id = $item->getAttribute('id');
} else {
//auto generate id for the tag
$id = uniqid();
$t[$key]->setAttribute('id', $id);
}
if ($last == 0) {
$toc .= '<li class="active"><a class="scrollto" href="#' . $id . '">' . $item->nodeValue . "</a>\n";
} else {
$toc .= '<li><a class="scrollto" href="#' . $id . '">' . $item->nodeValue . "</a>\n";
}
$last = $curr;
}
// 将修改后的DOM 赋值为docs
$docs = $dom->saveHTML();
  • 渲染页面
    $docs为html主体内容
    $toc为生成的目录, 形如
1
2
3
4
5
6
7
8
9
<ul>
<li></li>
<ul>
<li></li>
<li></li>
</ul>
<li></li>
<li></li>
</ul>

安装步骤

    1. 安装部署Docker
      因为docker对内核版本有要求,所有要先升级系统(linux内核3.0+),这里用的CentOS 7
  • 2.拉取镜像 docker pull sameersbn/redmine:latest

docker镜像加速

1
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://0835afe2.m.daocloud.io
    1. 安装docker-compose, “由于网络原因,该步骤可能会下载失败,请尝试多下载几次,或者外挂代理”
1
curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

修改权限

1
chmod +x /usr/local/bin/docker-compose
    1. 使用compose快速启动 可以需要外挂
1
2
wget https://raw.githubusercontent.com/sameersbn/docker-redmine/master/docker-compose.yml
docker-compose up
    1. 映射数据目录

注意事项

  • linux内核要最新 3.0+
  • redmine默认使用的端口是10083,如果启动成功后,无法打开,使用telnet 远程检测端口访问性,如果不能访问,检查两个方面:1.本机防火墙是否禁用端口(用telnet 检测);2.运营商/云端是否禁用
  • 注意使用volumn命令映射数据存储命令

参考链接

常见面试内容

技术线(偏深度):

    1. 基础知识,比如数据结构,多线程,I/O,Http协议等
    1. 语言深度,比如PHP的运行机制,性能优化,APC(以及其原理等)
    1. 源码阅读,比如看过哪些框

写出一下各段代码的输出结果:

1
2
3
4
5
6
7
$a = (1 < < 0);
$b = (1 < < $a);
$c = (1 < < $b);
echo ($c

$b) < < 2 * $a
$a;
    1. echo '1'.print(2)+3;
  • 3

1
2
3
4
5
$a = 0x01;
$b = 0x02;
echo $a===$b > > $a;

$foo = 5 + '10 things';
    1. print (196*100) !== (double)1960;
    1. echo 0500
    1. $a = 0; echo ~$a;

数据库

    1. 解释一下SQL注入的原理及危害,假设这里有一个不严谨的登陆程序,如何利用SQL注入进入。
    1. 解释一下跨站脚本的原理及危害,假设这里有一个不严谨的留言板,请构造跨站脚本范例。
    1. 解释一下数据索引为什么能提高效率。
      场景 SQL select * from user where area='广州‘ and sex='女‘ order by lastlogin desc limit 30;
  • 4.如何设计索引?
  • 5.现在有10万条 IP地址对应区间表,格式为 startip, endip, area
    要求每个用户访问时都能快速分析出对方地区,请设计实现。数据库或不用数据库,要求每秒钟实现超过千次的查询。
    基本上到这些对我而言就已经足够了,如果上面题目特别满意的,再附加
    1. 现在有一个屏蔽词列表,大约几千个词;假设有一个繁忙的论坛社区,发帖量巨大,要求用户每发一篇文章就要快速分析是否包含屏蔽词,请给出程序设计。
    1. 一个游戏网站,有数千个小游戏,每个游戏要做积分排行,数据结构如下
      gameid,userid,gamescore
  • 8.每天数百万积分提交,数据量巨大,目前要求分库处理。
    要求,可以基于每个游戏id可以查询积分排行榜。 可以基于用户id查询,比如自己或好友的历史游戏积分记录。请问如何处理分库。

专业程度体现在对PHP的基本功的掌握和一些进阶知识的了解,比如对常见性能点、不同版本特性等,session的实现机制、include/require的差异,进一步的主流框架掌握程度、常见的性能优化策略、其作为脚本语言所具备的一些特性、如何规避PHP的性能劣势打造高并发的系统、如何保证高并发下的可用性和数据完整性等等。

周边知识主要会包括MySQL性能优化、Memcache使用、Nginx/Apache的配合、对于PV/Click等数据收集和观察的方法等。 同样的,利用curl/socket操作http/tcp/udp,进一步的RPC调用(Web Service/JSONP/Thrift等)的前后端实现,进一步的包括更多的Cache策略(远近、一致性哈希、数据一致性)、对Sharding即分库分表之类操作的了解、更多NoSQL产品知识、各类常见协议细节、安全性(注入和旁注、跨站和CC)等。


  • 0.简单做一下自我介绍,? 然后谈一下近三年来你的得意之作?

  • 1.看看简历,会问一些过去做的项目的用户量、pv、吞吐量、相关难点和解决方法等

  • 2.数据库设计经验,为什么进行分表? 分库?
    一般多少数据量开始分表? 分库? 分库分表的目的? 什么是数据库垂直拆分? 水平拆分? 分区等等?可以举例说明

  • 3.数据库优化有哪些? 分别需要注意什么?

  • 4.web开发方面会遇到哪些缓存? 分别如何优化?

  • 5.给你256M的内存,对10G的文件进行排序(文件每行1个数字),如何实现?
    对10G的文件进行查找如何实现?
    统计10G文件每个关键字出现的次数如何实现?

  • 6.假如你现在是12306火车订票的设计师,你该如何设计满足全国人民订票?

  • 7.假如有1亿用户的访问量,你的服务器架构是怎样的? 用户信息的存储方案如何设计?

  • 8.如果你是技术组长,所带团队任务进度无法完成你该如何解决?
    如果在进度排满的前提下插入任务,你该如何保证总进度不延期?
    如果有的工程师今天预定任务没有完成,你该如何解决?

  • 9.从你的经验方面谈一下如何构建高性能web站点? 需要哪些环节? 步骤? 每个步骤需要注意什么如何优化等?

  • 10.为什么要对数据库进行主从分离?

  • 11.如何处理多服务器共享session?

  • 12.一个10G的表,你用php程序统计某个字段出现的次数,思路是?

  • 13.会告诉你一个nginx日志例子,用你认为最佳的编程语言统计一下http响应时间超过1秒的前10个url?

  • 14.给你一个mysql配置文件,用你认为最佳的编程语言解析该文件?

  • 15.给你两个路径a和b,写一个算法或思路计算a和b差距几层并显示a和b的交集?

  • 16.给你一个url,在nginx配置一下rewrite指定到某个具体路径?

  • 17.一个php文件的解释过程是? 一般加速php有哪些? 提高php整体性能会用到哪些技术?

  • 18.session和cookie生存周期区别? 存储位置区别?

  • 19.require、include、require_once、include_once区别? 加载区别? 如果程序按需加载某个php文件你如何实现?

  • 20.chrome号称为多线程的,所以多线程和多进程的区别为?

  • 21.php在2011年底出现hash碰撞,hash碰撞原理为? 如何进行修复?

  • 22.web不安全因素有哪些? 分别如何防范?

  • 23.假如两个单链表相交,写一个最优算法计算交点位置,说思路也可以?

  • 24.假如你是技术组长? 如何提高团队效率?

  • 25.nginx负载均衡有哪些? 如果其中一台服务器挂掉,报警机制如何实现?

  • 26.不优化前提下,apache一般最大连接数为? nginx一般最大连接数为? mysql 每秒insert ? select ? update ? delete?

  • 27.mysql 数据类型有哪些 ? 分别占用多少存储空间 ?

  • 28.nginx设置缓存js、css、图片等信息,缓存的实现原理是?

  • 29.如何提高缓存命中率? 如何对缓存进行颗粒化?

  • 30.php的内存回收机制是?

  • 31.我的所有问题都问完了,你有什么问题问我没有?

  • 32.请使用shell命令获取a文件的前10行与后10行的数据,将其放进b文件中。

  • 33.如何设计安全的php程序,可以防止SQL攻击。

  • 34.这里有一个sql语句,select id from table where a=? order by b,请你就句子判断,建立索引,使其能够在innodb和myisam引擎下,查询效果都可以加快。

  • 35.有一天,突然有一个用户投诉,说网页打不开,你该如何进行排查?

  • 36.何对一个亿级数据的表按照一个没有索引的字段进行统计


php实现扩展的机制

全站消息系统

消息队列

PHP的数组为什么不按照索引遍历

http://blog.sina.com.cn/s/blog_60d07c3101010m2h.html

http://www.php-note.com/article/detail/546

http://my.oschina.net/u/574366/blog/64814

  • 在Chrome中安装PHP Console 插件

  • 下载PHP Console 服务器端程序包到ThinkPHP的Vendor目录下
    例如 /ThinkPHP/Extend/Vendor/PhpConsole

  • 编写Behaviour行为类PhpConsoleBehavior.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

<?php
/*
* 程序初始化时,在DEBUG模式下自动导入PHP Console类并进行实例化
* PHP console for chrome degug tools
* @author zacksleo
*/
class PhpConsoleBehavior extends Behavior{

protected $options = array(
'PHP_CONSOLE' => false,
);

public function run(&$params){
if(C('PHP_CONSOLE')){
if(APP_DEBUG){
vendor('PhpConsole.__autoload'); //导入文件
PhpConsole\Helper::register(); //注册,自动实例化
$connector = PhpConsole\Connector::getInstance();
$connector->setPassword('password');
$handler = PhpConsole\Handler::getInstance();
// 输出PHP错误和异常
$handler->start();
// 配置 eval provider(在Chrome中远程执行PHP),如果不使用,则不配置
$evalProvider = $connector->getEvalDispatcher()->getEvalProvider();
$evalProvider->setOpenBaseDirs(array(__DIR__));
// 必须最后调用
$connector->startEvalRequestsListener();
}
}
}
}
?>
  • 在/App/Conf/tags.php 中配置标签位:’app_init’ => array(‘PhpConsole’),
  • 在config.php文件中定义标签’PHP_CONSOLE’ => true,
  • 修改ThinkPHP的Log.class文件(位于/ThinkPHP/Lib/Core/Log.class.php)中的recode方法如下
1
2
3
4
5
6
7
8
9
10
11
static function record($message, $level = self::ERR, $record = false){
//zacksleo
if($level == self::SQL){
PC::debug($message, 'SQL');
}
//zacksleo
if($record || false !== strpos(C('LOG_LEVEL'), $level)){
self::$log[] = "{$level}: {$message}\r\n";
}
}

0%