2013年4月27日星期六

RIP路由协议及工作原理


下面内容摘自笔者编著的网管员必读——网络基础》(第2版)一书。

9.11.1 RIP路由协议及工作原理

RIPRouting information Protocol路由信息协议)是应用较早、使用较普遍的内部网关协议(Interior Gateway ProtocolIGP),适用于小型同类网络的一个自治系统(AS)内的路由信息的传递。RIP协议是基于距离矢量算法(Distance Vector AlgorithmsDVA)的。它使用跳数,即metric来衡量到达目标地址的路由距离。文档见RFC1058RFC1723。它是一个用于路由器和主机间交换路由信息的距离向量协议,目前最新的版本为v4,也就是RIPv4
至于上面所说到的内部网关协议,我们可以这样理解。由于历史的原因,当前的 INTERNET 网被组成一系列的自治系统,各自治系统通过一个核心路由器连到主干网上。而一个自治系统往往对应一个组织实体(比如一个公司或大学)内部的网络与路由器集合。每个自治系统都有自己的路由技术,对不同的自治系统路由技术是不相同的。用于自治系统间接口上的路由协议称为外部网关协议,简称EGPExterior Gateway Protocol);而用于自治系统内部的路由协议称为内部网关协议,简称 IGP。内部网关与外部网关协议不同,外部路由协议只有一个,而内部路由器协议则是一族。各内部路由器协议的区别在于距离制式(distance metric, 即距离度量标准)不同,和路由刷新算法不同。RIP协议是最广泛使用的IGP类协议之一,著名的路径刷新程序Routed便是根据RIP实现的。RIP协议被设计用于使用同种技术的中型网络,因此适应于大多数的校园网和使用速率变化不是很大的连续线的地区性网络。对于更复杂的环境,一般不使用RIP协议。

1RIP工作原理

RIP协议是基于Bellham-Ford(距离向量)算法,此算法1969年被用于计算机路由选择,正式协议首先是由Xerox1970年开发的,当时是作为Xerox“Networking ServicesNXS协议族的一部分。由于RIP实现简单,迅速成为使用范围最广泛的路由协议。
路由器的关键作用是用于网络的互连,每个路由器与两个以上的实际网络相连,负责在这些网络之间转发数据报。在讨论 IP 进行选路和对报文进行转发时,我们总是假设路由器包含了正确的路由,而且路由器可以利用 ICMP 重定向机制来要求与之相连的主机更改路由。但在实际情况下,IP 进行选路之前必须先通过某种方法获取正确的路由表。在小型的、变化缓慢的互连网络中,管理者可以用手工方式来建立和更改路由表。而在大型的、迅速变化的环境下,人工更新的办法慢得不能接受。这就需要自动更新路由表的方法,即所谓的动态路由协议,RIP协议是其中最简单的一种。
在路由实现时,RIP作为一个系统长驻进程(daemon)而存在于路由器中,负责从网络系统的其它路由器接收路由信息,从而对本地IP层路由表作动态的维护,保证IP层发送报文时选择正确的路由。同时负责广播本路由器的路由信息,通知相邻路由器作相应的修改。RIP协议处于UDP协议的上层,RIP所接收的路由信息都封装在UDP协议的数据报中,RIP520UDP端口上接收来自远程路由器的路由修改信息,并对本地的路由表做相应的修改,同时通知其它路由器。通过这种方式,达到全局路由的有效。
RIP路由协议用更新(UNPDATES请求(REQUESTS这两种分组来传输信息的。每个具有RIP协议功能的路由器每隔30秒用UDP520端口给与之直接相连的机器广播更新信息。更新信息反映了该路由器所有的路由选择信息数据库。路由选择信息数据库的每个条目由局域网上能达到的IP地址与该网络的距离两部分组成。请求信息用于寻找网络上能发出RIP报文的其他设备。
RIP路程段数(即跳数)作为网络距离的尺度。每个路由器在给相邻路由器发出路由信息时,都会给每个路径加上内部距离。在如图9-31中,路由器3直接和网络C相连。当它向路由器2通告网络142.10.0.0的路径时,它把跳数增加1。与之相似,路由器2把跳数增加到2”,且通告路径给路由器1,则路由器2路由器1与路由器3所在网络142.10.0.0的距离分别是1跳、2跳。
9-31  RIP工作原理示例
然而在实际的网络路由选择上并不总是由跳数决定的,还要结合实际的路径连接性能综合考虑。在如9-32所示网络中,从路由器1到网络3RIP协议将更倾向于跳数为2的路由器1->路由器2->路由器31.5Mbps链路,而不是选择跳数为156Kbps,直接的路由器1->路由器3路径,因为跳数为156Kbps串行链路比跳数为21.5Mbps串行链路慢得多。
9-32   路由选择不仅限于跳数考虑的示例

2路由器的收敛机制

任何距离向量路由选择协议(如RIP)都有一个问题,路由器不知道网络的全局情况,路由器必须依靠相邻路由器来获取网络的可达信息。由于路由选择更新信息在网络上传播慢,距离向量路由选择算法有一个慢收敛问题,这个问题将导致不一致性产生。RIP协议使用以下机制减少因网络上的不一致带来的路由选择环路的可能性。
l              记数到无穷大机制
RIP协议允许最大跳数为15。大于15的目的地被认为是不可达。这个数字在限制了网络大小的同时也防止了一个叫做记数到无穷大的问题。
记数到无穷大机制的工作原理如下(如图9-33所示):
9-33  路由器收敛机制示例
1)现假设路由器1断开了与网络A相连,则路由器1丢失了与网络A相连的以太网接口后产生一个触发更新送往路由器2和路由器3。这个更新信息同时告诉路由器2和路由器3,路由器1不再有到达网络A的路径。假设这个更新信息传输到路由器2被推迟了(CPU忙、链路拥塞等),但到达了路由器3,所以路由器3会立即从路由表中去掉到网络A的路径。
2)路由器2由于未收到路由器1的触发更新信息,并发出它的常规路由选择更新信息,通告网络A2跳的距离可达。路由器3收到这个更新信息,认为出现了一条通过路由器2的到达网络A的新路径。于是路由器3告诉路由器1,它能以3跳的距离到达网络A
3)在收到路由器3的更新新后,就把这个信息加上一跳后向路由器2和路由器3同时发出更新信息,告诉他们路由器1可以以3跳的距离到达网络A
4)路由器2在收到路由器1的消息后,比较发现与原来到达网络A的路径不符,更新成可以以4,跳的距离到达网络A。这个消息再次会发往路由器3,以此循环,直到跳数达到超过RIP协议允许的最大值(在RIP中定义为16)。一旦一个路由器达到这个值,它将声明这条路径不可用,并从路由表中删除此路径。
由于记数到无穷大问题,路由选择信息将从一个路由器传到另一个路由器,每次段数加1。路由选择环路问题将无限制地进行下去,除非达到某个限制。这个限制就是RIP的最大跳数。当路径的跳数超过15,这条路径才从路由表中删除。
l              水平分割法
水平分割规则如下:路由器不向路径到来的方向回传此路径。当打开路由器接口后,路由器记录路径是从哪个接口来的,并且不向此接口回传此路径。
Cisco可以对每个接口关闭水平分割功能。这个特点在“non broadcast mutilple access”NBMA,非广播多路访问)环境下十分有用。在如图9-34所示网络中,路由器2通过帧中继连接路由器1和路由器3,两个PVC都在路由器2的同一个物理接口(S0)中止。如果在路由器2的水平分割功能未被关闭,那么路由器3将收不到路由器1的路由选择信息(反之亦然)。用“no ip split-horizon”接口子命令可关闭水平分割功能。
9-34  水平分割法原理示例
l              破坏逆转的水平分割法
水平分割是路由器用来防止把一个接口得来的路径又从此接口传回导致的问题的方案。水平分割方案忽略在更新过程中从一个路由器获取的路径又传回该路由器。有破坏逆转的水平分割方法是在更新信息中包括这些回传路径,但这种处理方法会把这些回传路径的跳数设为16(无穷)。通过把跳数设为无穷,并把这条路径告诉源路由器,有可能立刻解决路由选择环路。否则,不正确的路径将在路由表中驻留到超时为止。破坏逆转的缺点是它增加了路由更新的的数据大小。
l              保持定时器法
保持定时器法可防止路由器在路径从路由表中删除后一定的时间内(通常为180秒)接受新的路由信息。它的思想是保证每个路由器都收到了路径不可达信息,而且没有路由器发出无效路径信息。例如在图6-32所示网络中,由于路由更新信息被延迟,路由器2向路由器3发出错误信息。但使用保持计数器法后,这种情况将不会发生,因为路由器3将在180秒内不接受通向网络A的新的路径信息,到那时路由器2将存储正确的路由信息。
l              触发更新法
有破坏逆转的水平分割将任何两个路由器构成的环路打破,但三个或更多个路由器构成的环路仍会发生,直到无穷(16)时为止。触发式更新法可加速收敛时间,它的工作原理是当某个路径的跳数改变了,路由器立即发出更新信息,不管路由器是否到达常规信息更新时间都发出更新信息。

3RIP报文格式

如图9-35所示为RIP信息格式。各字段解释如下:
Command:命令字段,8位,用来指定数据报用途。命令有五种:Request(请求)、Response(响应)、Traceon(启用跟踪标记,自v2版本后已经淘汰)、Traceoff(关闭跟踪标记,自v2版本后已经淘汰)和 Reserved(保留)。
VersionRIP版本号字段,16位。
Address Family Identifier:地址族标识符字段,24位。它指出该入口的协议地址类型。由于 RIP2版本可能使用几种不同协议传送路由选择信息,所以要使用到该字段。IP协议地址的Address Family Identifier2
9-35  RIP协议信息格式
Route Tag:路由标记字段,32位,仅在v2版本以上需要,第一版本不用,为0。用于路由器指定属性,必须通过路由器保存和重新广告。路由标志是分离内部和外部 RIP 路由线路的一种常用方法(路由选择域内的网络传送线路),该方法在 EGPIGP都有应用。
IP Address:目标IP地址字段,IPv4地址为32位。
Subnet Mask:子网掩码字段,IPv4子网掩码地址为32位。它应用于IP地址,生成非主机地址部分。如果为0,说明该入口不包括子网掩码。也仅在v2版本以上需要,在RIPv1中不需要,为0
Next Hop:下一跳字段。指出下一跳IP地址,由路由入口指定的通向目的地的数据包需要转发到该地址。
Metric:跳数字段。表示从主机到目的地获得数据报过程中的整个成本。 

林彪提前培养儿子犯了毛泽东的大忌

林彪韬晦了几十年,但到了最后时刻却利令智昏。这个报告到了毛泽东那儿,包括军队空军对林立果的吹捧,毛产生了负面的看法。
林彪提前培养儿子犯了毛泽东的大忌 - 龙往事 - 龙往事
 九大是林彪由盛而衰的转折点
林彪的一些想法在九大上和毛泽东是有分歧的,林彪是比较主张“文革”结束国家开始搞现代化,恢复常规恢复秩序,毛泽东要不断革命。毛废弃了陈伯达给林彪写的稿子,那个稿子是林彪比较欣赏的,就是“文革”结束以后,应该搞经济建设。毛不看,用的是张春桥的稿子,张春桥的稿子就是继续革命不断革命。那么,林彪在九大作政治报告的时候,事先稿子是一个字都没有看的,他是很不高兴的。九大上,毛主席正式安排林彪为接班人,又来试探林彪了。九大时间开得很长,开了20多天。有一天开会选举大会主席团,毛主席说选举林彪同志作主席团主席,吓坏了林彪,好在林彪反应快,马上拉了话筒说选伟大领袖毛主席作主席,然后林彪说流眼泪就流眼泪,流着眼泪歌颂毛主席的丰功伟绩,在九大庄严的主席台上,两个人演出了这一场试探与反试探的一场戏。另外,毛主席在这次会议上,借用外电的话表达了自己的不安,主要借用苏联人的话,说“我们现在是军事官僚集团”。
毛主席在九大上安排林彪系统的人当了政治局委员,但又做了其他的布局。他任命了许世友、陈锡联担任政治局委员,他也让李先念担任政治局委员,几个老臣,刘伯承虽然双眼失明了也还是政治局委员,叶剑英也是政治局委员。所以,林彪并没有真正实现清一色,特别是大军区司令员这一级,林彪最希望能洗牌的这一级,毛泽东坚决不让林彪染指,大军区司令员这一级全是毛主席定的,林彪对广州军区只能有一个建议。
林彪推出林立果犯了大忌
毛岸英同志牺牲了,毛岸青的身体又不太好,1970年的7月份,林立果被隆重推出,给毛主席和江青造成巨大的心理上的一个反应,因为他们两位都没有儿子啊。怎么推出的?七月二十几日安排得很好,专门安排的,林彪视察军委的一个新工厂,特地安排儿子跟他一起走,录像在军中所有单位播放,这是第一个。第二个,七月下旬,林立果作学习毛主席著作的讲用报告,这个讲用报告长达七个小时,讲了一天,七万字,这个报告毛泽东当然看到了。林彪昏了头了,居然在家里欣赏得不得了,说这个报告不仅语言像我,思想像我,连讲话的声音都像。他韬晦了几十年,所以我们讲人有的时候会利令智昏的。这个报告到了毛泽东那儿,包括军队空军对林立果的吹捧,毛产生了负面的看法。本来毛泽东对林立果是有一点欣赏的,因为林立果是好动脑筋的人,动手能力很强,他是北大物理系的,喜欢搞一些军械等。特别是在1969年林立果突发奇想,将张家口以北的一个山劈掉一半,放一个雷达,说这个雷达放上去以后,莫斯科要对中国放战略导弹的话这个雷达可以提前知道。毛主席非常高兴,亲自接见林立果,称他是革命小将,敢想敢做。毛主席鼓励了,林彪本应该谦虚一点,但是他忘掉了,他以为毛泽东称赞他儿子了,他就要隆重推出。主要林受了刺激,毛远新这个时候在沈阳军区不断受提拔,现在他要赶紧提拔林立果。毛主席的反应是,林彪同志认为自己身体不好了,这么快这么着急地把他的儿子也推作接班人了,这是主席跟江青、康生等人的小范围的谈话。康生直接在主席面前说,林彪昏了头了,怎么能隆重推出自己儿子呢,特别是毛主席没有一个健康的儿子的时候,更不能推出。

京华时报:法国富豪捐赠兽首是场精明的买卖

  法国富豪捐赠兽首的时机很有意义。首先,此时正好是法国总统来华访问期间,捐赠兽首则给了中国一件大礼,有助于加强中法关系;其次,这也是一场精明的买卖。
  4月26日上午,国家文物局副局长宋新潮在北京会晤了法国PPR集团董事长兼首席执行官弗朗索瓦·亨利·皮诺。皮诺代表皮诺家族表示,将向中国政府捐赠流失海外的圆明园十二大水法中的青铜鼠首和兔首。
  皮诺先生称,将在9、10月份完成两件圆明园兽首的回归。而宋新潮副局长表示,中方希望能提前至7月份,这说明国家文物局对两件兽首归来的重视。
  据此,人们马上会想起2009年巴黎佳士得拍卖的这两件圆明园丢失的兽首。当年,佳士得拍卖行在法国巴黎大皇宫举办的拍卖会上,上拍了这两件兽首。在此之前,中国国家文物局以及国内民众一致要求巴黎佳士得撤拍两件兽首,但佳士得方面考虑整个专场是一个委托拍卖合同,虽然只撤拍两件,也是对整个专场拍卖协议的违约,法国方面过于强调遵守商业规则,没有顾及到中国人的情感。因此佳士得在与国家文物局多次交涉之后,最终没有撤拍。
  拍卖会上,拍卖师分别以1400万欧元将鼠首和兔首拍卖成交。中国商人蔡铭超竞买成功后马上宣布不付款,理由是:因为拍卖品是非法流失,故无法申报把兽首带入中国境内。这场一波三折的拍卖过程引起了国人的极大兴趣和广泛讨论,进而一度影响了佳士得在中国业务的发展。
  这次法国富豪捐赠兽首的时机很有意义。首先,此时正好是法国总统来华访问期间,捐赠兽首则给了中国一件大礼,有助于加强中法关系;其次,这也是一场精明的买卖。
  许多人也许不知道,亨利·皮诺除了是古驰、彪马等著名品牌的控制人以外,还在1998年成为了国际佳士得拍卖行的大股东,并保有至今。而佳士得公司刚刚在上海成立了独资拍卖公司,捐赠这两件礼物是明显向中国示好的动作。佳士得在步步争取获得中国的好感和支持,并有利于最终取得在国内的文物拍卖权!2009年以后,皮诺家族从兽首原持有人手中买下了这两件兽首,这次很好地抓住时机做成了觐见礼。

2013年4月26日星期五

PHP写文件函数file_put_contents确实给力


最近有个项目需要用file_put_contents函数写txt文件,由于需要频繁操作,所以经常出现前半截内容缺失的情况,非常苦恼。

后来查询资料发现,file_put_contents函数有个参数LOCK_EX非常有用,加上它之后,再也没有出现过内容缺失的情况了。

这个参数LOCK_EX的意思很直白,就是写文件时,先锁上这个文件,这样只允许某个客户端访问的时候写,其他客户端访问不能写了。

我的用法如下:
file_put_contents($file, $content, FILE_APPEND|LOCK_EX)
解释:
$file=>这个是写入文件的路径+文件名
$content=>这个是写入文件的内容
FILE_APPEND=>直接在该文件已有的内容后面追加内容
LOCK_EX=>写文件的时候先锁定,防止多人同时写入造成内容丢失

PHP操作ini配置文件



<?php
//写ini文件
function write_ini_file($assoc_arr, $path, $has_sections=FALSE)
{
    $content = "";
    if ($has_sections)
    {
        foreach ($assoc_arr as $key=>$elem)
        {
            $content .= "[".$key."]\n";
            foreach ($elem as $key2=>$elem2)
            {
                if(is_array($elem2))
                {
                    for($i=0;$i<count($elem2);$i++)
                    {
                        $content .= $key2."[] = \"".$elem2[$i]."\"\n";
                    }
                }
                else if($elem2=="") $content .= $key2." = \n";
                else $content .= $key2." = \"".$elem2."\"\n";
            }
        }
    }
    else
    {
        foreach ($assoc_arr as $key=>$elem)
        {
            if(is_array($elem))
            {
                for($i=0;$i<count($elem);$i++)
                {
                    $content .= $key2."[] = \"".$elem[$i]."\"\n";
                }
            }
            else if($elem=="") $content .= $key2." = \n";
            else $content .= $key2." = \"".$elem."\"\n";
        }
    }
    if (!$handle = fopen($path, 'w'))
    {
        return false;
    }
    if (!fwrite($handle, $content))
    {
        return false;
    }
    fclose($handle);
    return true;
}
 
//用法
//
$sampleData = array(
                'first' => array(
                    'first-1' => 1,
                    'first-2' => 2,
                    'first-3' => 3,
                    'first-4' => 4,
                    'first-5' => 5,
                ),
                'second' => array(
                    'second-1' => 1,
                    'second-2' => 2,
                    'second-3' => 3,
                    'second-4' => 4,
                    'second-5' => 5,
                ));
write_ini_file($sampleData, './data.ini', true);
 
//读ini文件
public function readini($name)
{
    if (file_exists(SEM_PATH.'init/'.$name))
    {
        $data = parse_ini_file(SEM_PATH.'init/'.$name,true);
        if ($data)
        {
        return $data;
        }
    }
    else
    {
        return false;
    }
}

PHP parse_ini_file() 函数


定义和用法
parse_ini_file() 函数解析一个配置文件,并以数组的形式返回其中的设置。
语法
parse_ini_file(file,process_sections)
参数 描述
file 必需。规定要检查的 ini 文件。
process_sections 可选。如果设置为 true,则返回一个多维数组,包括了配置文件中每一节的名称和设置。默认是 false。
说明
ini 文件的结构和 php.ini 的相似。
常量也可以在 ini 文件中被解析,因此如果在运行 parse_ini_file() 之前定义了常量作为 ini 的值,将会被集成到结果中去。只有 ini 的值会被求值。
由数字组成的键名和小节名会被 PHP 当作整数来处理,因此以 0 开头的数字会被当作八进制而以 0x 开头的会被当作十六进制。
提示和注释
注释:本函数可以用来读取你自己的应用程序的配置文件。本函数与 php.ini 文件没有关系,该文件在运行脚本时就已经处理过了。
注释:如果 ini 文件中的值包含任何非字母数字的字符,需要将其括在双引号中(")。
注释:有些保留字不能作为 ini 文件中的键名,包括:null,yes,no,true 和 false。值为 null,no 和 false 等效于 "",值为 yes 和 true 等效于 "1"。字符 {}|"~![()" 也不能用在键名的任何地方,而且这些字符在选项值中有着特殊的意义。
注释:自 PHP 5.0 版本开始,该函数也处理选项值内的新行。
例子
例子 1
"test.ini" 的内容:
[names]
me = Robert
you = Peter

[urls]
first = "http://www.example.com"
second = "http://www.w3school.com.cn"
PHP 代码:
<?php
print_r(parse_ini_file("test.ini"));
?>
输出:
Array
(
[me] => Robert
[you] => Peter
[first] => http://www.example.com
[second] => http://www.w3school.com.cn
)
例子 2
"test.ini" 的内容:
[names]
me = Robert
you = Peter

[urls]
first = "http://www.example.com"
second = "http://www.w3school.com.cn"
PHP 代码(process_sections 设置为 true):
<?php
print_r(parse_ini_file("test.ini",true));
?>
输出:
Array
(
[names] => Array
  (
  [me] => Robert
  [you] => Peter
  )
[urls] => Array
  (
  [first] => http://www.example.com
  [second] => http://www.w3school.com.cn
  )
)

2013年4月25日星期四

[教程] PHPCMS教程:PHP错误日志


错误日志的记录,可以帮助开发人员或者治理人员查看系统是否存在问题。在调试的时候可开启此功能。

建议不开启,影响速度。

一、开启错误日志

修改修改文件 根目录下面的 config.inc.php 文件,

找到 $CONFIG['enablephplog'] = '0'; //是否启用php错误日志

修改为:

$CONFIG['enablephplog'] = '1'; //是否启用php错误日志

二、查看错误日志

系统设置--系统工具 --php 错误日志

级别为:Notice 错误 为正常。

级别为: E_ERROR | E_WARNING | E_PARSE 中的任何一个,表示存在问题。



phpcms v9错误日志记录怎样清理?????????

直接删除error_log.php文件就可以了

MySQLi for Beginners

Introduction

Nearly every site that you visit nowadays has some sort of database storage in place, many sites opt to use MySQL databases when using PHP. However, many people haven't yet taken the step to interacting with databases properly in PHP. Here we guide you through what you should be doing - using PHP's MySQLi class - with a hat-tip to the one way that you definitely shouldn't be doing it.

The Wrong Way

If you're using a function called mysql_connect() or mysql_query() you really need to take note and change what you're doing. I understand that it's not easy to change current large projects, but look to change future ones.
Any of the functions that are prefixed with mysql_ are now being discouraged by PHP themselves as visible on this doc page, instead you should look to use one of the following:
  • MySQLi - The i standing for 'improved'.
  • PDO
Each has its advantages, PDO for example will work with various different database systems, where as MySQLi will only work with MySQL databases. Both are object oriented, but MySQLi allows procedural usage as well. There are other minor differences between the two, but it's up to you to choose which you want to use, here we'll be looking at MySQLi.

PHP MySQLi

Here we'll mostly be looking at the object oriented implementation, however, there is no reason you can't use this in a procedural format, but again no reason you shouldn't use the OO implementation.

Connecting

Connecting is as simple as just instantiating a new instance of MySQLi, we'll be using a username of user with a password of pass connecting to the demo database on the localhosthost:
$db = new mysqli('localhost', 'user', 'pass', 'demo');

if($db->connect_errno > 0){
    die('Unable to connect to database [' . $db->connect_error . ']');
}
Obviously, the database name is optional and can be omitted. If you omit the database name you must be sure to prefix your tables with the database in all of your queries.

Querying

Let's go ahead and pull out all of the users from the users table where they have live = 1:
$sql = <<<SQL
    SELECT *
    FROM `users`
    WHERE `live` = 1 
SQL;

if(!$result = $db->query($sql)){
    die('There was an error running the query [' . $db->error . ']');
}
We now have a variable that contains a mysqli_result object, we can now go ahead and do various things with this such as looping through the results, displaying how many there are and freeing the result.

Output query results

To loop through the results and output the username for each row on a new line we'd do the following:
while($row = $result->fetch_assoc()){
    echo $row['username'] . '<br />';
}
As you can see from this, the syntax isn't too dissimilar to the old mysql_ syntax that you're probably used to, this is just better and improved!

Number of returned rows

Each mysqli_result object that is returned has a variable defined which is called $num_rows, so all we need to do is access that variable by doing:
<?php
echo 'Total results: ' . $result->num_rows;
?>

Number of affected rows

When running an UPDATE query you sometimes want to know how many rows have been updated, or deleted if running a DELETE query, this is a variable which is inside the mysqliobject.
<?php
echo 'Total rows updated: ' . $db->affected_rows;
?>

Free result

It's advisable to free a result when you've finished playing with the result set, so in the above example we should put the following code after our while() loop:
$result->free();
This will free up some system resources, and is a good practice to get in the habit of doing.

Escaping characters

When inserting data into a database, you'll have been told (I hope) to escape it first, so that single quotes get preceeded be a backslash. This will mean that any quotes won't break out of any that you use in your SQL. This is still the case - and you should look to use the below method:
$db->real_escape_string('This is an unescaped "string"');
However, because this is a commonly used function, there is an alias function that you can use which is shorter and less to type:
$db->escape_string('This is an unescape "string"');
This string should now be safer to insert into your database through a query.

Close that connection

Don't forget, when you've finished playing with your database to make sure that you close the connection:
$db->close();

Prepared Statements

Prepared statements are complex to get your head around, but are really useful and can help alleviate a lot of the potential issues that you might have with escaping. Prepared statements basically work by you playing a ? where you want to substitute in a string,integerblob or double. Prepared statements don't substitute the value into the SQL so the issues with SQL injections are mostly removed.

Define a statement

Let's try to grab all of the users from the users table where they have a username of bob. We'd firstly define the SQL statement that we'd use:
$statment = $db->prepare("SELECT `name` FROM `users` WHERE `username` = ?");
That question mark there is what we're going to be assigning the word 'bob' to.

Bind parameters

We simply use the method bind_param to bind a parameter. You must specify the type as the first parameter then the variable as the second - so for instance we'd use s as the first parameter (for string), and our $name variable as the second:
$name = 'Bob';
$statement->bind_param('s', $name);
If we had 3 parameters to bind which are of varying types we could use bind_param('sdi', $name, $height, $age); for example. Note the types are not separated at all as the first parameter.

Execute the statement

No fuss, no mess, just execute the statement so that we can play with the result:
$statement->execute();

Iterating over results

Firstly we'll bind the result to variables, we do this using the bind_result() method which allow us specify some variables to assign the result to. So if we assign the returned name to the variable $returned_name we'd use:
$statement->bind_result($returned_name);
As before, if you have multiple variables to assign, just comma separate them - simple as that.
Now we have to actually fetch the results, this is just as simple as the earlier mysqli requests that we were doing - we'd use the method fetch(), which returns will assign the returned values into the binded variables - if we'd binded some.
while($statement->fetch()){
    echo $returned_name . '<br />';
}

Close statement

Don't forget to forgo a few seconds of your time to free the result - keep your code neat, clean and lean:
$statement->free_result();

MySQLi Transactions

One of the major improvements that MySQLi brings is the ability to use transactions. A transaction is a group of queries that execute but don't save their effects in the database. The advantage of this is if you have 4 inserts that all rely on each other, and one fails, you can roll back the others so that none of the data is inserted, or if updating fields relies on fields being inserted correctly.
You need to ensure that the database engine that you're using supports transactions.

Disable auto commit

Firstly you need to make it so that any query you submit doesn't automatically commit in the database. It's a simple one line boolean value:
$db->autocommit(FALSE);

Commit the queries

After a few queries that you've ran using $db->query() we can call a simple function to commit the transaction:
$db->commit();
Pretty simple stuff so far, and it's meant to be easy and approachable so that you have no reason to not use it.

Rollback

Just as easy as it is to commit something, it's just as simple to roll something back:
$db->rollback();
Take a look at the PHP documentation for an example of how to use rollbacks. I personally haven't found a scenario where I would use them, but they're worth knowing about so that you are aware they're there to be used.

Final Thoughts

Using mysql_ functions is a foolish move to make, don't use these outdated and useless methods because they're easier, or quicker. Man up and tackle one of the new forms of database interaction - MySQLi or PDO - you'll make @mfrost503 happier, and have better code too.

PHP and MySQLi quick tutorial / how to example



Here is a quick tutorial to get  you up in running with mysqli

<?php


// CONNECT TO THE DATABASE
$DB_NAME = 'DATABASE_NAME';
$DB_HOST = 'DATABASE_HOST';
$DB_USER = 'DATABASE_USER';
$DB_PASS = 'DATABASE_PASSWORD';
$mysqli = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n"mysqli_connect_error());
exit();
}

// A QUICK QUERY ON A FAKE USER TABLE
$query = "SELECT * FROM `users` WHERE `status`='bonkers'";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);

// GOING THROUGH THE DATA
if($result->num_rows > 0{
while($row $result->fetch_assoc()) {
echo stripslashes($row['username']);
}
}
else {
echo 'NO RESULTS';
}
// CLOSE CONNECTION
mysqli_close($mysqli);

?>

This will work for 90% of your querying needs.