当前位置: 编程技术>php
本页文章导读:
▪理解和运用PHP中的多态性[译]
什么是多态性? Polymorphism(多态性)是一个很长的单词,但是它表示的是一个非常简单的概念。 多态性描述了在面向对象编程模式中类有不同的功能,而共享一个通用的接口。 多态性的优点.........
▪应用开发中涉及到的css和php笔记分享
关于应用中遇到的一些问题及解决办法,做一些记录,以备后用。本人新手,技术还不熟练,笔记比较幼稚。嘲笑请轻笑。 1、关于层的绝对定位。 有两个div层,其中main类div是整.........
▪PHP源代码数组统计count分析
zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的).........
[1]理解和运用PHP中的多态性[译]
来源: 互联网 发布时间: 2013-11-30
什么是多态性?
Polymorphism(多态性)是一个很长的单词,但是它表示的是一个非常简单的概念。
多态性描述了在面向对象编程模式中类有不同的功能,而共享一个通用的接口。
多态性的优点是,并不需要知道它使用的是哪一个类,因为他们都用同样的方式与不同的类的代码工作。
可将多态性类比成现实世界的一个按钮。大家都知道如何使用一个按钮:您只需给它施加压力。一个按钮“确实是这样”,然而,取决于它和什么连接和使用它的上下文 - 但结果并不影响它是如何使用。如果你的老板告诉你按下一个按钮,您已经有执行任务所需的所有信息。
在编程的世界中,多态性是用来使应用程序更加模块化和可扩展的。相比于凌乱的条件语句描述的行动不同的课程,您可以创建根据您的需求选择的互换对象。这是多态性的基本目标。
Interfaces接口
接口是与类(class)类似的,除了它不能包含代码。接口可以定义方法名称和参数,但不是方法的内容。实现接口的任何类都必须实现接口中定义的所有方法。一个类可以实现多个接口。
使用的“interface”关键字声明一个接口:
interface MyInterface {
// methods
}
被附加到一个类,使用“implements”关键字(多个接口,可以通过用逗号分开):
class MyClass implements MyInterface {
// methods
}
在接口中可以像在类中一样定义方法,除了没有方法体(在大括号中的部分)以外。
interface MyInterface {
public function doThis();
public function doThat();
public function setName($name);
}
在这里定义的所有方法都必须如接口中所描述地那样被包含在任何实现它的类中。(读下面的代码注释)
//合法的 VALID
class MyClass implements MyInterface {
protected $name;
public function doThis() {
// code that does this
}
public function doThat() {
// code that does that
}
public function setName($name) {
$this->name = $name;
}
}
// 非法的INVALID
class MyClass implements MyInterface {
// missing doThis()!
private function doThat() {
// this should be public!
}
public function setName() {
// missing the name argument!
}
}
抽象类Abstract Class
抽象类是接口和类的混合。它可以像接口一样定义方法。继承自抽象类的类必须实现抽象类中定义的所有抽象方法。
抽象类的定义方式与类一样,不过是在前面附加了一个abstract 关键字。
abstract class MyAbstract {
// methods
}
并且 是用 ‘extends‘ 关键字附加到类:
class MyClass extends MyAbstract {
// class methods
}
就像在普通类中一样,普通的方法以及任何抽象方法(使用关键字“abstract”)可以在抽象类中定义。抽象方法的行为就像在接口中定义的的方法,而且继承它的扩展类中必须实现完全一样的定义。
abstract class MyAbstract {
public $name;
public function doThis() {
// do this
}
abstract public function doThat();
abstract public function setName($name);
}
我们假设你有一个文章Article类负责管理你网站上的文章。它包含关于文章的信息,包括:title, author, date, and category.
就像下面这样:
class poly_base_Article {
public $title;
public $author;
public $date;
public $category;
public function __construct($title, $author, $date, $category = 0) {
$this->title = $title;
$this->author = $author;
$this->date = $date;
$this->category = $category;
}
}
注意:在这个教程中的示例类使用了“package_component_Class”的命名约定,这是一个用来将类名分隔到虚拟的命名空间来避免命名冲突的通用方法。
现在你想添加一个方法来输出各种不同格式的信息,如XML和JSON。你也许会打算像下面这样做:
class poly_base_Article {
//...
public function write($type) {
$ret = '';
switch($type) {
case 'XML':
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
break;
case 'JSON':
$array = array('article' => $obj);
$ret = json_encode($array);
break;
}
return $ret;
}
}
这种解决方案是丑陋的,但是它是可靠的——至少在现在。问下你自己将来会发生什么,当我们需要添加更多格式的时候?你可以继续编辑这个类,然后添加越来越多的case ,
但是现在你只是在稀释(FIX ME: diluting) 你的类。
OOP的一个重要原则是一个类应该做的一件事情,而应该把它做好。
考虑到这一点,条件语句应该是一个红色的标志,表明你的类是试图做太多不同的东西。这是多态性的用武之地。
在我们的例子中,有两个任务明确的提出:管理文章和格式化其数据。在本教程中,我们将重构我们的格式化代码到一个新的类,然后我们会发现使用多态性是多么容易。
Step 2: 定义你的接口 Define Your Interface
第一件事就是我们应该定义接口,努力想好怎么定义你的接口是件重要的事情,因为对它的任何改变都将需要改动调用它的代码。
在我们这个例子中,我们将使用一个简单的接口来定义一个方法:
interface poly_writer_Writer {
public function write(poly_base_Article $obj);
}
它就是那样简单,我们已经定义了一个公用方法write() ,它接受一个文章对象作为参数。任何实现Writer接口的类都将会确保有这个方法。
小提示:如果你想严格限制传递给你的方法和函数的参数类型,你可以使用类型提示,就像我们已经在write()方法中做的一样,它只能接受poly_base_Article 对象类型的数
据。不幸的是,在目前版本的PHP中,返回类型提示是不被支持的,所以,你要小心返回值的类型了。
Step 3: 创建实现类 Create Your Implementation
定义接口后,该是时候来创建类来真正干活的啦。在我们的例子中,我们需要输出两种格式。这样,我们就要两个Writer 类:XMLWriter 和 JSONWriter 。从传递过来的
Article 对象提取数据然后格式化这些信息完全取决于这些类了。
下面是 XMLWriter 类的一个示例:
class poly_writer_XMLWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
return $ret;
}
}
就如你可以从类定义中看到的一样,我们使用implements关键字来实现我们的接口。write() 方法包含格式化为XML的功能。
现在我们来看下JSONWriter 类:
class poly_writer_JSONWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$array = array('article' => $obj);
return json_encode($array);
}
}
现在,我们的代码中的特定每种格式都包含在单独的类。每个类有全权负责处理特定的格式,而不是其他。您的应用程序中没有其他部分需要关心这些是如何工作的才能使用它,
感谢我们的接口。
Step 4: 使用你的接口Use Your Implementation
在我们的新类定义后,该是时候来重温一下我们的Article类了,所有原write() 方法中的代码已经被分离出来,进入到我们的新类中了。
我们的所有方法现在需要做的就是使用这些新的类,像这样:
class poly_base_Article {
//...
public function write(poly_writer_Writer $writer) {
return $writer->write($this);
}
}
获取一个 Writer对象 Obtaining A Writer
你也许会疑惑你该从哪里获取一个 Writer对象开始,因为你需要传递一个 Writer对象到这个方法。
这完全取决于你,并且,有很多策略。如,你可能会使用工厂类来获取请求数据然后创建一个对象:
class poly_base_Factory {
public static function getWriter() {
// grab request variable
$format = $_REQUEST['format'];
// construct our class name and check its existence
$class = 'poly_writer_' . $format . 'Writer';
if(class_exists($class)) {
// return a new Writer object
return new $class();
}
// otherwise we fail
throw new Exception('Unsupported format');
}
}
就像我说的,根据你的需求,有好多其它策略可用。在这个例子中,通过一个请求变量选择哪种格式是要使用的。它基于request请求变量来构造一个类名,检测它是否存在,
然后返回一个新的Writer对象。如果没有那个名字的类存在,抛出一个异常,让客户端代码决定接下来要干什么。
Step 5: 把它们放一起 Put It All Together
当所有东东都到位了,下面是我们的客户端代码如何放在一起:
$article = new poly_base_Article('Polymorphism', 'Steve', time(), 0);
try {
$writer = poly_base_Factory::getWriter();
}
catch (Exception $e) {
$writer = new poly_writer_XMLWriter();
}
echo $article->write($writer);
首先,我们创建了一个示例 Article 对象来配合工作。然后,我们试图从工厂Factory获取一个Factory对象,如果异常发生的话回滚到默认(XMLWriter) 。
最后,我们传递Writer对象给我们的Article的 write() 方法,输出结果。
结论 Conclusion
在本教程中,我提供了一个多态性的简介而且解释了PHP中的接口。我希望你意识到,我只向您展示一个潜在的使用多态性的案例。
多态性是以一个优雅的方式来避免您的OOP代码中丑陋的条件语句。它遵循的原则是使您的组件分离,而且它是许多设计模式的组成部分。如果您有任何问题,不要犹豫,在评论中提问!
译自:http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
原文发表在:http://ihacklog.com/?p=4703
Polymorphism(多态性)是一个很长的单词,但是它表示的是一个非常简单的概念。
多态性描述了在面向对象编程模式中类有不同的功能,而共享一个通用的接口。
多态性的优点是,并不需要知道它使用的是哪一个类,因为他们都用同样的方式与不同的类的代码工作。
可将多态性类比成现实世界的一个按钮。大家都知道如何使用一个按钮:您只需给它施加压力。一个按钮“确实是这样”,然而,取决于它和什么连接和使用它的上下文 - 但结果并不影响它是如何使用。如果你的老板告诉你按下一个按钮,您已经有执行任务所需的所有信息。
在编程的世界中,多态性是用来使应用程序更加模块化和可扩展的。相比于凌乱的条件语句描述的行动不同的课程,您可以创建根据您的需求选择的互换对象。这是多态性的基本目标。
Interfaces接口
接口是与类(class)类似的,除了它不能包含代码。接口可以定义方法名称和参数,但不是方法的内容。实现接口的任何类都必须实现接口中定义的所有方法。一个类可以实现多个接口。
使用的“interface”关键字声明一个接口:
代码如下:
interface MyInterface {
// methods
}
被附加到一个类,使用“implements”关键字(多个接口,可以通过用逗号分开):
代码如下:
class MyClass implements MyInterface {
// methods
}
在接口中可以像在类中一样定义方法,除了没有方法体(在大括号中的部分)以外。
代码如下:
interface MyInterface {
public function doThis();
public function doThat();
public function setName($name);
}
在这里定义的所有方法都必须如接口中所描述地那样被包含在任何实现它的类中。(读下面的代码注释)
代码如下:
//合法的 VALID
class MyClass implements MyInterface {
protected $name;
public function doThis() {
// code that does this
}
public function doThat() {
// code that does that
}
public function setName($name) {
$this->name = $name;
}
}
// 非法的INVALID
class MyClass implements MyInterface {
// missing doThis()!
private function doThat() {
// this should be public!
}
public function setName() {
// missing the name argument!
}
}
抽象类Abstract Class
抽象类是接口和类的混合。它可以像接口一样定义方法。继承自抽象类的类必须实现抽象类中定义的所有抽象方法。
抽象类的定义方式与类一样,不过是在前面附加了一个abstract 关键字。
代码如下:
abstract class MyAbstract {
// methods
}
并且 是用 ‘extends‘ 关键字附加到类:
代码如下:
class MyClass extends MyAbstract {
// class methods
}
就像在普通类中一样,普通的方法以及任何抽象方法(使用关键字“abstract”)可以在抽象类中定义。抽象方法的行为就像在接口中定义的的方法,而且继承它的扩展类中必须实现完全一样的定义。
代码如下:
abstract class MyAbstract {
public $name;
public function doThis() {
// do this
}
abstract public function doThat();
abstract public function setName($name);
}
我们假设你有一个文章Article类负责管理你网站上的文章。它包含关于文章的信息,包括:title, author, date, and category.
就像下面这样:
代码如下:
class poly_base_Article {
public $title;
public $author;
public $date;
public $category;
public function __construct($title, $author, $date, $category = 0) {
$this->title = $title;
$this->author = $author;
$this->date = $date;
$this->category = $category;
}
}
注意:在这个教程中的示例类使用了“package_component_Class”的命名约定,这是一个用来将类名分隔到虚拟的命名空间来避免命名冲突的通用方法。
现在你想添加一个方法来输出各种不同格式的信息,如XML和JSON。你也许会打算像下面这样做:
代码如下:
class poly_base_Article {
//...
public function write($type) {
$ret = '';
switch($type) {
case 'XML':
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
break;
case 'JSON':
$array = array('article' => $obj);
$ret = json_encode($array);
break;
}
return $ret;
}
}
这种解决方案是丑陋的,但是它是可靠的——至少在现在。问下你自己将来会发生什么,当我们需要添加更多格式的时候?你可以继续编辑这个类,然后添加越来越多的case ,
但是现在你只是在稀释(FIX ME: diluting) 你的类。
OOP的一个重要原则是一个类应该做的一件事情,而应该把它做好。
考虑到这一点,条件语句应该是一个红色的标志,表明你的类是试图做太多不同的东西。这是多态性的用武之地。
在我们的例子中,有两个任务明确的提出:管理文章和格式化其数据。在本教程中,我们将重构我们的格式化代码到一个新的类,然后我们会发现使用多态性是多么容易。
Step 2: 定义你的接口 Define Your Interface
第一件事就是我们应该定义接口,努力想好怎么定义你的接口是件重要的事情,因为对它的任何改变都将需要改动调用它的代码。
在我们这个例子中,我们将使用一个简单的接口来定义一个方法:
代码如下:
interface poly_writer_Writer {
public function write(poly_base_Article $obj);
}
它就是那样简单,我们已经定义了一个公用方法write() ,它接受一个文章对象作为参数。任何实现Writer接口的类都将会确保有这个方法。
小提示:如果你想严格限制传递给你的方法和函数的参数类型,你可以使用类型提示,就像我们已经在write()方法中做的一样,它只能接受poly_base_Article 对象类型的数
据。不幸的是,在目前版本的PHP中,返回类型提示是不被支持的,所以,你要小心返回值的类型了。
Step 3: 创建实现类 Create Your Implementation
定义接口后,该是时候来创建类来真正干活的啦。在我们的例子中,我们需要输出两种格式。这样,我们就要两个Writer 类:XMLWriter 和 JSONWriter 。从传递过来的
Article 对象提取数据然后格式化这些信息完全取决于这些类了。
下面是 XMLWriter 类的一个示例:
代码如下:
class poly_writer_XMLWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
return $ret;
}
}
就如你可以从类定义中看到的一样,我们使用implements关键字来实现我们的接口。write() 方法包含格式化为XML的功能。
现在我们来看下JSONWriter 类:
代码如下:
class poly_writer_JSONWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$array = array('article' => $obj);
return json_encode($array);
}
}
现在,我们的代码中的特定每种格式都包含在单独的类。每个类有全权负责处理特定的格式,而不是其他。您的应用程序中没有其他部分需要关心这些是如何工作的才能使用它,
感谢我们的接口。
Step 4: 使用你的接口Use Your Implementation
在我们的新类定义后,该是时候来重温一下我们的Article类了,所有原write() 方法中的代码已经被分离出来,进入到我们的新类中了。
我们的所有方法现在需要做的就是使用这些新的类,像这样:
代码如下:
class poly_base_Article {
//...
public function write(poly_writer_Writer $writer) {
return $writer->write($this);
}
}
获取一个 Writer对象 Obtaining A Writer
你也许会疑惑你该从哪里获取一个 Writer对象开始,因为你需要传递一个 Writer对象到这个方法。
这完全取决于你,并且,有很多策略。如,你可能会使用工厂类来获取请求数据然后创建一个对象:
代码如下:
class poly_base_Factory {
public static function getWriter() {
// grab request variable
$format = $_REQUEST['format'];
// construct our class name and check its existence
$class = 'poly_writer_' . $format . 'Writer';
if(class_exists($class)) {
// return a new Writer object
return new $class();
}
// otherwise we fail
throw new Exception('Unsupported format');
}
}
就像我说的,根据你的需求,有好多其它策略可用。在这个例子中,通过一个请求变量选择哪种格式是要使用的。它基于request请求变量来构造一个类名,检测它是否存在,
然后返回一个新的Writer对象。如果没有那个名字的类存在,抛出一个异常,让客户端代码决定接下来要干什么。
Step 5: 把它们放一起 Put It All Together
当所有东东都到位了,下面是我们的客户端代码如何放在一起:
代码如下:
$article = new poly_base_Article('Polymorphism', 'Steve', time(), 0);
try {
$writer = poly_base_Factory::getWriter();
}
catch (Exception $e) {
$writer = new poly_writer_XMLWriter();
}
echo $article->write($writer);
首先,我们创建了一个示例 Article 对象来配合工作。然后,我们试图从工厂Factory获取一个Factory对象,如果异常发生的话回滚到默认(XMLWriter) 。
最后,我们传递Writer对象给我们的Article的 write() 方法,输出结果。
结论 Conclusion
在本教程中,我提供了一个多态性的简介而且解释了PHP中的接口。我希望你意识到,我只向您展示一个潜在的使用多态性的案例。
多态性是以一个优雅的方式来避免您的OOP代码中丑陋的条件语句。它遵循的原则是使您的组件分离,而且它是许多设计模式的组成部分。如果您有任何问题,不要犹豫,在评论中提问!
译自:http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
原文发表在:http://ihacklog.com/?p=4703
[2]应用开发中涉及到的css和php笔记分享
来源: 互联网 发布时间: 2013-11-30
关于应用中遇到的一些问题及解决办法,做一些记录,以备后用。本人新手,技术还不熟练,笔记比较幼稚。嘲笑请轻笑。
1、关于层的绝对定位。
有两个div层,其中main类div是整体的层,cloud类div是绝对定位层,实现的效果是在屏幕右上角显示一个云彩的gif图片。div布局代码如下:
<div ></div>
<div >
...
</div>
在过程中一度将cloud层放在了main层里,导致ie无法显示cloud层。后将cloud层放出main层外部,并将main的position改为relative,才解决了这个问题。CSS代码如下:
.main{
position:relative;
top:30px;
width:600px;
height:auto;
text-align:left;
background-color:#FFFFFF;
border:5px solid #FF0011;
}
.cloud {
background: url(/image/cloud.gif) no-repeat 100% 50%;
height: 171px;
position: absolute;
right: 0px;
top: 0px;
width: 345px;
z-index: 1000;
}
2、关于年月日的下拉框选择实现。
年月日的下拉框做的很呆滞,日期不能随着年份和月份的变化而变化。但是这是比较初级的解决方案,也参考了网上别人的方法。以后再研究更新的方法。
<?php
$months = array (1 => '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月');
$days = range(1,31);
$years = range(1930,2011);
echo '<select name="year" >';
foreach ($years as $value) {
echo "<option value=\"$value\">$value 年</option>";
if ($value == 1987) {
echo '<option value=\'1987\' selected>1987 年</option>';
}
}
echo '</select>';
echo '<select name="month" >';
foreach ($months as $key => $value) {
echo "<option value=\"$key\">$value</option>";
}
echo '</select>';
echo '<select name="day" >';
foreach ($days as $value) {
echo "<option value=\"$value\"> $value 日</option>";
}
echo '</select>';
?>
3、待续。
1、关于层的绝对定位。
有两个div层,其中main类div是整体的层,cloud类div是绝对定位层,实现的效果是在屏幕右上角显示一个云彩的gif图片。div布局代码如下:
代码如下:
<div ></div>
<div >
...
</div>
在过程中一度将cloud层放在了main层里,导致ie无法显示cloud层。后将cloud层放出main层外部,并将main的position改为relative,才解决了这个问题。CSS代码如下:
代码如下:
.main{
position:relative;
top:30px;
width:600px;
height:auto;
text-align:left;
background-color:#FFFFFF;
border:5px solid #FF0011;
}
.cloud {
background: url(/image/cloud.gif) no-repeat 100% 50%;
height: 171px;
position: absolute;
right: 0px;
top: 0px;
width: 345px;
z-index: 1000;
}
2、关于年月日的下拉框选择实现。
年月日的下拉框做的很呆滞,日期不能随着年份和月份的变化而变化。但是这是比较初级的解决方案,也参考了网上别人的方法。以后再研究更新的方法。
代码如下:
<?php
$months = array (1 => '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月');
$days = range(1,31);
$years = range(1930,2011);
echo '<select name="year" >';
foreach ($years as $value) {
echo "<option value=\"$value\">$value 年</option>";
if ($value == 1987) {
echo '<option value=\'1987\' selected>1987 年</option>';
}
}
echo '</select>';
echo '<select name="month" >';
foreach ($months as $key => $value) {
echo "<option value=\"$key\">$value</option>";
}
echo '</select>';
echo '<select name="day" >';
foreach ($days as $value) {
echo "<option value=\"$value\"> $value 日</option>";
}
echo '</select>';
?>
3、待续。
[3]PHP源代码数组统计count分析
来源: 互联网 发布时间: 2013-11-30
zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:
//文件1:zend/zend.h
/*
* zval
*/
typedef struct _zval_struct zval;
...
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
//hash表的结构如下
//文件2:zend/zend_hash.h
typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
}
HashTable;
一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:
//文件3:zend/zend_operators.php
#define Z_STRLEN(zval) (zval).value.str.len
...
#define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
...
#define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)
而对于数组的count操作,其实有两种结果,在count 的api中也提到了第二个参数mode《http://www.php.net/manual/en/function.count.php》,这个mode参数指明了,是否需要重新统计,而它的重新统计将会遍历一次数组,效率上是O(N)[N:长度],默认情况下是不重新统计,那这个时候将会直接输出hashtable中的nNumOfElements,此时的效率也是O(1)次:count代码如下:
//文件4:ext/standard/array.c
PHP_FUNCTION(count)
{
zval *array;
long mode = COUNT_NORMAL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
return;
}
switch (Z_TYPE_P(array)) {
case IS_NULL:
RETURN_LONG(0);
break;
case IS_ARRAY:
RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
break;
.....
//php_count_recursive的实现
static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
{
long cnt = 0;
zval **element;
if (Z_TYPE_P(array) == IS_ARRAY) {
//错误处理
if (Z_ARRVAL_P(array)->nApplyCount > 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
return 0;
}
//通过zend_hash_num_elements直接获得长度
cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
//如果指定了需要重新统计,则会进入一次循环统计
if (mode == COUNT_RECURSIVE) {
HashPosition pos;
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
) {
Z_ARRVAL_P(array)->nApplyCount++;
cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
Z_ARRVAL_P(array)->nApplyCount--;
}
}
}
return cnt;
}
//文件5:zend/zend_hash.c
//zend_hash_num_elements的实现
ZEND_API int zend_hash_num_elements(const HashTable *ht)
{
IS_CONSISTENT(ht);
return ht->nNumOfElements;
}
代码如下:
//文件1:zend/zend.h
/*
* zval
*/
typedef struct _zval_struct zval;
...
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
//hash表的结构如下
//文件2:zend/zend_hash.h
typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
}
HashTable;
一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:
代码如下:
//文件3:zend/zend_operators.php
#define Z_STRLEN(zval) (zval).value.str.len
...
#define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
...
#define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)
而对于数组的count操作,其实有两种结果,在count 的api中也提到了第二个参数mode《http://www.php.net/manual/en/function.count.php》,这个mode参数指明了,是否需要重新统计,而它的重新统计将会遍历一次数组,效率上是O(N)[N:长度],默认情况下是不重新统计,那这个时候将会直接输出hashtable中的nNumOfElements,此时的效率也是O(1)次:count代码如下:
代码如下:
//文件4:ext/standard/array.c
PHP_FUNCTION(count)
{
zval *array;
long mode = COUNT_NORMAL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
return;
}
switch (Z_TYPE_P(array)) {
case IS_NULL:
RETURN_LONG(0);
break;
case IS_ARRAY:
RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
break;
.....
//php_count_recursive的实现
static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
{
long cnt = 0;
zval **element;
if (Z_TYPE_P(array) == IS_ARRAY) {
//错误处理
if (Z_ARRVAL_P(array)->nApplyCount > 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
return 0;
}
//通过zend_hash_num_elements直接获得长度
cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
//如果指定了需要重新统计,则会进入一次循环统计
if (mode == COUNT_RECURSIVE) {
HashPosition pos;
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
) {
Z_ARRVAL_P(array)->nApplyCount++;
cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
Z_ARRVAL_P(array)->nApplyCount--;
}
}
}
return cnt;
}
//文件5:zend/zend_hash.c
//zend_hash_num_elements的实现
ZEND_API int zend_hash_num_elements(const HashTable *ht)
{
IS_CONSISTENT(ht);
return ht->nNumOfElements;
}
最新技术文章: