[php]curl_init()和curl_multi_init()多线程的速度比较

php中curl_init()的作用很大,尤其是在抓取网页内容或文件信息的时候.

curl_init()处理事物是单线程模式,如果需要对事务处理走多线程模式,那么php里提供了一个函数curl_multi_init()给我们,这就是多线程模式处理事务的函数。

curl_init()与curl_multi_init()的速度比较

curl_multi_init()多线程能提高网页的处理速度吗?今天我通过实验来验证一下这个问题。

今天我的测试很简单,那就是要抓取某接口网页的内容,要连续抓5次,分别用curl_init()和curl_multi_init()函数来完成,记录两者的耗时,比较得出结论。

首先,用curl_init()单线程连续抓5次www.webkaka.com网页的内容。

程序代码如下:

        $time = microtime(true);
        for($i=1; $i<=5; $i++){
            $szUrl = 'http://172.30.16.212:8095/draw/';
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $szUrl);
            curl_setopt($curl, CURLOPT_HEADER, 0);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_ENCODING, '');
            $data=curl_exec($curl);
            echo "done:{$i}";
            echo "<br>";
            $ntime = microtime(true);
            echo $ntime - $time;
            echo "<br>";
        
        }

然后,用curl_multi_init()多线程连续抓5次api网页的内容。

代码如下:

 $time = microtime(true);
        $urls = array(
                'http://172.30.16.212:8095/draw/',
                'http://172.30.16.212:8095/draw/',
                'http://172.30.16.212:8095/draw/',
                'http://172.30.16.212:8095/draw/',
                'http://172.30.16.212:8095/draw/',
                );
        
        $data = $this->_async_get_url($urls);
        $ntime = microtime(true);
        echo $ntime - $time;
protected function _async_get_url($url_array, $wait_usec = 0) {
 
        
        $wait_usec = intval($wait_usec);
        $data    = array();
        $handle  = array();
        $running = 0;
        $mh = curl_multi_init(); // multi curl handler
        $i = 0;
        foreach($url_array as $url) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
            curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
            curl_multi_add_handle($mh, $ch); // 把 curl resource 放进 multi curl handler 里
            $handle[$i++] = $ch;
        }
        /* 执行 */
        do {
            curl_multi_exec($mh, $running);
            if ($wait_usec > 0) /* 每个 connect 要间隔多久 */
                usleep($wait_usec); // 250000 = 0.25 sec
        } while ($running > 0);
        /* 读取资料 */
        foreach($handle as $i => $ch) {
            $content  = curl_multi_getcontent($ch);
            $data[$i] = (curl_errno($ch) == 0) ? $content : false;
        }
        /* 移除 handle*/
        foreach($handle as $ch) {
            curl_multi_remove_handle($mh, $ch);
        }
        curl_multi_close($mh);
        return $data;            
    }

curl_init():

done:1
0.036259174346924
done:2
0.071941137313843
done:3
0.11369609832764
done:4
0.13930606842041
done:5
0.17635917663574
都是总结间

curl_multi_init():

0.05805516242981

关于curl_multi_init()

一般来说,想到要用curl_multi_init()时,目的是要同时请求多个url,而不是一个一个依次请求,否则就要curl_init()了。

不过,在使用curl_multi的时候,你可能遇到cpu消耗过高、网页假死等现象,可以看看如何解决curl_multi导致网页假死的问题

使用curl_multi的步骤总结如下:

第一步:调用curl_multi_init
第二步:循环调用curl_multi_add_handle
这一步需要注意的是,curl_multi_add_handle的第二个参数是由curl_init而来的子handle。
第三步:持续调用curl_multi_exec
第四步:根据需要循环调用curl_multi_getcontent获取结果
第五步:调用curl_multi_remove_handle,并为每个字handle调用curl_close
第六步:调用curl_multi_close

各函数作用解释:

curl_multi_init()
初始化一个curl批处理句柄资源。

curl_multi_add_handle()
向curl批处理会话中添加单独的curl句柄资源。curl_multi_add_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。

curl_multi_exec()
解析一个curl批处理句柄,curl_multi_exec()函数有两个参数,第一个参数表示一个批处理句柄资源,第二个参数是一个引用值的参数,表示剩余需要处理的单个的curl句柄资源数量。

curl_multi_remove_handle()
移除curl批处理句柄资源中的某个句柄资源,curl_multi_remove_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。

curl_multi_close()
关闭一个批处理句柄资源。

curl_multi_getcontent()
在设置了CURLOPT_RETURNTRANSFER的情况下,返回获取的输出的文本流。

curl_multi_info_read()
获取当前解析的curl的相关传输信息。

实例
请看本文里async_get_url()的写法。

[php]小知识php数组key类型

PHP array和别的语言不一样。

PHP数组的key可以为int,也可以为string,引申出下面的问题。

$a = array(
    1 => 'as',
    2 => '22',
);
var_dump($a[0]);
var_dump($a[2]);
var_dump($a['2']);
exit;

结果:$a[0]未定义,$a[2] 与 $a[‘2’]相同。

$a = array(
    '1' => 'as',
    '2' => '22',
);
var_dump($a[0]);
var_dump($a[2]);
var_dump($a['2']);

结果:$a[0]未定义,$a[2] 与 $a[‘2’]相同。

问题:string作为key,然后输出是string还是int。

$a = array(
    '1' => 'as',
    '2' => '22',
);
foreach($a as $k=>$v) {
    if($k === 1) {
        echo 'int';
    }
}

结果:输出int。

问题:现在可以得出结论说php array会自动把数字字符串的key转成int?请看下面的例子。

参考:http://www.cnblogs.com/sink_cup/archive/2010/12/12/php_intval_mysql_int.html

$a = array(
    'asdf' => 'a',
    '2' => '22',
    '2147483647' => '32 max',
    '2147483648' => '32 max + 1',
    '9223372036854775807' => '64 max',
    '9223372036854775808' => '64 max + 1',
    9223372036854775809 => '64 max + 2',
);
foreach($a as $k=>$v) {
        var_dump($k);
        echo ' ';
        var_dump($v);
}

结论:php的int取值范围与系统有关,在int的有效范围内,php array会把数字字符串的key转成int;当key超过int范围时,不再转换。
如果是字符串刚好可以转为ini型,作为key时可能会到意想不到的坑。

[转]Yii权限控制

基于角色的访问控制(Role-Based Access Control)

基于角色的访问控制提供了一种简单而又强大的集中访问控制。请参阅维基文章了解更多详细的RBAC与其他较传统的访问控制模式的比较。

Yii 通过其 authManager 组件实现了分等级的 RBAC 结构。在下文中,我们将首先介绍在此结构中用到的主要概念。然后讲解怎样定义用于授权的数据。在最后,我们看看如何利用这些授权数据执行访问检查。

概览(Overview)

在 Yii 的 RBAC 中,一个基本的概念是 授权项目(authorization item)。一个授权项目就是一个做某件事的许可(例如新帖发布,用户管理)。根据其粒度和目标受众,授权项目可分为 操作(operations),任务(tasks) 和 角色(roles)。一个角色由若干任务组成,一个任务由若干操作组成, 而一个操作就是一个许可,不可再分。例如,我们有一个系统,它有一个 管理员 角色,它由 帖子管理 和 用户管理 任务组成。 用户管理 任务可以包含 创建用户,修改用户 和 删除用户 操作组成。为保持灵活性,Yii 还允许一个角色包含其他角色或操作,一个任务可以包含其他操作,一个操作可以包括其他操作。

授权项目是通过它的名字唯一识别的。

一个授权项目可能与一个 业务规则 关联。业务规则是一段 PHP 代码,在进行涉及授权项目的访问检查时将会被执行。仅在执行返回 true 时,用户才会被视为拥有此授权项目所代表的权限许可。例如,当定义一个 updatePost(更新帖子) 操作时,我们可以添加一个检查当前用户 ID 是否与此帖子的作者 ID 相同的业务规则,这样,只有作者自己才有更新帖子的权限。

通过授权项目,我们可以构建一个 授权等级体系 。在等级体系中,如果项目 A 由另外的项目 B 组成(或者说 A 继承了 B 所代表的权限),则 A 就是 B 的父项目。一个授权项目可以有多个子项目,也可以有多个父项目。因此,授权等级体系是一个偏序图(partial-order graph)结构而不是一种树状结构。在这种等级体系中,角色项目位于最顶层,操作项目位于最底层,而任务项目位于两者之间。

一旦有了授权等级体系,我们就可以将此体系中的角色分配给用户。而一个用户一旦被赋予一个角色,他就会拥有此角色所代表的权限。例如,如果我们赋予一个用户 管理员 的角色,他就会拥有管理员的权限,包括 帖子管理 和 用户管理 (以及相应的操作,例如 创建用户)。

现在有趣的部分开始了,在一个控制器动作中,我们想检查当前用户是否可以删除指定的帖子。利用 RBAC 等级体系和分配,可以很容易做到这一点。如下:

view plaincopy
if(Yii::app()->user->checkAccess(‘deletePost’))
{
// 删除此帖
}

配置授权管理器(Authorization Manager)

在我们准备定义一个授权等级体系并执行访问权限检查之前,我们需要配置一下 authManager 应用组件。 Yii 提供了两种授权管理器: CPhpAuthManager 和 CDbAuthManager。前者将授权数据存储在一个 PHP 脚本文件中而后者存储在数据库中。配置 authManager 应用组件时,我们需要指定使用哪个授权管理器组件类,以及所选授权管理器组件的初始化属性值。例如:

view plaincopy
return array(
‘components’=>array(
‘db’=>array(
‘class’=>’CDbConnection’,
‘connectionString’=>’sqlite:path/to/file.db’,
),
‘authManager’=>array(
‘class’=>’CDbAuthManager’,
‘connectionID’=>’db’,
),
),
);

然后,我们便可以使用 Yii::app()->authManager 访问 authManager 应用组件。

定义授权等级体系

定义授权等级体总共分三步:定义授权项目,建立授权项目之间的关系,还要分配角色给用户。 authManager 应用组件提供了用于完成这三项任务的一系列 API 。

要定义一个授权项目,可调用下列方法之一,具体取决于项目的类型:

CAuthManager::createRole
CAuthManager::createTask
CAuthManager::createOperation
建立授权项目之后,我们就可以调用下列方法建立授权项目之间的关系:

CAuthManager::addItemChild
CAuthManager::removeItemChild
CAuthItem::addChild
CAuthItem::removeChild
最后,我们调用下列方法将角色分配给用户。

CAuthManager::assign
CAuthManager::revoke
下面的代码演示了使用 Yii 提供的 API 构建一个授权体系的例子:

view plaincopy
$auth=Yii::app()->authManager;

$auth->createOperation(‘createPost’,’create a post’);
$auth->createOperation(‘readPost’,’read a post’);
$auth->createOperation(‘updatePost’,’update a post’);
$auth->createOperation(‘deletePost’,’delete a post’);

$bizRule=’return Yii::app()->user->id==$params[“post”]->authID;’;
$task=$auth->createTask(‘updateOwnPost’,’update a post by author himself’,$bizRule);
$task->addChild(‘updatePost’);

$role=$auth->createRole(‘reader’);
$role->addChild(‘readPost’);

$role=$auth->createRole(‘author’);
$role->addChild(‘reader’);
$role->addChild(‘createPost’);
$role->addChild(‘updateOwnPost’);

$role=$auth->createRole(‘editor’);
$role->addChild(‘reader’);
$role->addChild(‘updatePost’);

$role=$auth->createRole(‘admin’);
$role->addChild(‘editor’);
$role->addChild(‘author’);
$role->addChild(‘deletePost’);

$auth->assign(‘reader’,’readerA’);
$auth->assign(‘author’,’authorB’);
$auth->assign(‘editor’,’editorC’);
$auth->assign(‘admin’,’adminD’);

建立此授权等级体系后,authManager 组件(例如 CPhpAuthManager, CDbAuthManager)就会自动加载授权项目。因此,我们只需要运行上述代码一次,并不需要在每个请求中都要运行。

信息: 上面的示例看起来比较冗长拖沓,它主要用于演示的目的。 开发者通常需要开发一些用于管理的用户界面,这样最终用户可以通过界面更直观地建立一个授权等级体系。

使用业务规则

在定义授权等级体系时,我们可以将 业务规则 关联到一个角色,一个任务,或者一个操作。我们也可以在为一个用户分配角色时关联一个业务规则。一个业务规则就是一段 PHP 代码,在我们执行权限检查时被执行。代码返回的值用来决定是否将角色或分配应用到当前用户。在上面的例子中,我们把一条业务规则关联到了 updateOwnPost 任务。在业务规则中,我们简单的检查了当前用户的 ID 是否与指定帖子的作者 ID 相同。 $params 数组中的帖子(post)信息由开发者在执行权限检查时提供。

权限检查

要执行权限检查,我们首先需要知道授权项目的名字。例如,要检查当前用户是否可以创建帖子,我们需要检查他是否拥有 createPost 所表示的权限。然后我们调用 CWebUser::checkAccess 执行权限检查:

view plaincopy
if(Yii::app()->user->checkAccess(‘createPost’))
{
// 创建帖子
}

如果授权规则关联了一条需要额外参数的业务规则,我们也可以传递给它。例如,要检查一个用户是否可以更新帖子,我们可以通过 $params 传递帖子的数据:

view plaincopy
$params=array(‘post’=>$post);
if(Yii::app()->user->checkAccess(‘updateOwnPost’,$params))
{
// 更新帖子
}

使用默认角色

注意: 默认角色功能从 1.0.3 版本起可用。

许多 Web 程序需要一些可以分配给系统中所有或大多数用户的比较特殊的角色。例如,我们可能想要分配一些权限给所有已通过身份验证的用户。如果我们特意指定并存储这些角色分配,就会引起很多维护上的麻烦。我们可以利用 默认角色 解决这个问题。

默认角色就是一个隐式分配给每个用户的角色,这些用户包括通过身份验证的用户和游客。我们不需要显式地将其分配给一个用户。当 CWebUser::checkAccess 被调用时,将会首先检查默认的角色,就像它已经被分配给这个用户一样。

默认角色必须定义在 CAuthManager::defaultRoles 属性中。例如,下面的配置声明了两个角色为默认角色:authenticated 和 guest。

view plaincopy
return array(
‘components’=>array(
‘authManager’=>array(
‘class’=>’CDbAuthManager’,
‘defaultRoles’=>array(‘authenticated’, ‘guest’),
),
),
);

由于默认角色会被分配给每个用户,它通常需要关联一个业务规则以确定角色是否真的要应用到用户。例如,下面的代码定义了两个角色, authenticated 和 guest,很高效地分别应用到了已通过身份验证的用户和游客用户。

view plaincopy
$bizRule=’return !Yii::app()->user->isGuest;’;
$auth->createRole(‘authenticated’, ‘authenticated user’, $bizRule);

$bizRule=’return Yii::app()->user->isGuest;’;
$auth->createRole(‘guest’, ‘guest user’, $bizRule);

linux系统硬件配置查看方法

一:查看cpu

more /proc/cpuinfo | grep “model name”

grep “model name” /proc/cpuinfo

如果觉得需要看的更加舒服

grep “model name” /proc/cpuinfo | cut -f2 -d:

二:查看内存

grep MemTotal /proc/meminfo

grep MemTotal /proc/meminfo | cut -f2 -d:

free -m |grep “Mem” | awk ‘{print $2}’

三:查看cpu是32位还是64位

查看CPU位数(32 or 64)

getconf LONG_BIT

四:查看当前linux的版本

more /etc/redhat-release

cat /etc/redhat-release

五:查看内核版本

uname -r

uname -a

六:查看当前时间

date

上面已经介绍如何同步时间了,

七:查看硬盘和分区

df -h

fdisk -l

也可以查看分区

du -sh

可以看到全部占用的空间

du /etc -sh

可以看到这个目录的大小

du -h –max-depth=1 ./
可以看到当前文件夹一级目录所占的大小

八:查看安装的软件包

查看系统安装的时候装的软件包

cat -n /root/install.log

more /root/install.log | wc -l

查看现在已经安装了那些软件包

rpm -qa

rpm -qa | wc -l

yum list installed | wc -l

不过很奇怪,我通过rpm,和yum这两种方式查询的安装软件包,数量并不一样。没有找到原因。

九:查看键盘布局

cat /etc/sysconfig/keyboard

cat /etc/sysconfig/keyboard | grep KEYTABLE | cut -f2 -d=

十:查看selinux情况

sestatus

sestatus | cut -f2 -d:

cat /etc/sysconfig/selinux

十一:查看ip,mac地址

在ifcfg-eth0 文件里你可以看到mac,网关等信息。

ifconfig

cat /etc/sysconfig/network-scripts/ifcfg-eth0 | grep IPADDR

cat /etc/sysconfig/network-scripts/ifcfg-eth0 | grep IPADDR | cut -f2 -d=

ifconfig eth0 |grep “inet addr:” |awk ‘{print $2}’|cut -c 6-

ifconfig | grep ‘inet addr:’| grep -v ‘127.0.0.1’ | cut -d: -f2 | awk ‘{ print $1}’

查看网关

cat /etc/sysconfig/network

查看dns

cat /etc/resolv.conf

十二:查看默认语言

echo $LANG $LANGUAGE

cat /etc/sysconfig/i18n

十三:查看所属时区和是否使用UTC时间

cat /etc/sysconfig/clock

十四:查看主机名

hostname

cat /etc/sysconfig/network

修改主机名就是修改这个文件,同时最好也把host文件也修改。

查看pci信息 lspci

查看硬盘信息 df -lh

curl –head www.163.com 查看163.com的服务器环境

uptime 查看开机时间多长

ifconfig eth0 up 启用网卡eth0

/etc/initd/network restart 重启网络服务

简单的抽奖接口设计

抽奖使用简单的服务提供高并发的访问,这里提供一下简单设计方案
一 数据库结构
1 user
所有参与的用户,每参加一次,insert一条记录
2 winner
获奖表,只有获奖用户才insert到这张表,并且查询用户是否已经获奖,保证只能获奖一次,减小了查询表的数据量
二 流程
1, 检测手机号是否合法
2, 检测referer是否合法
3, 参与用户的电话写入到参与队列中,同时脚本消化参与队列,写入到user表
4, Cache中获得剩余获奖名额,根据中奖率(剩余/总数)过滤运气不好的用户
5, 查询是否中过奖,没中奖写入到winner数据表
6, 剩余获奖名额-1,重写cache
7, 写入到发奖队列,发奖脚本消化队列
三 总结
1,使用cache或者redis
2,数据库只有insert和查询
3,使用消息队列
并发量有待考验!

[转]SQL语句优化原则

原文地址:http://www.cnblogs.com/pepcod/archive/2013/01/01/2913496.html
处理百万级以上的数据提高查询速度的方法:

1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,

如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0

4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20

5.下面的查询也将导致全表扫描:(不能前置百分号)
select id from t where name like ‘%abc%’
若要提高效率,可以考虑全文检索。

6.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3

select xx,phone FROM send a JOIN (
select ‘13891030091’ phone union select ‘13992085916’ ………… UNION SELECT ‘13619100234’ ) b
on a.Phone=b.phone
–替代下面 很多数据隔开的时候
in(‘13891030091′,’13992085916’,’13619100234’…………)

7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num 可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num

8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2

9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’–name以abc开头的id
select id from t where datediff(day,createdate,’2005-11-30′)=0–’2005-11-30′生成的id
应改为:
select id from t where name like ‘abc%’
select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′ 10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。 11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。 12.不要写一些没有意义的查询,如需要生成一个空表结构: select col1,col2 into #t from t where 1=0 这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样: create table #t(…) 13.很多时候用 exists 代替 in 是一个好的选择: select num from a where num in(select num from b) 用下面的语句替换: select num from a where exists(select 1 from b where num=a.num) 14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。 15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。 16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。 17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。 18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。 19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。 20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。 21.避免频繁创建和删除临时表,以减少系统表资源的消耗。 22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使 用导出表。 23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。 24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。 25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。 26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。 27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。 28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。 29.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。 30.尽量避免大事务操作,提高系统并发能力。

[转]Yii PHP 框架分析

原文链接:http://bbs.phpchina.com/thread-194123-1-1.html
1. 启动

网站的唯一入口程序 index.php :

$yii=dirname(__FILE__).’/../framework/yii.php’;
$config=dirname(__FILE__).’/protected/config/main.php’;

// remove the following line when in production mode
defined(‘YII_DEBUG’) or define(‘YII_DEBUG’,true);

require_once($yii);
Yii::createWebApplication($config)->run();
复制代码

上面的require_once($yii) 引用出了后面要用到的全局类Yii,Yii类是YiiBase类的完全继承:

class Yii extends YiiBase
{
}
复制代码

系统的全局访问都是通过Yii类(即YiiBase类)来实现的,Yii类的成员和方法都是static类型。

2. 类加载

Yii利用PHP5提供的spl库来完成类的自动加载。在YiiBase.php 文件结尾处

spl_autoload_register(array(‘YiiBase’,’autoload’));
复制代码

将YiiBase类的静态方法autoload 注册为类加载器。 PHP autoload 的简单原理就是执行 new 创建对象或通过类名访问静态成员时,系统将类名传递给被注册的类加载器函数,类加载器函数根据类名自行找到对应的类文件并include 。

下面是YiiBase类的autoload方法:

public static function autoload($className)
{
// use include so that the error PHP file may appear
if(isset(self::$_coreClasses[$className]))
include(YII_PATH.self::$_coreClasses[$className]);
else if(isset(self::$_classes[$className]))
include(self::$_classes[$className]);
else
include($className.’.php’);
}
复制代码

可以看到YiiBase的静态成员$_coreClasses 数组里预先存放着Yii系统自身用到的类对应的文件路径:

private static $_coreClasses=array(
‘CApplication’ => ‘/base/CApplication.php’,
‘CBehavior’ => ‘/base/CBehavior.php’,
‘CComponent’ => ‘/base/CComponent.php’,

)
复制代码

非 coreClasse 的类注册在YiiBase的$_classes 数组中:
private static $_classes=array();

其他的类需要用Yii::import()讲类路径导入PHP include paths 中,直接
include($className.’.php’)

3. CWebApplication的创建

回到前面的程序入口的 Yii::createWebApplication($config)->run();

public static function createWebApplication($config=null)
{
return new CWebApplication($config);
}
复制代码

现在autoload机制开始工作了。
当系统 执行 new CWebApplication() 的时候,会自动
include(YII_PATH.’/base/CApplication.php’)

将main.php里的配置信息数组$config传递给CWebApplication创建出对象,并执行对象的run() 方法启动框架。

CWebApplication类的继承关系

CWebApplication -> CApplication -> CModule -> CComponent

$config先被传递给CApplication的构造函数

public function __construct($config=null)
{
Yii::setApplication($this);

// set basePath at early as possible to avoid trouble
if(is_string($config))
$config=require($config);
if(isset($config[‘basePath’]))
{
$this->setBasePath($config[‘basePath’]);
unset($config[‘basePath’]);
}
else
$this->setBasePath(‘protected’);
Yii::setPathOfAlias(‘application’,$this->getBasePath());
Yii::setPathOfAlias(‘webroot’,dirname($_SERVER[‘SCRIPT_FILENAME’]));

$this->preinit();

$this->initSystemHandlers();
$this->registerCoreComponents();

$this->configure($config);
$this->attachBehaviors($this->behaviors);
$this->preloadComponents();

$this->init();
}
复制代码

Yii::setApplication($this); 将自身的实例对象赋给Yii的静态成员$_app,以后可以通过 Yii::app() 来取得。
后面一段是设置CApplication 对象的_basePath ,指向 proteced 目录。

Yii::setPathOfAlias(‘application’,$this->getBasePath());
Yii::setPathOfAlias(‘webroot’,dirname($_SERVER[‘SCRIPT_FILENAME’]));
复制代码

设置了两个系统路径别名 application 和 webroot,后面再import的时候可以用别名来代替实际的完整路径。别名配置存放在YiiBase的 $_aliases 数组中。

$this->preinit();
预初始化。preinit()是在 CModule 类里定义的,没有任何动作。

$this->initSystemHandlers() 方法内容:

/**
* Initializes the class autoloader and error handlers.
*/
protected function initSystemHandlers()
{
if(YII_ENABLE_EXCEPTION_HANDLER)
set_exception_handler(array($this,’handleException’));
if(YII_ENABLE_ERROR_HANDLER)
set_error_handler(array($this,’handleError’),error_reporting());
}
复制代码

设置系统exception_handler和 error_handler,指向对象自身提供的两个方法。

4. 注册核心组件

$this->registerCoreComponents();
代码如下:

protected function registerCoreComponents()
{
parent::registerCoreComponents();

$components=array(
‘urlManager’=>array(
‘class’=>’CUrlManager’,
),
‘request’=>array(
‘class’=>’CHttpRequest’,
),
‘session’=>array(
‘class’=>’CHttpSession’,
),
‘assetManager’=>array(
‘class’=>’CAssetManager’,
),
‘user’=>array(
‘class’=>’CWebUser’,
),
‘themeManager’=>array(
‘class’=>’CThemeManager’,
),
‘authManager’=>array(
‘class’=>’CPhpAuthManager’,
),
‘clientScript’=>array(
‘class’=>’CClientScript’,
),
);

$this->setComponents($components);
}
复制代码

注册了几个系统组件(Components)。
Components 是在 CModule 里定义和管理的,主要包括两个数组

private $_components=array();
private $_componentConfig=array();
复制代码

每个 Component 都是 IApplicationComponent接口的实例,Componemt的实例存放在$_components 数组里,相关的配置信息存放在$_componentConfig数组里。配置信息包括Component 的类名和属性设置。

CWebApplication 对象注册了以下几个Component:urlManager, request,session,assetManager,user,themeManager,authManager,clientScript。CWebApplication的parent 注册了以下几个Component:coreMessages,db,messages,errorHandler,securityManager,statePersister。

Component 在YiiPHP里是个非常重要的东西,它的特征是可以通过 CModule 的 __get() 和 __set() 方法来访问。 Component 注册的时候并不会创建对象实例,而是在程序里被第一次访问到的时候,由CModule 来负责(实际上就是 Yii::app())创建。

5. 处理 $config 配置

继续, $this->configure($config);
configure() 还是在CModule 里:

public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value)
$this->$key=$value;
}
}
复制代码

实际上是把$config数组里的每一项传给 CModule 的 父类 CComponent __set() 方法。

public function __set($name,$value)
{
$setter=’set’.$name;
if(method_exists($this,$setter))
$this->$setter($value);
else if(strncasecmp($name,’on’,2)===0
&& method_exists($this,$name))
{
//duplicating getEventHandlers() here for performance
$name=strtolower($name);
if(!isset($this->_e[$name]))
$this->_e[$name]=new CList;
$this->_e[$name]->add($value);
}
else if(method_exists($this,’get’.$name))
throw new CException(Yii::t(‘yii’,’Property “{class}.{property}” is read only.’,
array(‘{class}’=>get_class($this), ‘{property}’=>$name)));
else
throw new CException(Yii::t(‘yii’,’Property “{class}.{property}” is not defined.’,
array(‘{class}’=>get_class($this), ‘{property}’=>$name)));
}
}
复制代码

我们来看看:
if(method_exists($this,$setter))
根据这个条件,$config 数组里的basePath, params, modules, import, components 都被传递给相应的 setBasePath(), setParams() 等方法里进行处理。

6、$config 之 import

其中 import 被传递给 CModule 的 setImport:

public function setImport($aliases)
{
foreach($aliases as $alias)
Yii::import($alias);
}
复制代码

Yii::import($alias)里的处理:

public static function import($alias,$forceInclude=false)
{
// 先判断$alias是否存在于YiiBase::$_imports[] 中,已存在的直接return, 避免重复import。
if(isset(self::$_imports[$alias])) // previously imported
return self::$_imports[$alias];

// $alias类已定义,记入$_imports[],直接返回
if(class_exists($alias,false))
return self::$_imports[$alias]=$alias;

// 类似 urlManager 这样的已定义于$_coreClasses[]的类,或不含.的直接类名,记入$_imports[],直接返回
if(isset(self::$_coreClasses[$alias]) || ($pos=strrpos($alias,’.’))===false) // a simple class name
{
self::$_imports[$alias]=$alias;
if($forceInclude)
{
if(isset(self::$_coreClasses[$alias])) // a core class
require(YII_PATH.self::$_coreClasses[$alias]);
else
require($alias.’.php’);
}
return $alias;
}

// 产生一个变量 $className,为$alias最后一个.后面的部分
// 这样的:’x.y.ClassNamer’
// $className不等于 ‘*’, 并且ClassNamer类已定义的, ClassNamer’ 记入 $_imports[],直接返回
if(($className=(string)substr($alias,$pos+1))!==’*’ && class_exists($className,false))
return self::$_imports[$alias]=$className;

// 取得 $alias 里真实的路径部分并且路径有效
if(($path=self::getPathOfAlias($alias))!==false)
{
// $className!==’*’,$className 记入 $_imports[]
if($className!==’*’)
{
self::$_imports[$alias]=$className;
if($forceInclude)
require($path.’.php’);
else
self::$_classes[$className]=$path.’.php’;
return $className;
}
// $alias是’system.web.*’这样的已*结尾的路径,将路径加到include_path中
else // a directory
{
set_include_path(get_include_path().PATH_SEPARATOR.$path);
return self::$_imports[$alias]=$path;
}
}
else
throw new CException(Yii::t(‘yii’,’Alias “{alias}” is invalid. Make sure it points to an existing directory or file.’,
array(‘{alias}’=>$alias)));
}
复制代码

7. $config 之 components

$config 数组里的 $components 被传递给CModule 的setComponents($components)

public function setComponents($components)
{
foreach($components as $id=>$component)
{
if($component instanceof IApplicationComponent)
$this->setComponent($id,$component);
else if(isset($this->_componentConfig[$id]))
$this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);
else
$this->_componentConfig[$id]=$component;
}
}
复制代码

$componen是IApplicationComponen的实例的时候,直接赋值:
$this->setComponent($id,$component),

public function setComponent($id,$component)
{
$this->_components[$id]=$component;
if(!$component->getIsInitialized())
$component->init();
}
复制代码

如果$id已存在于_componentConfig[]中(前面注册的coreComponent),将$component 属性加进入。
其他的component将component属性存入_componentConfig[]中。

8. $config 之 params

这个很简单

public function setParams($value)
{
$params=$this->getParams();
foreach($value as $k=>$v)
$params->add($k,$v);
}
复制代码

configure 完毕!

9. attachBehaviors

$this->attachBehaviors($this->behaviors);
空的,没动作

预创建组件对象

$this->preloadComponents();

protected function preloadComponents()
{
foreach($this->preload as $id)
$this->getComponent($id);
}
复制代码

getComponent() 判断_components[] 数组里是否有 $id的实例,如果没有,就根据_componentConfig[$id]里的配置来创建组件对象,调用组件的init()方法,然后存入_components[$id]中。

10. init()

$this->init();

函数内:$this->getRequest();
创建了Reques 组件并初始化。

11. run()

public function run()
{
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
$this->onEndRequest(new CEvent($this));
}

[转]Linux netstat命令详解


原文链接:http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316661.html
简介
Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Memberships) 等等。

输出信息含义
执行netstat后,其输出结果为

复制代码
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 2 210.34.6.89:telnet 210.34.6.96:2873 ESTABLISHED
tcp 296 0 210.34.6.89:1165 210.34.6.84:netbios-ssn ESTABLISHED
tcp 0 0 localhost.localdom:9001 localhost.localdom:1162 ESTABLISHED
tcp 0 0 localhost.localdom:1162 localhost.localdom:9001 ESTABLISHED
tcp 0 80 210.34.6.89:1161 210.34.6.10:netbios-ssn CLOSE

Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
unix 1 [ ] STREAM CONNECTED 16178 @000000dd
unix 1 [ ] STREAM CONNECTED 16176 @000000dc
unix 9 [ ] DGRAM 5292 /dev/log
unix 1 [ ] STREAM CONNECTED 16182 @000000df
复制代码

从整体上看,netstat的输出结果可以分为两个部分:

一个是Active Internet connections,称为有源TCP连接,其中”Recv-Q”和”Send-Q”指%0A的是接收队列和发送队列。这些数字一般都应该是0。如果不是则表示软件包正在队列中堆积。这种情况只能在非常少的情况见到。

另一个是Active UNIX domain sockets,称为有源Unix域套接口(和网络套接字一样,但是只能用于本机通信,性能可以提高一倍)。
Proto显示连接使用的协议,RefCnt表示连接到本套接口上的进程号,Types显示套接口的类型,State显示套接口当前的状态,Path表示连接到套接口的其它进程使用的路径名。

常见参数
-a (all)显示所有选项,默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化成数字。
-l 仅列出有在 Listen (监听) 的服務状态

-p 显示建立相关链接的程序名
-r 显示路由信息,路由表
-e 显示扩展信息,例如uid等
-s 按各个协议进行统计
-c 每隔一个固定时间,执行该netstat命令。

提示:LISTEN和LISTENING的状态只有用-a或者-l才能看到

实用命令实例

1. 列出所有端口 (包括监听和未监听的)
列出所有端口 netstat -a

复制代码
# netstat -a | more
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:30037 *:* LISTEN
udp 0 0 *:bootpc *:*

Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 6135 /tmp/.X11-unix/X0
unix 2 [ ACC ] STREAM LISTENING 5140 /var/run/acpid.socket
复制代码
列出所有 tcp 端口 netstat -at

复制代码
# netstat -at
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:30037 *:* LISTEN
tcp 0 0 localhost:ipp *:* LISTEN
tcp 0 0 *:smtp *:* LISTEN
tcp6 0 0 localhost:ipp [::]:* LISTEN
复制代码
列出所有 udp 端口 netstat -au

# netstat -au
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 0 0 *:bootpc *:*
udp 0 0 *:49119 *:*
udp 0 0 *:mdns *:*

2. 列出所有处于监听状态的 Sockets
只显示监听端口 netstat -l

# netstat -l
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:ipp *:* LISTEN
tcp6 0 0 localhost:ipp [::]:* LISTEN
udp 0 0 *:49119 *:*
只列出所有监听 tcp 端口 netstat -lt

# netstat -lt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:30037 *:* LISTEN
tcp 0 0 *:smtp *:* LISTEN
tcp6 0 0 localhost:ipp [::]:* LISTEN
只列出所有监听 udp 端口 netstat -lu

# netstat -lu
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 0 0 *:49119 *:*
udp 0 0 *:mdns *:*
只列出所有监听 UNIX 端口 netstat -lx

复制代码
# netstat -lx
Active UNIX domain sockets (only servers)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 6294 private/maildrop
unix 2 [ ACC ] STREAM LISTENING 6203 public/cleanup
unix 2 [ ACC ] STREAM LISTENING 6302 private/ifmail
unix 2 [ ACC ] STREAM LISTENING 6306 private/bsmtp
复制代码

3. 显示每个协议的统计信息
显示所有端口的统计信息 netstat -s

复制代码
# netstat -s
Ip:
11150 total packets received
1 with invalid addresses
0 forwarded
0 incoming packets discarded
11149 incoming packets delivered
11635 requests sent out
Icmp:
0 ICMP messages received
0 input ICMP message failed.
Tcp:
582 active connections openings
2 failed connection attempts
25 connection resets received
Udp:
1183 packets received
4 packets to unknown port received.
…..
复制代码
显示 TCP 或 UDP 端口的统计信息 netstat -st 或 -su

# netstat -st
# netstat -su

4. 在 netstat 输出中显示 PID 和进程名称 netstat -p
netstat -p 可以与其它开关一起使用,就可以添加 “PID/进程名称” 到 netstat 输出中,这样 debugging 的时候可以很方便的发现特定端口运行的程序。

# netstat -pt
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 1 0 ramesh-laptop.loc:47212 192.168.185.75:www CLOSE_WAIT 2109/firefox
tcp 0 0 ramesh-laptop.loc:52750 lax:www ESTABLISHED 2109/firefox
5. 在 netstat 输出中不显示主机,端口和用户名 (host, port or user)
当你不想让主机,端口和用户名显示,使用 netstat -n。将会使用数字代替那些名称。

同样可以加速输出,因为不用进行比对查询。

# netstat -an
如果只是不想让这三个名称中的一个被显示,使用以下命令

# netsat -a –numeric-ports
# netsat -a –numeric-hosts
# netsat -a –numeric-users

6. 持续输出 netstat 信息
netstat 将每隔一秒输出网络信息。

复制代码
# netstat -c
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 ramesh-laptop.loc:36130 101-101-181-225.ama:www ESTABLISHED
tcp 1 1 ramesh-laptop.loc:52564 101.11.169.230:www CLOSING
tcp 0 0 ramesh-laptop.loc:43758 server-101-101-43-2:www ESTABLISHED
tcp 1 1 ramesh-laptop.loc:42367 101.101.34.101:www CLOSING
^C
复制代码

7. 显示系统不支持的地址族 (Address Families)
netstat –verbose
在输出的末尾,会有如下的信息

netstat: no support for `AF IPX’ on this system.
netstat: no support for `AF AX25′ on this system.
netstat: no support for `AF X25′ on this system.
netstat: no support for `AF NETROM’ on this system.

8. 显示核心路由信息 netstat -r
# netstat -r
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.1.0 * 255.255.255.0 U 0 0 0 eth2
link-local * 255.255.0.0 U 0 0 0 eth2
default 192.168.1.1 0.0.0.0 UG 0 0 0 eth2
注意: 使用 netstat -rn 显示数字格式,不查询主机名称。

9. 找出程序运行的端口
并不是所有的进程都能找到,没有权限的会不显示,使用 root 权限查看所有的信息。

# netstat -ap | grep ssh
tcp 1 0 dev-db:ssh 101.174.100.22:39213 CLOSE_WAIT –
tcp 1 0 dev-db:ssh 101.174.100.22:57643 CLOSE_WAIT –
找出运行在指定端口的进程

# netstat -an | grep ‘:80’

10. 显示网络接口列表
# netstat -i
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 0 0 0 0 0 0 0 0 0 BMU
eth2 1500 0 26196 0 0 0 26883 6 0 0 BMRU
lo 16436 0 4 0 0 0 4 0 0 0 LRU
显示详细信息,像是 ifconfig 使用 netstat -ie:

复制代码
# netstat -ie
Kernel Interface table
eth0 Link encap:Ethernet HWaddr 00:10:40:11:11:11
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Memory:f6ae0000-f6b00000
复制代码

11. IP和TCP分析
查看连接某服务端口最多的的IP地址

复制代码
wss8848@ubuntu:~$ netstat -nat | grep “192.168.1.15:22” |awk ‘{print $5}’|awk -F: ‘{print $1}’|sort|uniq -c|sort -nr|head -20
18 221.136.168.36
3 154.74.45.242
2 78.173.31.236
2 62.183.207.98
2 192.168.1.14
2 182.48.111.215
2 124.193.219.34
2 119.145.41.2
2 114.255.41.30
1 75.102.11.99
复制代码
TCP各种状态列表

复制代码
wss8848@ubuntu:~$ netstat -nat |awk ‘{print $6}’
established)
Foreign
LISTEN
TIME_WAIT
ESTABLISHED
TIME_WAIT
SYN_SENT
复制代码
先把状态全都取出来,然后使用uniq -c统计,之后再进行排序。
复制代码
wss8848@ubuntu:~$ netstat -nat |awk ‘{print $6}’|sort|uniq -c
143 ESTABLISHED
1 FIN_WAIT1
1 Foreign
1 LAST_ACK
36 LISTEN
6 SYN_SENT
113 TIME_WAIT
1 established)
复制代码
最后的命令如下:
netstat -nat |awk ‘{print $6}’|sort|uniq -c|sort -rn
分析access.log获得访问前10位的ip地址
awk ‘{print $1}’ access.log |sort|uniq -c|sort -nr|head -10

[Laravel5.1]学习笔记(二)–建立第一个Laravel页面

之前按照laravel 5.1搭建了开发环境,现在开始建立第一个页面。
(一)创建controller
在/app/Http/Controllers下建立第一个Controller,这里我命名为HomeController.php,内容如下:

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class HomeController extends Controller
{

    public function getIndex() {
    
        echo &quot;hello laravel&quot;;
    }   

    public function getTest() {
        return view('test', ['name' =&gt; 'MrCat']);
    }   

}

(二)配置路由
在/app/Http下修改routes.php文件,按照laravel的开发文档,routes支持支持的功能非常强大,但是我还是喜欢MVC的模式,将逻辑放在Controller层

Route::controller('home', 'HomeController');

(三)添加view
view放在/resources/views下,默认采用的是blade模板,熟悉smarty的同学肯定比较容易上手,这里就解析是@yield和@section:
模板用 @yield 和 @section 分别定义了一个区块,然后在子模板中去定义内容,由于 @yield 不能被扩展,所以即使加上了 @parent 也不起作用,输出的内容只有“新模板的内容”。而 @section 定义的部分,由于使用了 @parent 关键字,父模板中的内容会被保留,然后再扩展后添加的内容进去,输出的内容会是 “默认的内容 扩展的内容”。实例如下:
首先建立main.blade.php


&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;My Test Meau&lt;/h1&gt;
    @yield('title')

    @section('content')
    &lt;p&gt;this is section&lt;/p&gt;
    @show

&lt;/body&gt;
&lt;/html&gt;

之后建立test.blade.php, 继承main

@extends('main')

@section('title')
&lt;p&gt;this is new title&lt;/p&gt;
@stop

@section('content')
@parent
&lt;p&gt;this is new content&lt;/p&gt;
&lt;p&gt;this is my name {{$name}}&lt;/p&gt;
@stop

之后访问http://localhost/home/test/就可以出现预定于页面的内容了: