diff options
author | Andrey Andreev <narf@devilix.net> | 2014-02-19 22:49:31 +0100 |
---|---|---|
committer | Andrey Andreev <narf@devilix.net> | 2014-02-19 22:49:31 +0100 |
commit | 02545895c1c6551be3b167bcada171fe9423d443 (patch) | |
tree | 5b20681d01f237602d45f93d06ec59c772f19569 | |
parent | e75255684d55fa075dcc7c03274c41201825d430 (diff) |
Add compatibility layer for array_column(), array_replace(), array_replace_recursive()
-rw-r--r-- | system/core/CodeIgniter.php | 1 | ||||
-rw-r--r-- | system/core/Common.php | 2 | ||||
-rw-r--r-- | system/core/compat/array.php | 246 | ||||
-rw-r--r-- | tests/Bootstrap.php | 1 | ||||
-rw-r--r-- | tests/codeigniter/core/compat/array_test.php | 429 | ||||
-rw-r--r-- | user_guide_src/source/changelog.rst | 8 | ||||
-rw-r--r-- | user_guide_src/source/general/compatibility_functions.rst | 52 |
7 files changed, 736 insertions, 3 deletions
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index 2bdd76463..eb3927e47 100644 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -191,6 +191,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); require_once(BASEPATH.'core/compat/mbstring.php'); require_once(BASEPATH.'core/compat/hash.php'); require_once(BASEPATH.'core/compat/password.php'); + require_once(BASEPATH.'core/compat/array.php'); /* * ------------------------------------------------------ diff --git a/system/core/Common.php b/system/core/Common.php index c83d80a3d..7591cd794 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -587,7 +587,7 @@ if ( ! function_exists('_exception_handler')) if ($is_error) { set_status_header(500); - } + } // Should we ignore the error? We'll get the current error_reporting // level and add its bits with the severity bits to find out. diff --git a/system/core/compat/array.php b/system/core/compat/array.php new file mode 100644 index 000000000..b291703ed --- /dev/null +++ b/system/core/compat/array.php @@ -0,0 +1,246 @@ +<?php +/** + * CodeIgniter + * + * An open source application development framework for PHP 5.2.4 or newer + * + * NOTICE OF LICENSE + * + * Licensed under the Open Software License version 3.0 + * + * This source file is subject to the Open Software License (OSL 3.0) that is + * bundled with this package in the files license.txt / license.rst. It is + * also available through the world wide web at this URL: + * http://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to obtain it + * through the world wide web, please send an email to + * licensing@ellislab.com so we can send you a copy immediately. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) + * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * @link http://codeigniter.com + * @since Version 3.0 + * @filesource + */ +defined('BASEPATH') OR exit('No direct script access allowed'); + +/** + * PHP ext/standard/array compatibility package + * + * @package CodeIgniter + * @subpackage CodeIgniter + * @category Compatibility + * @author Andrey Andreev + * @link http://codeigniter.com/user_guide/ + * @link http://php.net/book.array + */ + +// ------------------------------------------------------------------------ + +if (is_php('5.5')) +{ + return; +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('array_column')) +{ + /** + * array_column() + * + * @link http://php.net/array_column + * @param string $array + * @param mixed $column_key + * @param mixed $index_key + * @return array + */ + function array_column(array $array, $column_key, $index_key = NULL) + { + if ( ! in_array($type = gettype($column_key), array('integer', 'string', 'NULL'), TRUE)) + { + if ($type === 'double') + { + $column_key = (int) $column_key; + } + elseif ($type === 'object' && method_exists($column_key, '__toString')) + { + $column_key = (string) $column_key; + } + else + { + trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING); + return FALSE; + } + } + + if ( ! in_array($type = gettype($index_key), array('integer', 'string', 'NULL'), TRUE)) + { + if ($type === 'double') + { + $index_key = (int) $index_key; + } + elseif ($type === 'object' && method_exists($index_key, '__toString')) + { + $index_key = (string) $index_key; + } + else + { + trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING); + return FALSE; + } + } + + $result = array(); + foreach ($array as &$a) + { + if ($column_key === NULL) + { + $value = $a; + } + elseif (is_array($a) && array_key_exists($column_key, $a)) + { + $value = $a[$column_key]; + } + else + { + continue; + } + + if ($index_key === NULL OR ! array_key_exists($index_key, $a)) + { + $result[] = $value; + } + else + { + $result[$a[$index_key]] = $value; + } + } + + return $result; + } +} + +// ------------------------------------------------------------------------ + +if (is_php('5.3')) +{ + return; +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('array_replace')) +{ + /** + * array_replace() + * + * @link http://php.net/array_replace + * @return array + */ + function array_replace() + { + $arrays = func_get_args(); + + if (($c = count($arrays)) === 0) + { + trigger_error('array_replace() expects at least 1 parameter, 0 given', E_USER_WARNING); + return NULL; + } + elseif ($c === 1) + { + if ( ! is_array($arrays[0])) + { + trigger_error('array_replace(): Argument #1 is not an array', E_USER_WARNING); + return NULL; + } + + return $arrays[0]; + } + + $array = array_shift($arrays); + $c--; + + for ($i = 0, $c = count($arrays); $i < $c; $i++) + { + if ( ! is_array($arrays[$i])) + { + trigger_error('array_replace(): Argument #'.($i + 2).' is not an array', E_USER_WARNING); + return NULL; + } + elseif (empty($arrays[$i])) + { + continue; + } + + foreach (array_keys($arrays[$i]) as $key) + { + $array[$key] = $arrays[$i][$key]; + } + } + + return $array; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('array_replace_recursive')) +{ + /** + * array_replace_recursive() + * + * @link http://php.net/array_replace_recursive + * @return array + */ + function array_replace_recursive() + { + $arrays = func_get_args(); + + if (($c = count($arrays)) === 0) + { + trigger_error('array_replace_recursive() expects at least 1 parameter, 0 given', E_USER_WARNING); + return NULL; + } + elseif ($c === 1) + { + if ( ! is_array($arrays[0])) + { + trigger_error('array_replace_recursive(): Argument #1 is not an array', E_USER_WARNING); + return NULL; + } + + return $arrays[0]; + } + + $array = array_shift($arrays); + $c--; + + for ($i = 0, $c = count($arrays); $i < $c; $i++) + { + if ( ! is_array($arrays[$i])) + { + trigger_error('array_replace_recursive(): Argument #'.($i + 2).' is not an array', E_USER_WARNING); + return NULL; + } + elseif (empty($arrays[$i])) + { + continue; + } + + foreach (array_keys($arrays[$i]) as $key) + { + $array[$key] = (is_array($arrays[$i][$key]) && isset($array[$key]) && is_array($array[$key])) + ? array_replace_recursive($array[$key], $arrays[$i][$key]) + : $arrays[$i][$key]; + } + } + + return $array; + } +} + +/* End of file array.php */ +/* Location: ./system/core/compat/array.php */
\ No newline at end of file diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index 5441f718a..9a06f9eb4 100644 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -66,6 +66,7 @@ else include_once SYSTEM_PATH.'core/compat/mbstring.php'; include_once SYSTEM_PATH.'core/compat/hash.php'; include_once SYSTEM_PATH.'core/compat/password.php'; +include_once SYSTEM_PATH.'core/compat/array.php'; include_once $dir.'/mocks/autoloader.php'; spl_autoload_register('autoload'); diff --git a/tests/codeigniter/core/compat/array_test.php b/tests/codeigniter/core/compat/array_test.php new file mode 100644 index 000000000..9d2deaba6 --- /dev/null +++ b/tests/codeigniter/core/compat/array_test.php @@ -0,0 +1,429 @@ +<?php + +class array_test extends CI_TestCase { + + public function test_bootstrap() + { + if (is_php('5.5')) + { + return $this->markTestSkipped('All array functions are already available on PHP 5.5'); + } + elseif ( ! is_php('5.3')) + { + $this->assertTrue(function_exists('array_replace')); + $this->assertTrue(function_exists('array_replace_recursive')); + } + + $this->assertTrue(function_exists('array_column')); + } + + // ------------------------------------------------------------------------ + + /** + * array_column() test + * + * Borrowed from PHP's own tests + * + * @depends test_bootstrap + */ + public function test_array_column() + { + // Basic tests + + $input = array( + array( + 'id' => 1, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + array( + 'id' => 2, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + array( + 'id' => 3, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ) + ); + + // Ensure internal array position doesn't break it + next($input); + + $this->assertEquals( + array('John', 'Sally', 'Jane'), + array_column($input, 'first_name') + ); + + $this->assertEquals( + array(1, 2, 3), + array_column($input, 'id') + ); + + $this->assertEquals( + array( + 1 => 'Doe', + 2 => 'Smith', + 3 => 'Jones' + ), + array_column($input, 'last_name', 'id') + ); + + $this->assertEquals( + array( + 'John' => 'Doe', + 'Sally' => 'Smith', + 'Jane' => 'Jones' + ), + array_column($input, 'last_name', 'first_name') + ); + + // Object key search + + $f = new Foo(); + $b = new Bar(); + + $this->assertEquals( + array('Doe', 'Smith', 'Jones'), + array_column($input, $f) + ); + + $this->assertEquals( + array( + 'John' => 'Doe', + 'Sally' => 'Smith', + 'Jane' => 'Jones' + ), + array_column($input, $f, $b) + ); + + // NULL parameters + + $input = array( + 456 => array( + 'id' => '3', + 'title' => 'Foo', + 'date' => '2013-03-25' + ), + 457 => array( + 'id' => '5', + 'title' => 'Bar', + 'date' => '2012-05-20' + ) + ); + + $this->assertEquals( + array( + 3 => array( + 'id' => '3', + 'title' => 'Foo', + 'date' => '2013-03-25' + ), + 5 => array( + 'id' => '5', + 'title' => 'Bar', + 'date' => '2012-05-20' + ) + ), + array_column($input, NULL, 'id') + ); + + $this->assertEquals( + array( + array( + 'id' => '3', + 'title' => 'Foo', + 'date' => '2013-03-25' + ), + array( + 'id' => '5', + 'title' => 'Bar', + 'date' => '2012-05-20' + ) + ), + array_column($input, NULL, 'foo') + ); + + $this->assertEquals( + array( + array( + 'id' => '3', + 'title' => 'Foo', + 'date' => '2013-03-25' + ), + array( + 'id' => '5', + 'title' => 'Bar', + 'date' => '2012-05-20' + ) + ), + array_column($input, NULL) + ); + + // Data types + + $fh = fopen(__FILE__, 'r', TRUE); + $stdClass = new stdClass(); + $input = array( + array( + 'id' => 1, + 'value' => $stdClass + ), + array( + 'id' => 2, + 'value' => 34.2345 + ), + array( + 'id' => 3, + 'value' => TRUE + ), + array( + 'id' => 4, + 'value' => FALSE + ), + array( + 'id' => 5, + 'value' => NULL + ), + array( + 'id' => 6, + 'value' => 1234 + ), + array( + 'id' => 7, + 'value' => 'Foo' + ), + array( + 'id' => 8, + 'value' => $fh + ) + ); + + $this->assertEquals( + array( + $stdClass, + 34.2345, + TRUE, + FALSE, + NULL, + 1234, + 'Foo', + $fh + ), + array_column($input, 'value') + ); + + $this->assertEquals( + array( + 1 => $stdClass, + 2 => 34.2345, + 3 => TRUE, + 4 => FALSE, + 5 => NULL, + 6 => 1234, + 7 => 'Foo', + 8 => $fh + ), + array_column($input, 'value', 'id') + ); + + // Numeric column keys + + $input = array( + array('aaa', '111'), + array('bbb', '222'), + array('ccc', '333', -1 => 'ddd') + ); + + $this->assertEquals( + array('111', '222', '333'), + array_column($input, 1) + ); + + $this->assertEquals( + array( + 'aaa' => '111', + 'bbb' => '222', + 'ccc' => '333' + ), + array_column($input, 1, 0) + ); + + $this->assertEquals( + array( + 'aaa' => '111', + 'bbb' => '222', + 'ccc' => '333' + ), + array_column($input, 1, 0.123) + ); + + $this->assertEquals( + array( + 0 => '111', + 1 => '222', + 'ddd' => '333' + ), + array_column($input, 1, -1) + ); + + // Non-existing columns + + $this->assertEquals(array(), array_column($input, 2)); + $this->assertEquals(array(), array_column($input, 'foo')); + $this->assertEquals( + array('aaa', 'bbb', 'ccc'), + array_column($input, 0, 'foo') + ); + $this->assertEquals(array(), array_column($input, 3.14)); + + // One-dimensional array + $this->assertEquals(array(), array_column(array('foo', 'bar', 'baz'), 1)); + + // Columns not present in all rows + + $input = array( + array('a' => 'foo', 'b' => 'bar', 'e' => 'bbb'), + array('a' => 'baz', 'c' => 'qux', 'd' => 'aaa'), + array('a' => 'eee', 'b' => 'fff', 'e' => 'ggg') + ); + + $this->assertEquals( + array('qux'), + array_column($input, 'c') + ); + + $this->assertEquals( + array('baz' => 'qux'), + array_column($input, 'c', 'a') + ); + + $this->assertEquals( + array( + 0 => 'foo', + 'aaa' => 'baz', + 1 => 'eee' + ), + array_column($input, 'a', 'd') + ); + + $this->assertEquals( + array( + 'bbb' => 'foo', + 0 => 'baz', + 'ggg' => 'eee' + ), + array_column($input, 'a', 'e') + ); + + $this->assertEquals( + array('bar', 'fff'), + array_column($input, 'b') + ); + + $this->assertEquals( + array( + 'foo' => 'bar', + 'eee' => 'fff' + ), + array_column($input, 'b', 'a') + ); + } + + // ------------------------------------------------------------------------ + + /** + * array_replace(), array_replace_recursive() tests + * + * Borrowed from PHP's own tests + * + * @depends test_bootstrap + */ + public function test_array_replace_recursive() + { + if (is_php('5.3')) + { + return $this->markTestSkipped('array_replace() and array_replace_recursive() are already available on PHP 5.3'); + } + + $array1 = array( + 0 => 'dontclobber', + '1' => 'unclobbered', + 'test2' => 0.0, + 'test3' => array( + 'testarray2' => TRUE, + 1 => array( + 'testsubarray1' => 'dontclobber2', + 'testsubarray2' => 'dontclobber3' + ) + ) + ); + + $array2 = array( + 1 => 'clobbered', + 'test3' => array( + 'testarray2' => FALSE + ), + 'test4' => array( + 'clobbered3' => array(0, 1, 2) + ) + ); + + // array_replace() + $this->assertEquals( + array( + 0 => 'dontclobber', + 1 => 'clobbered', + 'test2' => 0.0, + 'test3' => array( + 'testarray2' => FALSE + ), + 'test4' => array( + 'clobbered3' => array(0, 1, 2) + ) + ), + array_replace($array1, $array2) + ); + + // array_replace_recursive() + $this->assertEquals( + array( + 0 => 'dontclobber', + 1 => 'clobbered', + 'test2' => 0.0, + 'test3' => array( + 'testarray2' => FALSE, + 1 => array( + 'testsubarray1' => 'dontclobber2', + 'testsubarray2' => 'dontclobber3' + ) + ), + 'test4' => array( + 'clobbered3' => array(0, 1, 2) + ) + ), + array_replace_recursive($array1, $array2) + ); + } +} + +// ------------------------------------------------------------------------ + +// These are necessary for the array_column() tests + +class Foo { + + public function __toString() + { + return 'last_name'; + } +} + +class Bar { + + public function __toString() + { + return 'first_name'; + } +}
\ No newline at end of file diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst index 9ec4d0d77..ccbb950af 100644 --- a/user_guide_src/source/changelog.rst +++ b/user_guide_src/source/changelog.rst @@ -508,7 +508,13 @@ Release Date: Not Released - Changed method ``clean_string()`` to utilize ``mb_convert_encoding()`` if it is available but ``iconv()`` is not. - Renamed method ``_is_ascii()`` to ``is_ascii()`` and made it public. - - Added `compatibility layers <general/compatibility_functions>` for PHP's `mbstring <http://php.net/mbstring>`_ (limited support), `hash <http://php.net/hash>`_ and `password <http://php.net/password>`_ extensions. + - Added `compatibility layers <general/compatibility_functions>` for: + + - `Multibyte String <http://php.net/mbstring>`_ (limited support). + - `Hash <http://php.net/hash>`_ (just ``hash_pbkdf2()``). + - `Password Hashing <http://php.net/password>`_. + - `Array Functions <http://php.net/book.array>`_ (``array_column()``, ``array_replace()``, ``array_replace_recursive()``). + - Removed ``CI_CORE`` boolean constant from *CodeIgniter.php* (no longer Reactor and Core versions). - Log Library will now try to create the **log_path** directory if it doesn't exist. - Added support for HTTP-Only cookies with new config option *cookie_httponly* (default FALSE). diff --git a/user_guide_src/source/general/compatibility_functions.rst b/user_guide_src/source/general/compatibility_functions.rst index 3495101ac..398403eda 100644 --- a/user_guide_src/source/general/compatibility_functions.rst +++ b/user_guide_src/source/general/compatibility_functions.rst @@ -183,4 +183,54 @@ Function reference :rtype: string For more information, please refer to the `PHP manual for - mb_substr() <http://php.net/mb_substr>`_.
\ No newline at end of file + mb_substr() <http://php.net/mb_substr>`_. + +*************** +Array Functions +*************** + +This set of compatibility functions offers support for a few +standard `Array Functions <http://php.net/book.array>`_ in PHP +that otherwise require a newer PHP version. + +Dependancies +============ + +- None + +Function reference +================== + +.. function:: array_column(array $array, $column_key[, $index_key = NULL]) + + :param array $array: Array to fetch results from + :param mixed $column_key: Key of the column to return values from + :param mixed $index_key: Key to use for the returned values + :returns: An array of values representing a single column from the input array + :rtype: array + + For more information, please refer to the `PHP manual for + array_column() <http://php.net/array_column>`_. + +.. function:: array_replace(array $array1[, ...]) + + :param array $array1: Array in which to replace elements + :param array ...: Array (or multiple ones) from which to extract elements + :returns: Modified array + :rtype: array + + For more information, please refer to the `PHP manual for + array_replace() <http://php.net/array_replace>`_. + +.. function:: array_replace_recursive(array $array1[, ...]) + + :param array $array1: Array in which to replace elements + :param array ...: Array (or multiple ones) from which to extract elements + :returns: Modified array + :rtype: array + + For more information, please refer to the `PHP manual for + array_replace_recursive() <http://php.net/array_replace_recursive>`_. + + .. important:: Only PHP's native function can detect endless recursion. + Unless you are running PHP 5.3+, be careful with references!
\ No newline at end of file |