可以用 array() 语言结构来新建一个数组。它接受任意数量用逗号分隔的 键(key) => 值(value)对。
自 5.4 起可以使用短数组定义语法,用 [] 替代 array()。
key 可以是 integer 或者 string。value 可以是任意类型。
此外 key 会有如下的强制转换:
如果在数组定义中多个单元都使用了同一个键名,则只使用了最后一个,之前的都被覆盖了。
检测一个类是否可以使用 foreach 进行遍历的接口。 无法被单独实现的基本抽象接口。相反它必须由 IteratorAggregate 或 Iterator 接口实现。 这个接口没有任何方法,它的作用仅仅是作为所有可遍历类的基本接口。 PHP 已经提供了一些用于日常任务的迭代器。 详细列表参见 SPL 迭代器。
创建外部迭代器的接口。
可在内部迭代自己的外部迭代器或类的接口。
提供像访问数组一样访问对象的能力的接口。
自定义序列化的接口。实现此接口的类将不再支持 __sleep() 和 __wakeup()。不论何时,只要有实例需要被序列化,serialize 方法都将被调用。它将不会调用 __destruct() 或有其他影响,除非程序化地调用此方法。当数据被反序列化时,类将被感知并且调用合适的 unserialize() 方法而不是调用 __construct()。如果需要执行标准的构造器,你应该在这个方法中进行处理。
用于代表 匿名函数 的类. 匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象。在过去,这个类被认为是一个实现细节,但现在可以依赖它做一些事情。自 PHP 5.4 起,这个类带有一些方法,允许在匿名函数创建后对其进行更多的控制。 除了此处列出的方法,还有一个 __invoke 方法。这是为了与其他实现了 __invoke()魔术方法 的对象保持一致性,但调用匿名函数的过程与它无关。
class Test implements Iterator{ private $item = array('a' => 4, 1, 2, 3); public function rewind() { reset($this->item); } public function current() { return current($this->item); } public function key() { return key($this->item); } public function next() { return next($this->item); } public function valid() { return($this->current() !== false); } } $t = new Test; foreach($t as $k => $v){ var_dump($k, $v); }
引用全局作用域中可用的全部变量
$HTTP_SERVER_VARS [已弃用] — 服务器和执行环境信息
$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。这也就意味着大量的此类变量都会在» CGI 1.1 规范中说明,所以应该仔细研究一下。
$_GET – $HTTP_GET_VARS [已弃用] — HTTP GET 变量
$_POST – $HTTP_POST_VARS [已弃用] — HTTP POST 变量
HTTP 文件上传变量
HTTP Cookies
Session 变量
默认情况下包含了 $_GET,$_POST 和 $_COOKIE 的数组。
$_ENV – $HTTP_ENV_VARS [已弃用] — 环境变量 通过环境方式传递给当前脚本的变量的数组。
这些变量被从 PHP 解析器的运行环境导入到 PHP 的全局命名空间。很多是由支持 PHP 运行的 Shell 提供的,并且不同的系统很可能运行着不同种类的 Shell,所以不可能有一份确定的列表。请查看你的 Shell 文档来获取定义的环境变量列表。
其他环境变量包含了 CGI 变量,而不管 PHP 是以服务器模块还是 CGI 处理器的方式运行。
$HTTP_ENV_VARS 包含相同的信息,但它不是一个超全局变量。 (注意 $HTTP_ENV_VARS 和 $_ENV 是不同的变量,PHP 处理它们的方式不同)
对于任意 integer,float,string,boolean 和 resource 类型,如果将一个值转换为数组,将得到一个仅有一个元素的数组,其下标为 0,该元素即为此标量的值。换句话说,(array)$scalarValue 与 array($scalarValue) 完全一样。
如果一个 object 类型转换为 array,则结果为一个数组,其单元为该对象的属性。键名将为成员变量名,不过有几点例外:整数属性不可访问;私有变量前会加上类名作前缀;保护变量前会加上一个 ‘*’ 做前缀。这些前缀的前后都各有一个 NULL 字符。这会导致一些不可预知的行为
将 NULL 转换为 array 会得到一个空的数组。
serialize
unserialize
json_encode
json_decode
implode
explode
CJSON
array_walk
与 array_map
有什么不同?array array_map ( callable $callback , array $arr1 [, array $… ] )
array_map() 返回一个数组,该数组包含了 arr1 中的所有单元经过 callback 作用过之后的单元。callback 接受的参数数目应该和传递给 array_map() 函数的数组数目一致。
bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] )
array_walk() 不会受到 array 内部数组指针的影响。array_walk() 会遍历整个数组而不管指针的位置。将用户自定义函数 funcname 应用到 array 数组中的每个单元。
for
和 foreach
谁更快呢?为什么?有很多关于PHP数组的遍历效率的误解。我打算尽量揭开它的神秘面纱。
测试分为两个部分:
测试版本:PHP 5.5.9-1ubuntu4.5 (cli) (built: Oct 29 2014 11:59:10)
测试数组:$array = range(1, 1000000);
测试读
$start = microtime(true); foreach($array as $value){$value;}; printf('%.6f' . PHP_EOL, microtime(true) - $start); $start = microtime(true); $count = count($array); for($i=0;$i<$count;$i++){$array[$i];} printf('%.6f' . PHP_EOL, microtime(true) - $start);
常见错误:
foreach($array as $value);
for($i=0;$i<$count;$i++);
for-loop
根本没有遍历数组,因此结果没有多少参考价值。for($i=0;$i<count($array);$i++)
PHP每次循环都会重新计算count($array)
的值,虽然时间复杂度为1,但仍会对结果产生影响。结果分析:
foreach | for | |
---|---|---|
0.040372 | 0.063877 |
结果分析: PHP数组内部指针(Bucket *pInternalPointer)指向当前数组元素(Bucket),当前数组元素保存着指向下一元素的指针(struct bucket *pListNext),foreach
能用到这个结构快速遍历整个数组。 for-loop
的开销在于哈希算法,以及哈希冲突时遍历整个Bucket(PHP中的哈希表是使用拉链法来解决冲突的,具体点讲就是使用链表来存储哈希到同一个槽位的数据, Zend为了保存数据之间的关系使用了双向列表来链接元素)
测试写
$start = microtime(true); foreach($array as $key => $value) { $array[$key] += 1; } printf('%.6f' . PHP_EOL, microtime(true) - $start); $start = microtime(true); $count = count($array); for($i=0;$i<$count;$i++) { $array[$i] += 1; } printf('%.6f' . PHP_EOL, microtime(true) - $start);
常见错误:
结果分析:
foreach | for | |
---|---|---|
0.299544 | 0.077412 |
foreach
循环性能下降很快,主要性能消耗在执行变量分离(PHP的COW机制(copy on write 写时复制),遍历时$value
and $array[$key]
共同指向一个Bucket
, 所以性能损耗很小,但当其中一个值改变时,则需要执行变量分离)
*这也是测试代码的问题所在,foreach
不仅执行了哈希查找,还进行了变量分离,有个办法可以避免这个问题,但很少人用:
foreach($array as &$value) { $value += 1; } unset($value);
PHP中的哈希表实现在Zend/zend_hash.c中, PHP使用如下两个数据结构来实现哈希表,HashTable结构体用于保存整个哈希表需要的基本信息, 而Bucket结构体用于保存具体的数据内容
typedef struct _hashtable { uint nTableSize; // hash Bucket的大小,最小为8,以2x增长。 uint nTableMask; // nTableSize-1 , 索引取值的优化 uint nNumOfElements; // hash Bucket中当前存在的元素个数,count()函数会直接返回此值 ulong nNextFreeElement; // 下一个数字索引的位置 Bucket *pInternalPointer; // 当前遍历的指针(foreach比for快的原因之一) Bucket *pListHead; // 存储数组头元素指针 Bucket *pListTail; // 存储数组尾元素指针 Bucket **arBuckets; // 存储hash数组 dtor_func_t pDestructor; // 在删除元素时执行的回调函数,用于资源的释放 zend_bool persistent; //指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。 unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归) zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次 #if ZEND_DEBUG int inconsistent; #endif } HashTable;
数据容器:槽位
typedef struct bucket { ulong h; // 对char *key进行hash后的值,或者是用户指定的数字索引值 uint nKeyLength; // hash关键字的长度,如果数组索引为数字,此值为0 void *pData; // 指向value,一般是用户数据的副本,如果是指针数据,则指向pDataPtr void *pDataPtr; //如果是指针数据,此值会指向真正的value,同时上面pData会指向此值 struct bucket *pListNext; // 整个hash表的下一元素 struct bucket *pListLast; // 整个哈希表该元素的上一个元素 struct bucket *pNext; // 存放在同一个hash Bucket内的下一个元素 struct bucket *pLast; // 同一个哈希bucket的上一个元素 // 保存当前值所对于的key字符串,这个字段只能定义在最后,实现变长结构体 char arKey[1]; } Bucket;
reset($array); while(list($key, $value) = each($array)); $value = reset($array); do { } while($value = next($array));
foreach
时应该注意什么问题?1.
$array = array(1,2,3,4); echo next($array); foreach ($array as $value) { if (!next($array)) reset($array); } echo current($array);
2.
$array = array(1,2,3,4); foreach($array as $key => $value) { $array[$key] = &$value; }
3.
$array = array(1,2,3,4); foreach($array as $key => $value) { $value = &$array[$key]; }
array_key_exists
检查给定的键名或索引是否存在于数组中。isset
检测变量是否设置,并且不是 NULL。in_array
检查数组中是否存在某个值array_search
在数组中搜索给定的值,如果成功则返回相应的键名array_keys
返回数组中所有的键名,如果指定了可选参数 search_value,则只返回该值的键名。否则 input 数组中的所有键名都会被返回。$array[$key]
array_flip
array_search
在数组中搜索给定的值,如果成功则返回相应的键名array_keys
返回数组中所有的键名,如果指定了可选参数 search_value,则只返回该值的键名。否则 input 数组中的所有键名都会被返回。$array = array(599 => 'PHP', 'JAVA'); $search = 'P'; $result = array_filter(array_map(function($value) use ($search) { return is_numeric(strpos($value, $search)) ? $value : false; }, $array));
$grade = array("score" => array(70, 95, 70.0, 60, "70"), "name" => array("Zhang San", "Li Si", "Wang Wu", "Zhao Liu", "Liu Qi")); array_multisort($grade["score"], SORT_NUMERIC, SORT_DESC, // 将分数作为数值,由高到低排序 $grade["name"], SORT_STRING, SORT_ASC); // 将名字作为字符串,由小到大排序
$data[] = array('volume' => 67, 'edition' => 2); $data[] = array('volume' => 86, 'edition' => 1); $data[] = array('volume' => 85, 'edition' => 6); $data[] = array('volume' => 98, 'edition' => 2); $data[] = array('volume' => 86, 'edition' => 6); $data[] = array('volume' => 67, 'edition' => 7); $volume = array_column($data, 'volume'); $edition = array_column($data, 'edition'); array_multisort($volume, SORT_DESC, $edition, SORT_ASC, $data);
array_splice — 把数组中的一部分去掉并用其它值取代。把 input 数组中由 offset 和 length 指定的单元去掉,如果提供了 replacement 参数,则用其中的单元取代。注意 input 中的数字键名不被保留。 array_splice($array, 1, 0, array('Python', 'JAVASCRIPT'));
array_slice — 从数组中取出一段
array array_filter ( array $input [, callable $callback = “” ] )
依次将 input 数组中的每个值传递到 callback 函数。如果 callback 函数返回 TRUE,则 input 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。
array_multisort
函数?array_map(function($value) {}, $array);
SplHeap
SplStack
$n = 254; $a = []; while($n > 1) { $a[] = $n % 2; $n >>= 1; } echo implode('', array_reverse($a));
递归调用
function recursiveDirectory($dir) { if (is_dir($dir) && $db = opendir($dir)) { while ($file = readdir($db)) { $path = $dir . '/' . $file; if (in_array($file, array('.', '..'))) { $files[] = $file; continue; } elseif (is_dir($path)) { RecursiveDirectory($path); } elseif (is_file($path)) { } } closedir($db); } }
使用队列
function queueDirectory($dir) { $queue = array(''); while (($file = array_shift($queue)) !== null) { $path = join(DIRECTORY_SEPARATOR, array($dir, $file)); if (in_array($file, array('.', '..'))) { $files[] = $file; continue; } elseif (is_dir($path) && $dh = opendir($path)) { while ($file = readdir($dh)) { $queue[] = $file; } closedir($dh); } elseif (is_file($path)) { } } }
使用SPL
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST); foreach($objects as $name => $object){ }
function josephus($n, $m) { $i = 1; while (count($n) > 1) { foreach ($n as $k => $v) { if ($i++ == $m) { unset($n[$k]); $i = 1; } } } return array_pop($n); }
function josephus($total, $index) { $result = 0; for($i=2; $i<= $total; $i++) { $result = ($result + $index) % $i; } $num = $result + 1; return $num; }
深入理解PHP之数组(遍历顺序)
深入理解PHP原理之foreach
深入理解PHP内核
The PHP Benchmark
A Closer Look Into PHP Arrays: What You Don’t See
深入理解PHP原理之变量分离/引用(Variables Separation)\