首页 > 编程 > PHP > 正文

自定义PHP数组类的实现

2019-11-08 03:12:32
字体:
来源:转载
供稿:网友

php一开始是面向过程的语言,到后期才支持面向对象,数组在php中的类型是 “array”:

echo gettype(array());

输出

array

很多操作数组的函数都是以 “array” 开头,第一个参数为要操作的数组。

要实现一个数组类,需要实现Arrayaccess这个接口,这个接口的功能是 “提供像访问数组一样访问对象的能力” ,该接口有四个方法:

abstract public boolean offsetExists ( mixed $offset )abstract public mixed offsetGet ( mixed $offset )abstract public void offsetSet ( mixed $offset , mixed $value )abstract public void offsetUnset ( mixed $offset )

这四个函数的作用如下(假设$obj是一个实现了该接口的类的实例):

offsetExists, 执行isset( $obj[$key])时触发offsetGe,获取$obj[$key]时触发offsetSet, 执行 $obj[$key] = $value时触发offsetUnset, 执行unset($obj[$key])时触发

看上去有点类似C++的运算符重载。我们可以封装一个类,以一个数组变量作为其私有属性,这四个函数操作数组变量就行。

且看代码: 我们把这个类命名为XArray。

class XArray implements ArrayAccess{ PRivate $container = array(); public function __construct($size = 0, $value = 0) { if ($size > 0) { $this->container = array_fill(0, $size, $value); } } public function offsetSet($offset, $value) { echo "call ", __METHOD__, PHP_EOL; if(is_null($offset)) { $this->container[] = $value; } else { $this->container[$offset] = $value; } } public function offsetUnset($offset) { echo "call ", __METHOD__, PHP_EOL; unset($this->container[$offset]); } public function offsetGet($offset) { echo "call ", __METHOD__, PHP_EOL; return isset($this->container[$offset]) ? $this->container[$offset] : null; } public function offsetExists($offset) { echo "call ", __METHOD__, PHP_EOL; return isset($this->container[$offset]); }}

测试:

$obj = new XArray(5, 10);$obj[] = 16;echo $obj['a'];isset($obj[3]);unset($obj['a']);

输出:

call XArray::offsetSetcall XArray::offsetGetcall XArray::offsetExistscall XArray::offsetUnset

正如以上分析所言。

我们可以像操作数组一样操作一个XArray类的实例,可是对于便利操作(foreach),就不行了。

foreach ($obj as $v) { echo $v;}

没有输出。

要能让XArray的实例实现遍历操作,得实现Traversable接口,但这是个抽象接口,不过Iterator接口继承了Traversable接口,所以我们可以实现Iterator接口。这个接口有5个方法:

abstract public mixed current ( void )abstract public scalar key ( void )abstract public void next ( void )abstract public void rewind ( void )abstract public boolean valid ( void )

各方法的作用如下:

current, 返回当前元素key, 返回当前元素的键next, 移动到下一个元素rewind, 返回迭代器的第一个元素valid, 在rewind和next方法之后调用,检查当前位置是否有效

在XArray中增加一个$position私有变量,然后增加以下5个方法:

public function rewind() { echo "call ", __METHOD__, PHP_EOL; reset($this->container); $this->position = 0; } public function current() { echo "call ", __METHOD__, PHP_EOL; return current($this->container); } public function next() { echo "call ", __METHOD__, PHP_EOL; next($this->container); $this->position++; } public function key() { echo "call ", __METHOD__, PHP_EOL; return key($this->container); } public function valid() { echo "call ", __METHOD__, PHP_EOL; return $this->position < count($this->container); }

有rewind, current,next,key这4个方法,内部都是通过调用php操作数组的相应方法来实现的。

测试:

$obj = new XArray();$obj[] = 1;$obj[] = 2;$obj[] = 3;foreach ($obj as $v) { echo $v, PHP_EOL;}

输出:

call XArray::offsetSetcall XArray::offsetSetcall XArray::offsetSetcall XArray::rewindcall XArray::validcall XArray::current1call XArray::nextcall XArray::validcall XArray::current2call XArray::nextcall XArray::validcall XArray::current3call XArray::nextcall XArray::valid

由以上输出可知,首次遍历时,依次调用rewind, valid方法, 然后调用current方法得到值;后面都是依次调用next, valid方法,再调用current方法得到值。如果valid方法返回false,说明遍历到了末尾,则不再调用current方法,遍历结束。

可以看到,用 foreach($obj as $v)这种遍历方式,并没有调用key方法。

我们把遍历方式改为foreach($obj as $k => $v)试一下:

$obj = new XArray();$obj[] = 1;$obj[] = 2;$obj[] = 3;foreach ($obj as $k => $v) { echo $v, PHP_EOL;}call XArray::offsetSetcall XArray::offsetSetcall XArray::offsetSetcall XArray::rewindcall XArray::validcall XArray::currentcall XArray::key1call XArray::nextcall XArray::validcall XArray::currentcall XArray::key2call XArray::nextcall XArray::validcall XArray::currentcall XArray::key3call XArray::nextcall XArray::valid

这次有调用key方法了,而且可以看到, key方法是在current方法调用之后才调用的。

这样的数组类,功能还是有点弱,很多数组的方法都没有,比如,push, pop, slice, 也不能获取数组长度(length属性)。

在XArray类中添加如下代码:

public function all() { return $this->container; } /** $obj->length 获取数组元素个数 */ public function __get($property) { if ($property == 'length') { return count($this->container); } return null; } /** 把一个XArray类的实例,数组或其他类型的变量合并到本实例的数组变量中 */ public function merge($data) { $class = get_class($this); if ($data instanceof $class) { $this->container = array_merge($this->container, $data->all()); } elseif (is_array($data)) { $this->container = array_merge($this->container, $data); } else{ $this->container[] = $data; } return $this; } public function shift() { return array_shift($this->container); } public function pop() { return array_pop($this->container); } public function push($ele) { foreach (func_get_args() as $v) { array_push($this->container, $v); } return $this; } public function unshift($ele) { foreach (func_get_args() as $v) { array_unshift($this->container, $v); } return $this; } public function slice($offset, $length) { $arr = array_slice($this->container, $offset, $length); if (empty($arr)) { return null; } $class = get_class($this); $obj = new $class(); return $obj->merge($arr); } /** 打印实例内容 */ public function dump() { echo "[elements begin]", PHP_EOL; foreach ($this->container as $k => $v) { echo "/t",$k, " => ", $v, PHP_EOL; } echo "[elements end]", PHP_EOL; }

大部分函数都是调用php的同名array_系列数组操作函数,其中unshift, push方法最后时return $this,可以实现链式调用, slice方法则是返回一个XArray对象。

测试代码:

$obj = new XArray(2, 6);$obj[] = 16;$obj->push(1,2,3)->unshift(5)->pop();$obj->dump();echo "len(obj) = ", $obj->length, PHP_EOL;$obj2 = $obj->slice(0, 3);$obj2->dump();

输出:

call XArray::offsetSet[elements begin] 0 => 5 1 => 6 2 => 6 3 => 16 4 => 1 5 => 2[elements end]len(obj) = 6[elements begin] 0 => 5 1 => 6 2 => 6[elements end]

如预期。

由于时间有限,数组操作的其他功能就不实现了。之前查看Laravel的源码,有个文件也是实现了一个数组类,很多数组操作的方法都实现了。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表