diff options
93 files changed, 7411 insertions, 107 deletions
@@ -1,5 +1,35 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +06 Jun 2013, PHP 5.5.0 Release Candidate 3 + +- Core: + . Fixed bug #64960 (Segfault in gc_zval_possible_root). (Laruence) + . Fixed bug #64879 (Heap based buffer overflow in quoted_printable_encode, + CVE 2013-2110). (Stas) + +- FPM: + . Fixed Bug #64915 (error_log ignored when daemonize=0). (Remi) + +- GD: + . Fixed Bug #64962 (imagerotate produces corrupted image). (Remi) + . Fixed Bug #64961 (segfault in imagesetinterpolation). (Remi) + +- Hash: + . Fixed Bug #64745 (hash_pbkdf2() truncates data when using default length + and hex output). (Anthony Ferrara) + +- PDO_DBlib: + . Fixed bug #63638 (Cannot connect to SQL Server 2008 with PDO dblib). + (Stanley Sufficool) + . Fixed bug #64338 (pdo_dblib can't connect to Azure SQL). (Stanley + Sufficool) + . Fixed bug #64808 (FreeTDS PDO getColumnMeta on a prepared but not executed + statement crashes). (Stanley Sufficool) + +- PDO_pgsql: + . Fixed Bug #64949 (Buffer overflow in _pdo_pgsql_error). (Remi) + + 23 May 2013, PHP 5.5.0 Release Candidate 2 - Core: diff --git a/Zend/tests/bug64960.phpt b/Zend/tests/bug64960.phpt new file mode 100644 index 000000000..b31cca3dc --- /dev/null +++ b/Zend/tests/bug64960.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #64960 (Segfault in gc_zval_possible_root) +--FILE-- +<?php +// this makes ob_end_clean raise an error +ob_end_flush(); + +class ExceptionHandler { + public function __invoke (Exception $e) + { + // this triggers the custom error handler + ob_end_clean(); + } +} + +// this must be a class, closure does not trigger segfault +set_exception_handler(new ExceptionHandler()); + +// exception must be throwed from error handler. +set_error_handler(function() +{ + $e = new Exception; + $e->_trace = debug_backtrace(); + + throw $e; +}); + +// trigger error handler +$a['waa']; +?> +--EXPECTF-- +Notice: ob_end_flush(): failed to delete and flush buffer. No buffer to delete or flush in %sbug64960.php on line 3 + +Fatal error: Uncaught exception 'Exception' in %sbug64960.php:19 +Stack trace: +#0 [internal function]: {closure}(8, 'ob_end_clean():...', '%s', 9, Array) +#1 %sbug64960.php(9): ob_end_clean() +#2 [internal function]: ExceptionHandler->__invoke(Object(Exception)) +#3 {main} + thrown in %sbug64960.php on line 19 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 862f82da0..8674f9247 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5687,7 +5687,7 @@ void zend_do_shell_exec(znode *result, const znode *cmd TSRMLS_DC) /* {{{ */ break; } SET_NODE(opline->op1, cmd); - opline->op2.opline_num = 0; + opline->op2.opline_num = 1; opline->extended_value = ZEND_DO_FCALL; SET_UNUSED(opline->op2); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 8739e21c2..d831b107a 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -254,15 +254,13 @@ void shutdown_executor(TSRMLS_D) /* {{{ */ if (EG(user_error_handler)) { zeh = EG(user_error_handler); EG(user_error_handler) = NULL; - zval_dtor(zeh); - FREE_ZVAL(zeh); + zval_ptr_dtor(&zeh); } if (EG(user_exception_handler)) { zeh = EG(user_exception_handler); EG(user_exception_handler) = NULL; - zval_dtor(zeh); - FREE_ZVAL(zeh); + zval_ptr_dtor(&zeh); } zend_stack_destroy(&EG(user_error_handlers_error_reporting)); @@ -3687,7 +3687,7 @@ ac_config_headers="$ac_config_headers main/php_config.h" PHP_MAJOR_VERSION=5 PHP_MINOR_VERSION=5 PHP_RELEASE_VERSION=0 -PHP_EXTRA_VERSION="RC2" +PHP_EXTRA_VERSION="RC3" PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION" PHP_VERSION_ID=`expr $PHP_MAJOR_VERSION \* 10000 + $PHP_MINOR_VERSION \* 100 + $PHP_RELEASE_VERSION` diff --git a/configure.in b/configure.in index 0a6bc5088..ed4afb9e4 100644 --- a/configure.in +++ b/configure.in @@ -120,7 +120,7 @@ int zend_sprintf(char *buffer, const char *format, ...); PHP_MAJOR_VERSION=5 PHP_MINOR_VERSION=5 PHP_RELEASE_VERSION=0 -PHP_EXTRA_VERSION="RC2" +PHP_EXTRA_VERSION="RC3" PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION" PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION` diff --git a/ext/fileinfo/tests/resources/test.mp3 b/ext/fileinfo/tests/resources/test.mp3 Binary files differindex 0b534374b..ff6dc3ad7 100644 --- a/ext/fileinfo/tests/resources/test.mp3 +++ b/ext/fileinfo/tests/resources/test.mp3 diff --git a/ext/gd/gd.c b/ext/gd/gd.c index d6d2848d4..73434307c 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -5212,7 +5212,7 @@ PHP_FUNCTION(imageaffine) PHP_FUNCTION(imageaffinematrixget) { double affine[6]; - gdAffineStandardMatrix type; + long type; zval *options; zval **tmp; int res = GD_FALSE, i; @@ -5221,7 +5221,7 @@ PHP_FUNCTION(imageaffinematrixget) return; } - switch(type) { + switch((gdAffineStandardMatrix)type) { case GD_AFFINE_TRANSLATE: case GD_AFFINE_SCALE: { double x, y; @@ -5271,7 +5271,7 @@ PHP_FUNCTION(imageaffinematrixget) } default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", type); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %li", type); RETURN_FALSE; } @@ -5361,7 +5361,7 @@ PHP_FUNCTION(imagesetinterpolation) { zval *IM; gdImagePtr im; - gdInterpolationMethod method = GD_BILINEAR_FIXED; + long method = GD_BILINEAR_FIXED; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &IM, &method) == FAILURE) { return; diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c index 6c5549eba..9652a3a18 100644 --- a/ext/gd/libgd/gd_interpolation.c +++ b/ext/gd/libgd/gd_interpolation.c @@ -1690,8 +1690,8 @@ gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, co unsigned int j; dst_offset_x = 0; for (j = 0; j < new_width; j++) { - gdFixed f_i = gd_itofx(i - new_height/2); - gdFixed f_j = gd_itofx(j-new_width/2); + gdFixed f_i = gd_itofx((int)i - (int)new_height/2); + gdFixed f_j = gd_itofx((int)j - (int)new_width/2); gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; long m = gd_fxtoi(f_m); @@ -1753,8 +1753,8 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b unsigned int j; dst_offset_x = 0; for (j = 0; j < new_width; j++) { - gdFixed f_i = gd_itofx(i - new_height/ 2); - gdFixed f_j = gd_itofx(j -new_width / 2); + gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2); + gdFixed f_j = gd_itofx((int)j - (int)new_width / 2); gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; long m = gd_fxtoi(f_m); @@ -1814,8 +1814,8 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int dst_offset_x = 0; for (j=0; j < new_width; j++) { - const gdFixed f_i = gd_itofx(i-new_height/2); - const gdFixed f_j = gd_itofx(j-new_width/2); + const gdFixed f_i = gd_itofx((int)i - (int)new_height/2); + const gdFixed f_j = gd_itofx((int)j - (int)new_width/2); const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; const unsigned int m = gd_fxtoi(f_m); @@ -1830,18 +1830,18 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int const gdFixed f_w4 = gd_mulfx(f_f, f_g); if (n < src_w - 1) { - src_offset_x = m + 1; - src_offset_y = n; + src_offset_x = n + 1; + src_offset_y = m; } if (m < src_h-1) { - src_offset_x = m; - src_offset_y = n + 1; + src_offset_x = n; + src_offset_y = m + 1; } if (!((n >= src_w-1) || (m >= src_h-1))) { - src_offset_x = m + 1; - src_offset_y = n + 1; + src_offset_x = n + 1; + src_offset_y = m + 1; } { const int pixel1 = src->tpixels[src_offset_y][src_offset_x]; @@ -1941,8 +1941,8 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const dst_offset_x = 0; for (j=0; j < new_width; j++) { - const gdFixed f_i = gd_itofx(i-new_height/2); - const gdFixed f_j = gd_itofx(j-new_width/2); + const gdFixed f_i = gd_itofx((int)i - (int)new_height/2); + const gdFixed f_j = gd_itofx((int)j - (int)new_width/2); const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; const int m = gd_fxtoi(f_m); diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 9492387db..9cede1412 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -659,6 +659,9 @@ PHP_FUNCTION(hash_pbkdf2) /* Setup Main Loop to build a long enough result */ if (length == 0) { length = ops->digest_size; + if (!raw_output) { + length = length * 2; + } } digest_length = length; if (!raw_output) { diff --git a/ext/hash/tests/bug64745.phpt b/ext/hash/tests/bug64745.phpt new file mode 100644 index 000000000..427f89b72 --- /dev/null +++ b/ext/hash/tests/bug64745.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #64745 hash_pbkdf2() truncates data when using default length and hex output +--SKIPIF-- +<?php extension_loaded('hash') or die('skip'); ?> +--FILE-- +<?php +$hash = hash_pbkdf2('sha1', 'password', 'salt', 1, 0); +$rawHash = hash_pbkdf2('sha1', 'password', 'salt', 1, 0, true); + +var_dump($hash); +var_dump(bin2hex($rawHash)); + +?> +--EXPECT-- +string(40) "0c60c80f961f0e71f3a9b524af6012062fe037a6" +string(40) "0c60c80f961f0e71f3a9b524af6012062fe037a6" + diff --git a/ext/json/CREDITS b/ext/json/CREDITS new file mode 100644 index 000000000..9bd7f44f2 --- /dev/null +++ b/ext/json/CREDITS @@ -0,0 +1,2 @@ +JSON +Omar Kilani, Scott MacVicar diff --git a/ext/json/JSON_parser.c b/ext/json/JSON_parser.c new file mode 100644 index 000000000..dd832a7cb --- /dev/null +++ b/ext/json/JSON_parser.c @@ -0,0 +1,759 @@ +/* JSON_parser.c */ + +/* 2005-12-30 */ + +/* +Copyright (c) 2005 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include <stdio.h> +#include "JSON_parser.h" + +/* Windows defines IN for documentation */ +#undef IN + +#define true 1 +#define false 0 +#define __ -1 /* the universal error code */ + +/* + Characters are mapped into these 31 character classes. This allows for + a significant reduction in the size of the state transition table. +*/ + +enum classes { + C_SPACE, /* space */ + C_WHITE, /* other whitespace */ + C_LCURB, /* { */ + C_RCURB, /* } */ + C_LSQRB, /* [ */ + C_RSQRB, /* ] */ + C_COLON, /* : */ + C_COMMA, /* , */ + C_QUOTE, /* " */ + C_BACKS, /* \ */ + C_SLASH, /* / */ + C_PLUS, /* + */ + C_MINUS, /* - */ + C_POINT, /* . */ + C_ZERO , /* 0 */ + C_DIGIT, /* 123456789 */ + C_LOW_A, /* a */ + C_LOW_B, /* b */ + C_LOW_C, /* c */ + C_LOW_D, /* d */ + C_LOW_E, /* e */ + C_LOW_F, /* f */ + C_LOW_L, /* l */ + C_LOW_N, /* n */ + C_LOW_R, /* r */ + C_LOW_S, /* s */ + C_LOW_T, /* t */ + C_LOW_U, /* u */ + C_ABCDF, /* ABCDF */ + C_E, /* E */ + C_ETC, /* everything else */ + NR_CLASSES +}; + +static const int ascii_class[128] = { +/* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. +*/ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC +}; + + +/* + The state codes. +*/ +enum states { + GO, /* start */ + OK, /* ok */ + OB, /* object */ + KE, /* key */ + CO, /* colon */ + VA, /* value */ + AR, /* array */ + ST, /* string */ + ES, /* escape */ + U1, /* u1 */ + U2, /* u2 */ + U3, /* u3 */ + U4, /* u4 */ + MI, /* minus */ + ZE, /* zero */ + IN, /* integer */ + FR, /* fraction */ + E1, /* e */ + E2, /* ex */ + E3, /* exp */ + T1, /* tr */ + T2, /* tru */ + T3, /* true */ + F1, /* fa */ + F2, /* fal */ + F3, /* fals */ + F4, /* false */ + N1, /* nu */ + N2, /* nul */ + N3, /* null */ + NR_STATES +}; + + +static const int state_transition_table[NR_STATES][NR_CLASSES] = { +/* + The state transition table takes the current state and the current symbol, + and returns either a new state or an action. An action is represented as a + negative number. A JSON text is accepted if at the end of the text the + state is OK and if the mode is MODE_DONE. + + white 1-9 ABCDF etc + space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E |*/ +/*start GO*/ {GO,GO,-6,__,-5,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*ok OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*object OB*/ {OB,OB,__,-9,__,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*key KE*/ {KE,KE,__,__,__,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*colon CO*/ {CO,CO,__,__,__,__,-2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*value VA*/ {VA,VA,-6,__,-5,__,__,__,ST,__,__,__,MI,__,ZE,IN,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__}, +/*array AR*/ {AR,AR,-6,__,-5,-7,__,__,ST,__,__,__,MI,__,ZE,IN,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__}, +/*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,ES,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST}, +/*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__}, +/*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__}, +/*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__}, +/*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__}, +/*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ST,ST,ST,ST,ST,ST,ST,ST,__,__,__,__,__,__,ST,ST,__}, +/*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*zero ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,FR,__,__,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, +/*int IN*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,FR,IN,IN,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, +/*frac FR*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, +/*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*exp E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__}, +/*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__}, +/*true T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__}, +/*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__}, +/*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__}, +/*false F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__}, +/*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__}, +/*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__}, +/*null N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__}, +}; + + +/* + These modes can be pushed on the stack. +*/ +enum modes { + MODE_ARRAY, + MODE_DONE, + MODE_KEY, + MODE_OBJECT, +}; + + +/* + Push a mode onto the stack. Return false if there is overflow. +*/ +static int +push(JSON_parser jp, int mode) +{ + jp->top += 1; + if (jp->top >= jp->depth) { + jp->error_code = PHP_JSON_ERROR_DEPTH; + return false; + } + jp->stack[jp->top] = mode; + return true; +} + + +/* + Pop the stack, assuring that the current mode matches the expectation. + Return false if there is underflow or if the modes mismatch. +*/ +static int +pop(JSON_parser jp, int mode) +{ + if (jp->top < 0 || jp->stack[jp->top] != mode) { + jp->error_code = PHP_JSON_ERROR_STATE_MISMATCH; + return false; + } + jp->top -= 1; + return true; +} + +/* + new_JSON_checker starts the checking process by constructing a JSON_checker + object. It takes a depth parameter that restricts the level of maximum + nesting. + + To continue the process, call JSON_checker_char for each character in the + JSON text, and then call JSON_checker_done to obtain the final result. + These functions are fully reentrant. + + The JSON_checker object will be deleted by JSON_checker_done. + JSON_checker_char will delete the JSON_checker object if it sees an error. +*/ +JSON_parser +new_JSON_parser(int depth) +{ + JSON_parser jp = (JSON_parser)emalloc(sizeof(struct JSON_parser_struct)); + jp->state = GO; + jp->depth = depth; + jp->top = -1; + jp->error_code = PHP_JSON_ERROR_NONE; + jp->stack = (int*)ecalloc(depth, sizeof(int)); + if (depth > JSON_PARSER_DEFAULT_DEPTH) { + jp->the_zstack = (zval **)safe_emalloc(depth, sizeof(zval), 0); + } else { + jp->the_zstack = &jp->the_static_zstack[0]; + } + push(jp, MODE_DONE); + return jp; +} + +/* + Delete the JSON_parser object. +*/ +int +free_JSON_parser(JSON_parser jp) +{ + efree((void*)jp->stack); + if (jp->the_zstack != &jp->the_static_zstack[0]) { + efree(jp->the_zstack); + } + efree((void*)jp); + return false; +} + +static int dehexchar(char c) +{ + if (c >= '0' && c <= '9') + { + return c - '0'; + } + else if (c >= 'A' && c <= 'F') + { + return c - ('A' - 10); + } + else if (c >= 'a' && c <= 'f') + { + return c - ('a' - 10); + } + else + { + return -1; + } +} + + +static void json_create_zval(zval **z, smart_str *buf, int type, int options) +{ + ALLOC_INIT_ZVAL(*z); + + if (type == IS_LONG) + { + zend_bool bigint = 0; + + if (buf->c[0] == '-') { + buf->len--; + } + + if (buf->len >= MAX_LENGTH_OF_LONG - 1) { + if (buf->len == MAX_LENGTH_OF_LONG - 1) { + int cmp = strcmp(buf->c + (buf->c[0] == '-'), long_min_digits); + + if (!(cmp < 0 || (cmp == 0 && buf->c[0] == '-'))) { + bigint = 1; + } + } else { + bigint = 1; + } + } + + if (bigint) { + /* value too large to represent as a long */ + if (options & PHP_JSON_BIGINT_AS_STRING) { + if (buf->c[0] == '-') { + /* Restore last char consumed above */ + buf->len++; + } + goto use_string; + } else { + goto use_double; + } + } + + ZVAL_LONG(*z, strtol(buf->c, NULL, 10)); + } + else if (type == IS_DOUBLE) + { +use_double: + ZVAL_DOUBLE(*z, zend_strtod(buf->c, NULL)); + } + else if (type == IS_STRING) + { +use_string: + ZVAL_STRINGL(*z, buf->c, buf->len, 1); + } + else if (type == IS_BOOL) + { + ZVAL_BOOL(*z, (*(buf->c) == 't')); + } + else /* type == IS_NULL) || type unknown */ + { + ZVAL_NULL(*z); + } +} + + +static void utf16_to_utf8(smart_str *buf, unsigned short utf16) +{ + if (utf16 < 0x80) + { + smart_str_appendc(buf, (unsigned char) utf16); + } + else if (utf16 < 0x800) + { + smart_str_appendc(buf, 0xc0 | (utf16 >> 6)); + smart_str_appendc(buf, 0x80 | (utf16 & 0x3f)); + } + else if ((utf16 & 0xfc00) == 0xdc00 + && buf->len >= 3 + && ((unsigned char) buf->c[buf->len - 3]) == 0xed + && ((unsigned char) buf->c[buf->len - 2] & 0xf0) == 0xa0 + && ((unsigned char) buf->c[buf->len - 1] & 0xc0) == 0x80) + { + /* found surrogate pair */ + unsigned long utf32; + + utf32 = (((buf->c[buf->len - 2] & 0xf) << 16) + | ((buf->c[buf->len - 1] & 0x3f) << 10) + | (utf16 & 0x3ff)) + 0x10000; + buf->len -= 3; + + smart_str_appendc(buf, (unsigned char) (0xf0 | (utf32 >> 18))); + smart_str_appendc(buf, 0x80 | ((utf32 >> 12) & 0x3f)); + smart_str_appendc(buf, 0x80 | ((utf32 >> 6) & 0x3f)); + smart_str_appendc(buf, 0x80 | (utf32 & 0x3f)); + } + else + { + smart_str_appendc(buf, 0xe0 | (utf16 >> 12)); + smart_str_appendc(buf, 0x80 | ((utf16 >> 6) & 0x3f)); + smart_str_appendc(buf, 0x80 | (utf16 & 0x3f)); + } +} + +static void attach_zval(JSON_parser jp, int up, int cur, smart_str *key, int assoc TSRMLS_DC) +{ + zval *root = jp->the_zstack[up]; + zval *child = jp->the_zstack[cur]; + int up_mode = jp->stack[up]; + + if (up_mode == MODE_ARRAY) + { + add_next_index_zval(root, child); + } + else if (up_mode == MODE_OBJECT) + { + if (!assoc) + { + add_property_zval_ex(root, (key->len ? key->c : "_empty_"), (key->len ? (key->len + 1) : sizeof("_empty_")), child TSRMLS_CC); + Z_DELREF_P(child); + } + else + { + add_assoc_zval_ex(root, (key->len ? key->c : ""), (key->len ? (key->len + 1) : sizeof("")), child); + } + key->len = 0; + } +} + + +#define FREE_BUFFERS() smart_str_free(&buf); smart_str_free(&key); +#define SWAP_BUFFERS(from, to) do { \ + char *t1 = from.c; \ + int t2 = from.a; \ + from.c = to.c; \ + from.a = to.a; \ + to.c = t1; \ + to.a = t2; \ + to.len = from.len; \ + from.len = 0; \ + } while(0); +#define JSON_RESET_TYPE() type = -1; + +/* + The JSON_parser takes a UTF-16 encoded string and determines if it is a + syntactically correct JSON text. Along the way, it creates a PHP variable. + + It is implemented as a Pushdown Automaton; that means it is a finite state + machine with a stack. +*/ +int +parse_JSON_ex(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int options TSRMLS_DC) +{ + int next_char; /* the next character */ + int next_class; /* the next character class */ + int next_state; /* the next state */ + int the_index; + int assoc = options & PHP_JSON_OBJECT_AS_ARRAY; + + smart_str buf = {0}; + smart_str key = {0}; + + unsigned short utf16 = 0; + int type; + + JSON_RESET_TYPE(); + + for (the_index = 0; the_index < length; the_index += 1) { + next_char = utf16_json[the_index]; + if (next_char >= 128) { + next_class = C_ETC; + } else { + next_class = ascii_class[next_char]; + if (next_class <= __) { + jp->error_code = PHP_JSON_ERROR_CTRL_CHAR; + FREE_BUFFERS(); + return false; + } + } +/* + Get the next state from the transition table. +*/ + next_state = state_transition_table[jp->state][next_class]; + if (next_state >= 0) { +/* + Change the state and iterate +*/ + if (type == IS_STRING) { + if (next_state == ST && jp->state != U4) { + if (jp->state != ES) { + utf16_to_utf8(&buf, next_char); + } else { + switch (next_char) { + case 'b': + smart_str_appendc(&buf, '\b'); + break; + case 't': + smart_str_appendc(&buf, '\t'); + break; + case 'n': + smart_str_appendc(&buf, '\n'); + break; + case 'f': + smart_str_appendc(&buf, '\f'); + break; + case 'r': + smart_str_appendc(&buf, '\r'); + break; + default: + utf16_to_utf8(&buf, next_char); + break; + } + } + } else if (next_state == U2) { + utf16 = dehexchar(next_char) << 12; + } else if (next_state == U3) { + utf16 += dehexchar(next_char) << 8; + } else if (next_state == U4) { + utf16 += dehexchar(next_char) << 4; + } else if (next_state == ST && jp->state == U4) { + utf16 += dehexchar(next_char); + utf16_to_utf8(&buf, utf16); + } + } else if (type < IS_LONG && (next_class == C_DIGIT || next_class == C_ZERO)) { + type = IS_LONG; + smart_str_appendc(&buf, next_char); + } else if (type == IS_LONG && next_state == E1) { + type = IS_DOUBLE; + smart_str_appendc(&buf, next_char); + } else if (type < IS_DOUBLE && next_class == C_POINT) { + type = IS_DOUBLE; + smart_str_appendc(&buf, next_char); + } else if (type < IS_STRING && next_class == C_QUOTE) { + type = IS_STRING; + } else if (type < IS_BOOL && ((jp->state == T3 && next_state == OK) || (jp->state == F4 && next_state == OK))) { + type = IS_BOOL; + } else if (type < IS_NULL && jp->state == N3 && next_state == OK) { + type = IS_NULL; + } else if (type != IS_STRING && next_class > C_WHITE) { + utf16_to_utf8(&buf, next_char); + } + jp->state = next_state; + } else { +/* + Perform one of the predefined actions. +*/ + switch (next_state) { +/* empty } */ + case -9: + if (!pop(jp, MODE_KEY)) { + FREE_BUFFERS(); + return false; + } + jp->state = OK; + break; +/* } */ + case -8: + if (type != -1 && jp->stack[jp->top] == MODE_OBJECT) + { + zval *mval; + smart_str_0(&buf); + + json_create_zval(&mval, &buf, type, options); + + if (!assoc) { + add_property_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : "_empty_"), (key.len ? (key.len + 1) : sizeof("_empty_")), mval TSRMLS_CC); + Z_DELREF_P(mval); + } else { + add_assoc_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : ""), (key.len ? (key.len + 1) : sizeof("")), mval); + } + key.len = 0; + buf.len = 0; + JSON_RESET_TYPE(); + } + + + if (!pop(jp, MODE_OBJECT)) { + FREE_BUFFERS(); + return false; + } + jp->state = OK; + break; +/* ] */ + case -7: + { + if (type != -1 && jp->stack[jp->top] == MODE_ARRAY) + { + zval *mval; + smart_str_0(&buf); + + json_create_zval(&mval, &buf, type, options); + add_next_index_zval(jp->the_zstack[jp->top], mval); + buf.len = 0; + JSON_RESET_TYPE(); + } + + if (!pop(jp, MODE_ARRAY)) { + FREE_BUFFERS(); + return false; + } + jp->state = OK; + } + break; +/* { */ + case -6: + if (!push(jp, MODE_KEY)) { + FREE_BUFFERS(); + return false; + } + + jp->state = OB; + if (jp->top > 0) { + zval *obj; + + if (jp->top == 1) { + obj = z; + } else { + ALLOC_INIT_ZVAL(obj); + } + + if (!assoc) { + object_init(obj); + } else { + array_init(obj); + } + + jp->the_zstack[jp->top] = obj; + + if (jp->top > 1) { + attach_zval(jp, jp->top - 1, jp->top, &key, assoc TSRMLS_CC); + } + + JSON_RESET_TYPE(); + } + + break; +/* [ */ + case -5: + if (!push(jp, MODE_ARRAY)) { + FREE_BUFFERS(); + return false; + } + jp->state = AR; + + if (jp->top > 0) { + zval *arr; + + if (jp->top == 1) { + arr = z; + } else { + ALLOC_INIT_ZVAL(arr); + } + + array_init(arr); + jp->the_zstack[jp->top] = arr; + + if (jp->top > 1) { + attach_zval(jp, jp->top - 1, jp->top, &key, assoc TSRMLS_CC); + } + + JSON_RESET_TYPE(); + } + + break; + +/* " */ + case -4: + switch (jp->stack[jp->top]) { + case MODE_KEY: + jp->state = CO; + smart_str_0(&buf); + SWAP_BUFFERS(buf, key); + JSON_RESET_TYPE(); + break; + case MODE_ARRAY: + case MODE_OBJECT: + jp->state = OK; + break; + case MODE_DONE: + if (type == IS_STRING) { + smart_str_0(&buf); + ZVAL_STRINGL(z, buf.c, buf.len, 1); + jp->state = OK; + break; + } + /* fall through if not IS_STRING */ + default: + FREE_BUFFERS(); + jp->error_code = PHP_JSON_ERROR_SYNTAX; + return false; + } + break; +/* , */ + case -3: + { + zval *mval; + + if (type != -1 && + (jp->stack[jp->top] == MODE_OBJECT || + jp->stack[jp->top] == MODE_ARRAY)) + { + smart_str_0(&buf); + json_create_zval(&mval, &buf, type, options); + } + + switch (jp->stack[jp->top]) { + case MODE_OBJECT: + if (pop(jp, MODE_OBJECT) && push(jp, MODE_KEY)) { + if (type != -1) { + if (!assoc) { + add_property_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : "_empty_"), (key.len ? (key.len + 1) : sizeof("_empty_")), mval TSRMLS_CC); + Z_DELREF_P(mval); + } else { + add_assoc_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : ""), (key.len ? (key.len + 1) : sizeof("")), mval); + } + key.len = 0; + } + jp->state = KE; + } + break; + case MODE_ARRAY: + if (type != -1) { + add_next_index_zval(jp->the_zstack[jp->top], mval); + } + jp->state = VA; + break; + default: + FREE_BUFFERS(); + jp->error_code = PHP_JSON_ERROR_SYNTAX; + return false; + } + buf.len = 0; + JSON_RESET_TYPE(); + } + break; +/* : */ + case -2: + if (pop(jp, MODE_KEY) && push(jp, MODE_OBJECT)) { + jp->state = VA; + break; + } +/* + syntax error +*/ + default: + { + jp->error_code = PHP_JSON_ERROR_SYNTAX; + FREE_BUFFERS(); + return false; + } + } + } + } + + FREE_BUFFERS(); + if (jp->state == OK && pop(jp, MODE_DONE)) { + return true; + } + + jp->error_code = PHP_JSON_ERROR_SYNTAX; + return false; +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/json/JSON_parser.h b/ext/json/JSON_parser.h new file mode 100644 index 000000000..8671765b4 --- /dev/null +++ b/ext/json/JSON_parser.h @@ -0,0 +1,43 @@ +/* JSON_parser.h */ + +#ifndef JSON_PARSER_H +#define JSON_PARSER_H + +#include "php.h" +#include "ext/standard/php_smart_str.h" +#include "php_json.h" + +#define JSON_PARSER_DEFAULT_DEPTH 512 + +typedef struct JSON_parser_struct { + int state; + int depth; + int top; + int error_code; + int* stack; + zval **the_zstack; + zval *the_static_zstack[JSON_PARSER_DEFAULT_DEPTH]; +} * JSON_parser; + +enum error_codes { + PHP_JSON_ERROR_NONE = 0, + PHP_JSON_ERROR_DEPTH, + PHP_JSON_ERROR_STATE_MISMATCH, + PHP_JSON_ERROR_CTRL_CHAR, + PHP_JSON_ERROR_SYNTAX, + PHP_JSON_ERROR_UTF8, + PHP_JSON_ERROR_RECURSION, + PHP_JSON_ERROR_INF_OR_NAN, + PHP_JSON_ERROR_UNSUPPORTED_TYPE +}; + +extern JSON_parser new_JSON_parser(int depth); +extern int parse_JSON_ex(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int options TSRMLS_DC); +extern int free_JSON_parser(JSON_parser jp); + +static inline int parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC) +{ + return parse_JSON_ex(jp, z, utf16_json, length, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0 TSRMLS_CC); +} + +#endif diff --git a/ext/json/README b/ext/json/README new file mode 100644 index 000000000..d680b0c59 --- /dev/null +++ b/ext/json/README @@ -0,0 +1,76 @@ +json 1.2.0 +========== + +This extension implements the JavaScript Object Notation (JSON) +data-interchange format as specified in [0]. + +Two functions are implemented: encoding and decoding. The decoding +is handled by a parser based on JSON_checker[1] by Douglas Crockford. + + +Function overview +----------------- + + string json_encode ( mixed value ) + +json_encode returns a string containing the JSON representation of value. +value can be any type except a resource. + + mixed json_decode ( string json, [bool assoc] ) + +json_decode takes a JSON string and converts it into a PHP variable. +When assoc is given, and evaluates to TRUE, json_decode() will return +any objects as associative arrays. + + +Example usage +------------- + +$arr = array("a"=>1,"b"=>2,"c"=>3,"d"=>4,"e"=>5); +echo json_encode($arr); + +---> {"a":1,"b":2,"c":3,"d":4,"e":5} + +$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}'; +var_dump(json_decode($json)); + +---> object(stdClass)#1 (5) { + ["a"]=> + int(1) + ["b"]=> + int(2) + ["c"]=> + int(3) + ["d"]=> + int(4) + ["e"]=> + int(5) + } + +$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}'; +var_dump(json_decode($json, true)); + +---> array(5) { + ["a"]=> + int(1) + ["b"]=> + int(2) + ["c"]=> + int(3) + ["d"]=> + int(4) + ["e"]=> + int(5) + } + + +Authors +------- + +Omar Kilani <omar@php.net> + + +--- + +[0] http://www.crockford.com/JSON/draft-jsonorg-json-00.txt +[1] http://www.crockford.com/JSON/JSON_checker/ diff --git a/ext/json/config.m4 b/ext/json/config.m4 new file mode 100644 index 000000000..26c43a0e3 --- /dev/null +++ b/ext/json/config.m4 @@ -0,0 +1,15 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(json, whether to enable JavaScript Object Serialization support, +[ --disable-json Disable JavaScript Object Serialization support], yes) + +if test "$PHP_JSON" != "no"; then + AC_DEFINE([HAVE_JSON],1 ,[whether to enable JavaScript Object Serialization support]) + AC_HEADER_STDC + + PHP_NEW_EXTENSION(json, json.c utf8_decode.c JSON_parser.c, $ext_shared) + PHP_INSTALL_HEADERS([ext/json], [php_json.h]) + PHP_SUBST(JSON_SHARED_LIBADD) +fi diff --git a/ext/json/config.w32 b/ext/json/config.w32 new file mode 100644 index 000000000..cedbf4282 --- /dev/null +++ b/ext/json/config.w32 @@ -0,0 +1,11 @@ +// $Id$ +// vim:ft=javascript + +ARG_ENABLE("json", "JavaScript Object Serialization support", "yes"); + +if (PHP_JSON != "no") { + EXTENSION('json', 'json.c', PHP_JSON_SHARED, ""); + ADD_SOURCES(configure_module_dirname, "JSON_parser.c utf8_decode.c", "json"); + PHP_INSTALL_HEADERS("ext/json/", "php_json.h"); +} + diff --git a/ext/json/json.c b/ext/json/json.c new file mode 100644 index 000000000..53608412f --- /dev/null +++ b/ext/json/json.c @@ -0,0 +1,872 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Omar Kilani <omar@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/html.h" +#include "ext/standard/php_smart_str.h" +#include "JSON_parser.h" +#include "php_json.h" +#include <zend_exceptions.h> + +static PHP_MINFO_FUNCTION(json); +static PHP_FUNCTION(json_encode); +static PHP_FUNCTION(json_decode); +static PHP_FUNCTION(json_last_error); +static PHP_FUNCTION(json_last_error_msg); + +static const char digits[] = "0123456789abcdef"; + +zend_class_entry *php_json_serializable_ce; + +ZEND_DECLARE_MODULE_GLOBALS(json) + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, depth) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1) + ZEND_ARG_INFO(0, json) + ZEND_ARG_INFO(0, assoc) + ZEND_ARG_INFO(0, depth) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_json_last_error_msg, 0) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ json_functions[] */ +static const zend_function_entry json_functions[] = { + PHP_FE(json_encode, arginfo_json_encode) + PHP_FE(json_decode, arginfo_json_decode) + PHP_FE(json_last_error, arginfo_json_last_error) + PHP_FE(json_last_error_msg, arginfo_json_last_error_msg) + PHP_FE_END +}; +/* }}} */ + +/* {{{ JsonSerializable methods */ +ZEND_BEGIN_ARG_INFO(json_serialize_arginfo, 0) + /* No arguments */ +ZEND_END_ARG_INFO(); + +static const zend_function_entry json_serializable_interface[] = { + PHP_ABSTRACT_ME(JsonSerializable, jsonSerialize, json_serialize_arginfo) + PHP_FE_END +}; +/* }}} */ + +/* {{{ MINIT */ +static PHP_MINIT_FUNCTION(json) +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "JsonSerializable", json_serializable_interface); + php_json_serializable_ce = zend_register_internal_interface(&ce TSRMLS_CC); + + REGISTER_LONG_CONSTANT("JSON_HEX_TAG", PHP_JSON_HEX_TAG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_HEX_AMP", PHP_JSON_HEX_AMP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_UNESCAPED_SLASHES", PHP_JSON_UNESCAPED_SLASHES, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_PARTIAL_OUTPUT_ON_ERROR", PHP_JSON_PARTIAL_OUTPUT_ON_ERROR, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_RECURSION", PHP_JSON_ERROR_RECURSION, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_INF_OR_NAN", PHP_JSON_ERROR_INF_OR_NAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_UNSUPPORTED_TYPE", PHP_JSON_ERROR_UNSUPPORTED_TYPE, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("JSON_OBJECT_AS_ARRAY", PHP_JSON_OBJECT_AS_ARRAY, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_BIGINT_AS_STRING", PHP_JSON_BIGINT_AS_STRING, CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_GINIT_FUNCTION +*/ +static PHP_GINIT_FUNCTION(json) +{ + json_globals->encoder_depth = 0; + json_globals->error_code = 0; + json_globals->encode_max_depth = 0; +} +/* }}} */ + + +/* {{{ json_module_entry + */ +zend_module_entry json_module_entry = { + STANDARD_MODULE_HEADER, + "json", + json_functions, + PHP_MINIT(json), + NULL, + NULL, + NULL, + PHP_MINFO(json), + PHP_JSON_VERSION, + PHP_MODULE_GLOBALS(json), + PHP_GINIT(json), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; +/* }}} */ + +#ifdef COMPILE_DL_JSON +ZEND_GET_MODULE(json) +#endif + +/* {{{ PHP_MINFO_FUNCTION + */ +static PHP_MINFO_FUNCTION(json) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "json support", "enabled"); + php_info_print_table_row(2, "json version", PHP_JSON_VERSION); + php_info_print_table_end(); +} +/* }}} */ + +static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC); + +static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */ +{ + int i; + HashTable *myht = HASH_OF(*val); + + i = myht ? zend_hash_num_elements(myht) : 0; + if (i > 0) { + char *key; + ulong index, idx; + uint key_len; + HashPosition pos; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + idx = 0; + for (;; zend_hash_move_forward_ex(myht, &pos)) { + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if (i == HASH_KEY_NON_EXISTANT) { + break; + } + + if (i == HASH_KEY_IS_STRING) { + return 1; + } else { + if (index != idx) { + return 1; + } + } + idx++; + } + } + + return PHP_JSON_OUTPUT_ARRAY; +} +/* }}} */ + +/* {{{ Pretty printing support functions */ + +static inline void json_pretty_print_char(smart_str *buf, int options, char c TSRMLS_DC) /* {{{ */ +{ + if (options & PHP_JSON_PRETTY_PRINT) { + smart_str_appendc(buf, c); + } +} +/* }}} */ + +static inline void json_pretty_print_indent(smart_str *buf, int options TSRMLS_DC) /* {{{ */ +{ + int i; + + if (options & PHP_JSON_PRETTY_PRINT) { + for (i = 0; i < JSON_G(encoder_depth); ++i) { + smart_str_appendl(buf, " ", 4); + } + } +} +/* }}} */ + +/* }}} */ + +static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */ +{ + int i, r; + HashTable *myht; + + if (Z_TYPE_PP(val) == IS_ARRAY) { + myht = HASH_OF(*val); + r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC); + } else { + myht = Z_OBJPROP_PP(val); + r = PHP_JSON_OUTPUT_OBJECT; + } + + if (myht && myht->nApplyCount > 1) { + JSON_G(error_code) = PHP_JSON_ERROR_RECURSION; + smart_str_appendl(buf, "null", 4); + return; + } + + if (r == PHP_JSON_OUTPUT_ARRAY) { + smart_str_appendc(buf, '['); + } else { + smart_str_appendc(buf, '{'); + } + + json_pretty_print_char(buf, options, '\n' TSRMLS_CC); + ++JSON_G(encoder_depth); + + i = myht ? zend_hash_num_elements(myht) : 0; + + if (i > 0) + { + char *key; + zval **data; + ulong index; + uint key_len; + HashPosition pos; + HashTable *tmp_ht; + int need_comma = 0; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + for (;; zend_hash_move_forward_ex(myht, &pos)) { + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if (i == HASH_KEY_NON_EXISTANT) + break; + + if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) { + tmp_ht = HASH_OF(*data); + if (tmp_ht) { + tmp_ht->nApplyCount++; + } + + if (r == PHP_JSON_OUTPUT_ARRAY) { + if (need_comma) { + smart_str_appendc(buf, ','); + json_pretty_print_char(buf, options, '\n' TSRMLS_CC); + } else { + need_comma = 1; + } + + json_pretty_print_indent(buf, options TSRMLS_CC); + php_json_encode(buf, *data, options TSRMLS_CC); + } else if (r == PHP_JSON_OUTPUT_OBJECT) { + if (i == HASH_KEY_IS_STRING) { + if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) { + /* Skip protected and private members. */ + if (tmp_ht) { + tmp_ht->nApplyCount--; + } + continue; + } + + if (need_comma) { + smart_str_appendc(buf, ','); + json_pretty_print_char(buf, options, '\n' TSRMLS_CC); + } else { + need_comma = 1; + } + + json_pretty_print_indent(buf, options TSRMLS_CC); + + json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC); + smart_str_appendc(buf, ':'); + + json_pretty_print_char(buf, options, ' ' TSRMLS_CC); + + php_json_encode(buf, *data, options TSRMLS_CC); + } else { + if (need_comma) { + smart_str_appendc(buf, ','); + json_pretty_print_char(buf, options, '\n' TSRMLS_CC); + } else { + need_comma = 1; + } + + json_pretty_print_indent(buf, options TSRMLS_CC); + + smart_str_appendc(buf, '"'); + smart_str_append_long(buf, (long) index); + smart_str_appendc(buf, '"'); + smart_str_appendc(buf, ':'); + + json_pretty_print_char(buf, options, ' ' TSRMLS_CC); + + php_json_encode(buf, *data, options TSRMLS_CC); + } + } + + if (tmp_ht) { + tmp_ht->nApplyCount--; + } + } + } + } + + if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) { + JSON_G(error_code) = PHP_JSON_ERROR_DEPTH; + } + --JSON_G(encoder_depth); + json_pretty_print_char(buf, options, '\n' TSRMLS_CC); + json_pretty_print_indent(buf, options TSRMLS_CC); + + if (r == PHP_JSON_OUTPUT_ARRAY) { + smart_str_appendc(buf, ']'); + } else { + smart_str_appendc(buf, '}'); + } +} +/* }}} */ + +static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */ +{ + size_t pos = 0, us; + int j, status; + + if (utf16) { + /* really convert the utf8 string */ + for (j=0 ; pos < len ; j++) { + us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status); + if (status != SUCCESS) { + return -1; + } + /* From http://en.wikipedia.org/wiki/UTF16 */ + if (us >= 0x10000) { + us -= 0x10000; + utf16[j++] = (unsigned short)((us >> 10) | 0xd800); + utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00); + } else { + utf16[j] = (unsigned short)us; + } + } + } else { + /* Only check if utf8 string is valid, and compute utf16 lenght */ + for (j=0 ; pos < len ; j++) { + us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status); + if (status != SUCCESS) { + return -1; + } + if (us >= 0x10000) { + j++; + } + } + } + return j; +} +/* }}} */ + + +static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */ +{ + int pos = 0, ulen = 0; + unsigned short us; + unsigned short *utf16; + size_t newlen; + + if (len == 0) { + smart_str_appendl(buf, "\"\"", 2); + return; + } + + if (options & PHP_JSON_NUMERIC_CHECK) { + double d; + int type; + long p; + + if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) { + if (type == IS_LONG) { + smart_str_append_long(buf, p); + } else if (type == IS_DOUBLE) { + if (!zend_isinf(d) && !zend_isnan(d)) { + char *tmp; + int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d); + smart_str_appendl(buf, tmp, l); + efree(tmp); + } else { + JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN; + smart_str_appendc(buf, '0'); + } + } + return; + } + + } + + utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0); + ulen = json_utf8_to_utf16(utf16, s, len); + if (ulen <= 0) { + if (utf16) { + efree(utf16); + } + if (ulen < 0) { + JSON_G(error_code) = PHP_JSON_ERROR_UTF8; + smart_str_appendl(buf, "null", 4); + } else { + smart_str_appendl(buf, "\"\"", 2); + } + return; + } + if (!(options & PHP_JSON_UNESCAPED_UNICODE)) { + len = ulen; + } + + /* pre-allocate for string length plus 2 quotes */ + smart_str_alloc(buf, len+2, 0); + smart_str_appendc(buf, '"'); + + while (pos < len) + { + us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++]; + + switch (us) + { + case '"': + if (options & PHP_JSON_HEX_QUOT) { + smart_str_appendl(buf, "\\u0022", 6); + } else { + smart_str_appendl(buf, "\\\"", 2); + } + break; + + case '\\': + smart_str_appendl(buf, "\\\\", 2); + break; + + case '/': + if (options & PHP_JSON_UNESCAPED_SLASHES) { + smart_str_appendc(buf, '/'); + } else { + smart_str_appendl(buf, "\\/", 2); + } + break; + + case '\b': + smart_str_appendl(buf, "\\b", 2); + break; + + case '\f': + smart_str_appendl(buf, "\\f", 2); + break; + + case '\n': + smart_str_appendl(buf, "\\n", 2); + break; + + case '\r': + smart_str_appendl(buf, "\\r", 2); + break; + + case '\t': + smart_str_appendl(buf, "\\t", 2); + break; + + case '<': + if (options & PHP_JSON_HEX_TAG) { + smart_str_appendl(buf, "\\u003C", 6); + } else { + smart_str_appendc(buf, '<'); + } + break; + + case '>': + if (options & PHP_JSON_HEX_TAG) { + smart_str_appendl(buf, "\\u003E", 6); + } else { + smart_str_appendc(buf, '>'); + } + break; + + case '&': + if (options & PHP_JSON_HEX_AMP) { + smart_str_appendl(buf, "\\u0026", 6); + } else { + smart_str_appendc(buf, '&'); + } + break; + + case '\'': + if (options & PHP_JSON_HEX_APOS) { + smart_str_appendl(buf, "\\u0027", 6); + } else { + smart_str_appendc(buf, '\''); + } + break; + + default: + if (us >= ' ' && ((options & PHP_JSON_UNESCAPED_UNICODE) || (us & 127) == us)) { + smart_str_appendc(buf, (unsigned char) us); + } else { + smart_str_appendl(buf, "\\u", 2); + smart_str_appendc(buf, digits[(us & 0xf000) >> 12]); + smart_str_appendc(buf, digits[(us & 0xf00) >> 8]); + smart_str_appendc(buf, digits[(us & 0xf0) >> 4]); + smart_str_appendc(buf, digits[(us & 0xf)]); + } + break; + } + } + + smart_str_appendc(buf, '"'); + if (utf16) { + efree(utf16); + } +} +/* }}} */ + + +static void json_encode_serializable_object(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce = Z_OBJCE_P(val); + zval *retval = NULL, fname; + HashTable* myht; + + if (Z_TYPE_P(val) == IS_ARRAY) { + myht = HASH_OF(val); + } else { + myht = Z_OBJPROP_P(val); + } + + if (myht && myht->nApplyCount > 1) { + JSON_G(error_code) = PHP_JSON_ERROR_RECURSION; + smart_str_appendl(buf, "null", 4); + return; + } + + ZVAL_STRING(&fname, "jsonSerialize", 0); + + if (FAILURE == call_user_function_ex(EG(function_table), &val, &fname, &retval, 0, NULL, 1, NULL TSRMLS_CC) || !retval) { + zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed calling %s::jsonSerialize()", ce->name); + smart_str_appendl(buf, "null", sizeof("null") - 1); + return; + } + + if (EG(exception)) { + /* Error already raised */ + zval_ptr_dtor(&retval); + smart_str_appendl(buf, "null", sizeof("null") - 1); + return; + } + + if ((Z_TYPE_P(retval) == IS_OBJECT) && + (Z_OBJ_HANDLE_P(retval) == Z_OBJ_HANDLE_P(val))) { + /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */ + json_encode_array(buf, &retval, options TSRMLS_CC); + } else { + /* All other types, encode as normal */ + php_json_encode(buf, retval, options TSRMLS_CC); + } + + zval_ptr_dtor(&retval); +} +/* }}} */ + +PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */ +{ + switch (Z_TYPE_P(val)) + { + case IS_NULL: + smart_str_appendl(buf, "null", 4); + break; + + case IS_BOOL: + if (Z_BVAL_P(val)) { + smart_str_appendl(buf, "true", 4); + } else { + smart_str_appendl(buf, "false", 5); + } + break; + + case IS_LONG: + smart_str_append_long(buf, Z_LVAL_P(val)); + break; + + case IS_DOUBLE: + { + char *d = NULL; + int len; + double dbl = Z_DVAL_P(val); + + if (!zend_isinf(dbl) && !zend_isnan(dbl)) { + len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl); + smart_str_appendl(buf, d, len); + efree(d); + } else { + JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN; + smart_str_appendc(buf, '0'); + } + } + break; + + case IS_STRING: + json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC); + break; + + case IS_OBJECT: + if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce TSRMLS_CC)) { + json_encode_serializable_object(buf, val, options TSRMLS_CC); + break; + } + /* fallthrough -- Non-serializable object */ + case IS_ARRAY: + json_encode_array(buf, &val, options TSRMLS_CC); + break; + + default: + JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE; + smart_str_appendl(buf, "null", 4); + break; + } + + return; +} +/* }}} */ + +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */ +{ + int utf16_len; + zval *z; + unsigned short *utf16; + JSON_parser jp; + + utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1); + + utf16_len = json_utf8_to_utf16(utf16, str, str_len); + if (utf16_len <= 0) { + if (utf16) { + efree(utf16); + } + JSON_G(error_code) = PHP_JSON_ERROR_UTF8; + RETURN_NULL(); + } + + if (depth <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero"); + efree(utf16); + RETURN_NULL(); + } + + ALLOC_INIT_ZVAL(z); + jp = new_JSON_parser(depth); + if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) { + *return_value = *z; + } + else + { + double d; + int type, overflow_info; + long p; + + RETVAL_NULL(); + if (str_len == 4) { + if (!strcasecmp(str, "null")) { + /* We need to explicitly clear the error because its an actual NULL and not an error */ + jp->error_code = PHP_JSON_ERROR_NONE; + RETVAL_NULL(); + } else if (!strcasecmp(str, "true")) { + RETVAL_BOOL(1); + } + } else if (str_len == 5 && !strcasecmp(str, "false")) { + RETVAL_BOOL(0); + } + + if ((type = is_numeric_string_ex(str, str_len, &p, &d, 0, &overflow_info)) != 0) { + if (type == IS_LONG) { + RETVAL_LONG(p); + } else if (type == IS_DOUBLE) { + if (options & PHP_JSON_BIGINT_AS_STRING && overflow_info) { + /* Within an object or array, a numeric literal is assumed + * to be an integer if and only if it's entirely made up of + * digits (exponent notation will result in the number + * being treated as a double). We'll match that behaviour + * here. */ + int i; + zend_bool is_float = 0; + + for (i = (str[0] == '-' ? 1 : 0); i < str_len; i++) { + /* Not using isdigit() because it's locale specific, + * but we expect JSON input to always be UTF-8. */ + if (str[i] < '0' || str[i] > '9') { + is_float = 1; + break; + } + } + + if (is_float) { + RETVAL_DOUBLE(d); + } else { + RETVAL_STRINGL(str, str_len, 1); + } + } else { + RETVAL_DOUBLE(d); + } + } + } + + if (Z_TYPE_P(return_value) != IS_NULL) { + jp->error_code = PHP_JSON_ERROR_NONE; + } + + zval_dtor(z); + } + FREE_ZVAL(z); + efree(utf16); + JSON_G(error_code) = jp->error_code; + free_JSON_parser(jp); +} +/* }}} */ + + +/* {{{ proto string json_encode(mixed data [, int options[, int depth]]) + Returns the JSON representation of a value */ +static PHP_FUNCTION(json_encode) +{ + zval *parameter; + smart_str buf = {0}; + long options = 0; + long depth = JSON_PARSER_DEFAULT_DEPTH; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", ¶meter, &options, &depth) == FAILURE) { + return; + } + + JSON_G(error_code) = PHP_JSON_ERROR_NONE; + + JSON_G(encode_max_depth) = depth; + + php_json_encode(&buf, parameter, options TSRMLS_CC); + + if (JSON_G(error_code) != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { + ZVAL_FALSE(return_value); + } else { + ZVAL_STRINGL(return_value, buf.c, buf.len, 1); + } + + smart_str_free(&buf); +} +/* }}} */ + +/* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]]) + Decodes the JSON representation into a PHP value */ +static PHP_FUNCTION(json_decode) +{ + char *str; + int str_len; + zend_bool assoc = 0; /* return JS objects as PHP objects by default */ + long depth = JSON_PARSER_DEFAULT_DEPTH; + long options = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) { + return; + } + + JSON_G(error_code) = 0; + + if (!str_len) { + RETURN_NULL(); + } + + /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ + if (assoc) { + options |= PHP_JSON_OBJECT_AS_ARRAY; + } else { + options &= ~PHP_JSON_OBJECT_AS_ARRAY; + } + + php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto int json_last_error() + Returns the error code of the last json_encode() or json_decode() call. */ +static PHP_FUNCTION(json_last_error) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + RETURN_LONG(JSON_G(error_code)); +} +/* }}} */ + +/* {{{ proto string json_last_error_msg() + Returns the error string of the last json_encode() or json_decode() call. */ +static PHP_FUNCTION(json_last_error_msg) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + switch(JSON_G(error_code)) { + case PHP_JSON_ERROR_NONE: + RETURN_STRING("No error", 1); + case PHP_JSON_ERROR_DEPTH: + RETURN_STRING("Maximum stack depth exceeded", 1); + case PHP_JSON_ERROR_STATE_MISMATCH: + RETURN_STRING("State mismatch (invalid or malformed JSON)", 1); + case PHP_JSON_ERROR_CTRL_CHAR: + RETURN_STRING("Control character error, possibly incorrectly encoded", 1); + case PHP_JSON_ERROR_SYNTAX: + RETURN_STRING("Syntax error", 1); + case PHP_JSON_ERROR_UTF8: + RETURN_STRING("Malformed UTF-8 characters, possibly incorrectly encoded", 1); + case PHP_JSON_ERROR_RECURSION: + RETURN_STRING("Recursion detected", 1); + case PHP_JSON_ERROR_INF_OR_NAN: + RETURN_STRING("Inf and NaN cannot be JSON encoded", 1); + case PHP_JSON_ERROR_UNSUPPORTED_TYPE: + RETURN_STRING("Type is not supported", 1); + default: + RETURN_STRING("Unknown error", 1); + } + +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/json/json.dsp b/ext/json/json.dsp new file mode 100644 index 000000000..e5bb3767b --- /dev/null +++ b/ext/json/json.dsp @@ -0,0 +1,135 @@ +# Microsoft Developer Studio Project File - Name="json" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=json - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "json.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "json.mak" CFG="json - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "json - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "json - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "json - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /I "json_c" /D HAVE_JSON=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D ZEND_DEBUG=1 /D ZTS=1 /D COMPILE_DL_JSON=1 /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x1009 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 iconv.lib php4ts_debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS/php_json.dll" /pdbtype:sept /libpath:"..\..\Debug_TS"
+
+!ELSEIF "$(CFG)" == "json - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /I "json_c" /D HAVE_JSON=1 /D "ZEND_WIN32" /D ZEND_DEBUG=0 /D "PHP_WIN32" /D ZTS=1 /D COMPILE_DL_JSON=1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /D "HAVE_FCNTL_H" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x1009 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 iconv.lib php4ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_json.dll" /libpath:"..\..\Release_TS"
+
+!ENDIF
+
+# Begin Target
+
+# Name "json - Win32 Debug_TS"
+# Name "json - Win32 Release_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=".\json.c"
+# End Source File
+# Begin Source File
+
+SOURCE=.\JSON_parser.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\JSON_parser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_decode.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_decode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_to_utf16.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_to_utf16.h
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php_json.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/ext/json/package.xml b/ext/json/package.xml new file mode 100644 index 000000000..0651de736 --- /dev/null +++ b/ext/json/package.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE package SYSTEM "../pear/package.dtd"> +<package> + <dep type="php" rel="ge" version="4.3.0" optional="no"/> + <name>json</name> + <summary>JavaScript Object Notation</summary> + <maintainers> + <maintainer> + <user>omar</user> + <name>Omar Kilani</name> + <email>omar@php.net</email> + <role>lead</role> + </maintainer> + </maintainers> + <description> + Support for JSON (JavaScript Object Notation) serialization. + </description> + <license>PHP 3.01</license> + <release> + <state>stable</state> + <version>1.2.1</version> + <date>2006-03-18</date> + <notes> + Fix PECL bug #7147 - rework handling of comma insertion while encoding. + Add tests to package.xml + </notes> + </release> + <configureoptions> + </configureoptions> + <filelist> + <file role="doc" name="README" /> + <file role="src" name="config.m4" /> + <file role="src" name="config.w32" /> + <file role="src" name="json.dsp" /> + <file role="src" name="json.c" /> + <file role="src" name="JSON_parser.c" /> + <file role="src" name="JSON_parser.h" /> + <file role="src" name="php_json.h" /> + <file role="src" name="utf8_decode.c" /> + <file role="src" name="utf8_decode.h" /> + <file role="src" name="utf8_to_utf16.c" /> + <file role="src" name="utf8_to_utf16.h" /> + <dir role="test" name="tests"> + <file role="test" name="fail001.phpt" /> + <file role="test" name="pass001.phpt" /> + <file role="test" name="pass001.1.phpt" /> + <file role="test" name="pass002.phpt" /> + <file role="test" name="pass003.phpt" /> + </dir> + </filelist> + <changelog> + <release> + <state>stable</state> + <version>1.0.0</version> + <date>2005-04-01</date> + <notes> + Initial release. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.1</version> + <date>2005-06-10</date> + <notes> + Fixed non-linear and mixed type array index issues, fixed issues with escaping \\, forked json-c and added Unicode support. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.2</version> + <date>2005-06-11</date> + <notes> + Fixed issues with object reference counts under PHP4. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.3</version> + <date>2005-06-15</date> + <notes> + Fixed json-c string corruption issues under Mac OS X and FreeBSD. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.4</version> + <date>2005-06-15</date> + <notes> + Changes in 1.0.4 released with 1.0.5. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.5</version> + <date>2005-06-16</date> + <notes> + Changed spacing in json-c encoding, added optional assoc (boolean) parameter to json_decode to decode as associative array instead of object, fixed issues with escaping /. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.6</version> + <date>2005-08-05</date> + <notes> + Fixed issues with exporting private and protected class members. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.7</version> + <date>2005-09-07</date> + <notes> + Fixed issues with negative array keys, modified json-c to return an error on unquoted object key names instead of going into an infinite loop. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.8</version> + <date>2005-12-01</date> + <notes> + Changed license to LGPL, modified build system to allow static compilation into PHP, added strndup check for json-c. + </notes> + </release> + <release> + <state>stable</state> + <version>1.1.0</version> + <date>2005-12-04</date> + <notes> + Port to Win32. + </notes> + </release> + <release> + <state>stable</state> + <version>1.1.1</version> + <date>2006-01-12</date> + <notes> + Cleanup and TSRM performance fixes by rasmus. + </notes> + </release> + <release> + <state>stable</state> + <version>1.2.0</version> + <date>2006-03-15</date> + <notes> + Complete rewrite using JSON_checker as the base for the parser. Implements the JSON specification. 3-8x faster on encodes and 1.2x-4x faster on decodes. + </notes> + </release> + </changelog> +</package> +<!-- +vim:et:ts=1:sw=1 +--> diff --git a/ext/json/php_json.h b/ext/json/php_json.h new file mode 100644 index 000000000..ec707ce34 --- /dev/null +++ b/ext/json/php_json.h @@ -0,0 +1,92 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Omar Kilani <omar@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_JSON_H +#define PHP_JSON_H + +#define PHP_JSON_VERSION "1.2.1" +#include "ext/standard/php_smart_str.h" + +extern zend_module_entry json_module_entry; +#define phpext_json_ptr &json_module_entry + +#if defined(PHP_WIN32) && defined(JSON_EXPORTS) +#define PHP_JSON_API __declspec(dllexport) +#else +#define PHP_JSON_API PHPAPI +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +ZEND_BEGIN_MODULE_GLOBALS(json) + int encoder_depth; + int error_code; + int encode_max_depth; +ZEND_END_MODULE_GLOBALS(json) + +#ifdef ZTS +# define JSON_G(v) TSRMG(json_globals_id, zend_json_globals *, v) +#else +# define JSON_G(v) (json_globals.v) +#endif + +PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC); +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC); +extern zend_class_entry *php_json_serializable_ce; + + +/* json_encode() options */ +#define PHP_JSON_HEX_TAG (1<<0) +#define PHP_JSON_HEX_AMP (1<<1) +#define PHP_JSON_HEX_APOS (1<<2) +#define PHP_JSON_HEX_QUOT (1<<3) +#define PHP_JSON_FORCE_OBJECT (1<<4) +#define PHP_JSON_NUMERIC_CHECK (1<<5) +#define PHP_JSON_UNESCAPED_SLASHES (1<<6) +#define PHP_JSON_PRETTY_PRINT (1<<7) +#define PHP_JSON_UNESCAPED_UNICODE (1<<8) +#define PHP_JSON_PARTIAL_OUTPUT_ON_ERROR (1<<9) + +/* Internal flags */ +#define PHP_JSON_OUTPUT_ARRAY 0 +#define PHP_JSON_OUTPUT_OBJECT 1 + +/* json_decode() options */ +#define PHP_JSON_OBJECT_AS_ARRAY (1<<0) +#define PHP_JSON_BIGINT_AS_STRING (1<<1) + +static inline void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) +{ + php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth TSRMLS_CC); +} + + +#endif /* PHP_JSON_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/json/tests/001.phpt b/ext/json/tests/001.phpt new file mode 100644 index 000000000..02d43c424 --- /dev/null +++ b/ext/json/tests/001.phpt @@ -0,0 +1,71 @@ +--TEST-- +json_decode() tests +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +var_dump(json_decode()); +var_dump(json_decode("")); +var_dump(json_decode("", 1)); +var_dump(json_decode("", 0)); +var_dump(json_decode(".", 1)); +var_dump(json_decode(".", 0)); +var_dump(json_decode("<?>")); +var_dump(json_decode(";")); +var_dump(json_decode("руссиш")); +var_dump(json_decode("blah")); +var_dump(json_decode(NULL)); +var_dump(json_decode('{ "test": { "foo": "bar" } }')); +var_dump(json_decode('{ "test": { "foo": "" } }')); +var_dump(json_decode('{ "": { "foo": "" } }')); +var_dump(json_decode('{ "": { "": "" } }')); +var_dump(json_decode('{ "": { "": "" }')); +var_dump(json_decode('{ "": "": "" } }')); + +?> +===DONE=== +--EXPECTF-- +Warning: json_decode() expects at least 1 parameter, 0 given in %s on line %d +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +object(stdClass)#%d (1) { + ["test"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } +} +object(stdClass)#%d (1) { + ["test"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(0) "" + } +} +object(stdClass)#%d (1) { + ["_empty_"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(0) "" + } +} +object(stdClass)#%d (1) { + ["_empty_"]=> + object(stdClass)#%d (1) { + ["_empty_"]=> + string(0) "" + } +} +NULL +NULL +===DONE=== diff --git a/ext/json/tests/002.phpt b/ext/json/tests/002.phpt new file mode 100644 index 000000000..5959d4a5f --- /dev/null +++ b/ext/json/tests/002.phpt @@ -0,0 +1,38 @@ +--TEST-- +json_encode() tests +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +var_dump(json_encode("")); +var_dump(json_encode(NULL)); +var_dump(json_encode(TRUE)); + +var_dump(json_encode(array(""=>""))); +var_dump(json_encode(array(array(1)))); +var_dump(json_encode(array())); + +var_dump(json_encode(array(""=>""), JSON_FORCE_OBJECT)); +var_dump(json_encode(array(array(1)), JSON_FORCE_OBJECT)); +var_dump(json_encode(array(), JSON_FORCE_OBJECT)); + +var_dump(json_encode(1)); +var_dump(json_encode("руссиш")); + + +echo "Done\n"; +?> +--EXPECTF-- +string(2) """" +string(4) "null" +string(4) "true" +string(7) "{"":""}" +string(5) "[[1]]" +string(2) "[]" +string(7) "{"":""}" +string(13) "{"0":{"0":1}}" +string(2) "{}" +string(1) "1" +string(38) ""\u0440\u0443\u0441\u0441\u0438\u0448"" +Done diff --git a/ext/json/tests/003.phpt b/ext/json/tests/003.phpt new file mode 100644 index 000000000..4ce5b0fde --- /dev/null +++ b/ext/json/tests/003.phpt @@ -0,0 +1,41 @@ +--TEST-- +json_encode() & endless loop - 1 +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$a = array(); +$a[] = &$a; + +var_dump($a); + +echo "\n"; + +var_dump(json_encode($a)); +var_dump(json_last_error(), json_last_error_msg()); + +echo "\n"; + +var_dump(json_encode($a, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); + +echo "Done\n"; +?> +--EXPECTF-- +array(1) { + [0]=> + &array(1) { + [0]=> + *RECURSION* + } +} + +bool(false) +int(6) +string(%d) "Recursion detected" + +string(8) "[[null]]" +int(6) +string(%d) "Recursion detected" +Done diff --git a/ext/json/tests/004.phpt b/ext/json/tests/004.phpt new file mode 100644 index 000000000..70ef3ffd1 --- /dev/null +++ b/ext/json/tests/004.phpt @@ -0,0 +1,38 @@ +--TEST-- +json_encode() & endless loop - 2 +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$a = new stdclass; +$a->prop = $a; + +var_dump($a); + +echo "\n"; + +var_dump(json_encode($a)); +var_dump(json_last_error(), json_last_error_msg()); + +echo "\n"; + +var_dump(json_encode($a, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); + +echo "Done\n"; +?> +--EXPECTF-- +object(stdClass)#%d (1) { + ["prop"]=> + *RECURSION* +} + +bool(false) +int(6) +string(%d) "Recursion detected" + +string(22) "{"prop":{"prop":null}}" +int(6) +string(%d) "Recursion detected" +Done diff --git a/ext/json/tests/005.phpt b/ext/json/tests/005.phpt new file mode 100644 index 000000000..01a307f4a --- /dev/null +++ b/ext/json/tests/005.phpt @@ -0,0 +1,23 @@ +--TEST-- +json_encode() & endless loop - 3 +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$a = array(); +$a[] = $a; + +var_dump($a); +var_dump(json_encode($a)); + +echo "Done\n"; +?> +--EXPECTF-- +array(1) { + [0]=> + array(0) { + } +} +string(4) "[[]]" +Done diff --git a/ext/json/tests/006.phpt b/ext/json/tests/006.phpt new file mode 100644 index 000000000..e1d4b4688 --- /dev/null +++ b/ext/json/tests/006.phpt @@ -0,0 +1,23 @@ +--TEST-- +json_encode() & extended encoding +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$a = array('<foo>',"'bar'",'"baz"','&blong&'); + +echo "Normal: ", json_encode($a), "\n"; +echo "Tags: ", json_encode($a,JSON_HEX_TAG), "\n"; +echo "Apos: ", json_encode($a,JSON_HEX_APOS), "\n"; +echo "Quot: ", json_encode($a,JSON_HEX_QUOT), "\n"; +echo "Amp: ", json_encode($a,JSON_HEX_AMP), "\n"; +echo "All: ", json_encode($a,JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP), "\n"; +?> +--EXPECT-- +Normal: ["<foo>","'bar'","\"baz\"","&blong&"] +Tags: ["\u003Cfoo\u003E","'bar'","\"baz\"","&blong&"] +Apos: ["<foo>","\u0027bar\u0027","\"baz\"","&blong&"] +Quot: ["<foo>","'bar'","\u0022baz\u0022","&blong&"] +Amp: ["<foo>","'bar'","\"baz\"","\u0026blong\u0026"] +All: ["\u003Cfoo\u003E","\u0027bar\u0027","\u0022baz\u0022","\u0026blong\u0026"] diff --git a/ext/json/tests/007.phpt b/ext/json/tests/007.phpt new file mode 100644 index 000000000..7557ac9ed --- /dev/null +++ b/ext/json/tests/007.phpt @@ -0,0 +1,40 @@ +--TEST-- +json_last_error() tests +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +var_dump(json_decode("[1]")); +var_dump(json_last_error(), json_last_error_msg()); +var_dump(json_decode("[[1]]", false, 2)); +var_dump(json_last_error(), json_last_error_msg()); +var_dump(json_decode("[1}")); +var_dump(json_last_error(), json_last_error_msg()); +var_dump(json_decode('["' . chr(0) . 'abcd"]')); +var_dump(json_last_error(), json_last_error_msg()); +var_dump(json_decode("[1")); +var_dump(json_last_error(), json_last_error_msg()); + + +echo "Done\n"; +?> +--EXPECT-- +array(1) { + [0]=> + int(1) +} +int(0) +string(8) "No error" +NULL +int(1) +string(28) "Maximum stack depth exceeded" +NULL +int(2) +string(42) "State mismatch (invalid or malformed JSON)" +NULL +int(3) +string(53) "Control character error, possibly incorrectly encoded" +NULL +int(4) +string(12) "Syntax error" +Done diff --git a/ext/json/tests/008.phpt b/ext/json/tests/008.phpt new file mode 100644 index 000000000..f2354d381 --- /dev/null +++ b/ext/json/tests/008.phpt @@ -0,0 +1,17 @@ +--TEST-- +json_decode() with large integers +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +$json = '{"largenum":123456789012345678901234567890}'; +$x = json_decode($json); +var_dump($x->largenum); +$x = json_decode($json, false, 512, JSON_BIGINT_AS_STRING); +var_dump($x->largenum); +echo "Done\n"; +?> +--EXPECT-- +float(1.2345678901235E+29) +string(30) "123456789012345678901234567890" +Done diff --git a/ext/json/tests/bug40503.phpt b/ext/json/tests/bug40503.phpt new file mode 100644 index 000000000..48f18a4e0 --- /dev/null +++ b/ext/json/tests/bug40503.phpt @@ -0,0 +1,21 @@ +--TEST-- +Bug #40503 (json_encode() value corruption on 32bit systems with overflown values) +--INI-- +precision=14 +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +function show_eq($x,$y) { + echo "$x ". ($x==$y ? "==" : "!=") ." $y\n"; +} + +$value = 0x7FFFFFFF; #2147483647; +show_eq("$value", json_encode($value)); +$value++; +show_eq("$value", json_encode($value)); + +?> +--EXPECT-- +2147483647 == 2147483647 +2147483648 == 2147483648 diff --git a/ext/json/tests/bug41034.phpt b/ext/json/tests/bug41034.phpt new file mode 100644 index 000000000..cc7704122 --- /dev/null +++ b/ext/json/tests/bug41034.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #41034 (json_encode() ignores null byte started keys in arrays) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +echo json_encode(array(0,"\0ab"=>1,"\0null-prefixed value")); +echo "\nDone\n"; +?> +--EXPECT-- +{"0":0,"\u0000ab":1,"1":"\u0000null-prefixed value"} +Done diff --git a/ext/json/tests/bug41067.phpt b/ext/json/tests/bug41067.phpt new file mode 100644 index 000000000..b20e6e1b5 --- /dev/null +++ b/ext/json/tests/bug41067.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #41067 (json_encode() problem with UTF-16 input) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +$single_barline = "\360\235\204\200"; +$array = array($single_barline); +print bin2hex($single_barline) . "\n"; +// print $single_barline . "\n\n"; +$json = json_encode($array); +print $json . "\n\n"; +$json_decoded = json_decode($json, true); +// print $json_decoded[0] . "\n"; +print bin2hex($json_decoded[0]) . "\n"; +print "END\n"; +?> +--EXPECT-- +f09d8480 +["\ud834\udd00"] + +f09d8480 +END diff --git a/ext/json/tests/bug41403.phpt b/ext/json/tests/bug41403.phpt new file mode 100644 index 000000000..1a7343122 --- /dev/null +++ b/ext/json/tests/bug41403.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #41403 (json_decode cannot decode floats if localeconv decimal_point is not '.') +--SKIPIF-- +<?php + +if (!extension_loaded('json')) die('skip'); + +if (setlocale(LC_NUMERIC, "de_DE") === false) { + die("skip no de_DE locale"); +} +?> +--INI-- +precision=14 +--FILE-- +<?php + +setlocale(LC_NUMERIC, 'de_DE'); +var_dump(json_decode('[2.1]')); +var_dump(json_decode('[0.15]')); +var_dump(json_decode('[123.13452345]')); +var_dump(json_decode('[123,13452345]')); + +echo "Done\n"; +?> +--EXPECTF-- +array(1) { + [0]=> + float(2,1) +} +array(1) { + [0]=> + float(0,15) +} +array(1) { + [0]=> + float(123,13452345) +} +array(2) { + [0]=> + int(123) + [1]=> + int(13452345) +} +Done diff --git a/ext/json/tests/bug41504.phpt b/ext/json/tests/bug41504.phpt new file mode 100644 index 000000000..bef497404 --- /dev/null +++ b/ext/json/tests/bug41504.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug #41504 (json_decode() converts empty array keys to "_empty_") +--SKIPIF-- +<?php if (!extension_loaded('json')) print 'skip'; ?> +--FILE-- +<?php + +var_dump(json_decode('{"":"value"}', true)); +var_dump(json_decode('{"":"value", "key":"value"}', true)); +var_dump(json_decode('{"key":"value", "":"value"}', true)); + +echo "Done\n"; +?> +--EXPECT-- +array(1) { + [""]=> + string(5) "value" +} +array(2) { + [""]=> + string(5) "value" + ["key"]=> + string(5) "value" +} +array(2) { + ["key"]=> + string(5) "value" + [""]=> + string(5) "value" +} +Done diff --git a/ext/json/tests/bug41567.phpt b/ext/json/tests/bug41567.phpt new file mode 100644 index 000000000..a253a47b7 --- /dev/null +++ b/ext/json/tests/bug41567.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #41567 (json_encode() double conversion is inconsistent with PHP) +--INI-- +precision=14 +--SKIPIF-- +<?php if (!extension_loaded('json')) print 'skip'; ?> +--FILE-- +<?php + +$a = json_encode(123456789.12345); +var_dump(json_decode($a)); + +echo "Done\n"; +?> +--EXPECT-- +float(123456789.12345) +Done diff --git a/ext/json/tests/bug42090.phpt b/ext/json/tests/bug42090.phpt new file mode 100644 index 000000000..9e5b3317e --- /dev/null +++ b/ext/json/tests/bug42090.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #42090 (json_decode causes segmentation fault) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +var_dump( + json_decode('""'), + json_decode('"..".'), + json_decode('"'), + json_decode('""""'), + json_encode('"'), + json_decode(json_encode('"')), + json_decode(json_encode('""')) +); +?> +--EXPECT-- +string(0) "" +NULL +NULL +NULL +string(4) ""\""" +string(1) """ +string(2) """" diff --git a/ext/json/tests/bug42785.phpt b/ext/json/tests/bug42785.phpt new file mode 100644 index 000000000..7bdadbed1 --- /dev/null +++ b/ext/json/tests/bug42785.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #42785 (Incorrect formatting of double values with non-english locales) +--SKIPIF-- +<?php + if (!extension_loaded("json")) { + print "skip"; + } else if (!setlocale(LC_CTYPE, "de_DE", "de", "german", "ge", "de_DE.ISO8859-1", "ISO8859-1")) { + die("skip locale needed for this test is not supported on this platform"); + } +?> +--FILE-- +<?php +setlocale(LC_ALL, "de_DE", "de", "german", "ge", "de_DE.ISO8859-1", "ISO8859-1"); + +$foo = Array(100.10,"bar"); +var_dump(json_encode($foo)); + +Class bar {} +$bar1 = new bar; +$bar1->a = 100.10; +$bar1->b = "foo"; +var_dump(json_encode($bar1)); +?> +--EXPECT-- +string(13) "[100.1,"bar"]" +string(21) "{"a":100.1,"b":"foo"}" diff --git a/ext/json/tests/bug43941.phpt b/ext/json/tests/bug43941.phpt new file mode 100644 index 000000000..48bd7ad52 --- /dev/null +++ b/ext/json/tests/bug43941.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #43941 (json_encode() invalid UTF-8) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +var_dump(json_encode("abc")); +var_dump(json_encode("ab\xE0")); +var_dump(json_encode("ab\xE0", JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_encode(array("ab\xE0", "ab\xE0c", "abc"), JSON_PARTIAL_OUTPUT_ON_ERROR)); + +echo "Done\n"; +?> +--EXPECTF-- +string(5) ""abc"" +bool(false) +string(4) "null" +string(17) "[null,null,"abc"]" +Done diff --git a/ext/json/tests/bug45791.phpt b/ext/json/tests/bug45791.phpt new file mode 100644 index 000000000..7d3706825 --- /dev/null +++ b/ext/json/tests/bug45791.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #45791 (json_decode() does not handle number 0e0) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +var_dump(json_decode('{"zero": 0e0}')); + +?> +--EXPECT-- +object(stdClass)#1 (1) { + ["zero"]=> + float(0) +} diff --git a/ext/json/tests/bug46215.phpt b/ext/json/tests/bug46215.phpt new file mode 100644 index 000000000..0ac460cc1 --- /dev/null +++ b/ext/json/tests/bug46215.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug #46215 (json_encode mutates its parameter and has some class-specific state) +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php + +class foo { + protected $a = array(); +} + +$a = new foo; +$x = json_encode($a); + +print_r($a); + +?> +--EXPECT-- +foo Object +( + [a:protected] => Array + ( + ) + +) diff --git a/ext/json/tests/bug46944.phpt b/ext/json/tests/bug46944.phpt new file mode 100644 index 000000000..812a54887 --- /dev/null +++ b/ext/json/tests/bug46944.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug #46944 (json_encode() doesn't handle 3 byte utf8 correctly) +--SKIPIF-- +<?php if (!extension_loaded('json')) print 'skip'; ?> +--FILE-- +<?php + +for ($i = 1; $i <= 16; $i++) { + $first = 0xf0|($i >> 2); + $second = 0x8f|($i & 3) << 4; + $string = sprintf("aa%c%c\xbf\xbdzz", $first, $second); + echo json_encode($string) . "\n"; +} + + +echo "Done\n"; +?> +--EXPECT-- +"aa\ud83f\udffdzz" +"aa\ud87f\udffdzz" +"aa\ud8bf\udffdzz" +"aa\ud8ff\udffdzz" +"aa\ud93f\udffdzz" +"aa\ud97f\udffdzz" +"aa\ud9bf\udffdzz" +"aa\ud9ff\udffdzz" +"aa\uda3f\udffdzz" +"aa\uda7f\udffdzz" +"aa\udabf\udffdzz" +"aa\udaff\udffdzz" +"aa\udb3f\udffdzz" +"aa\udb7f\udffdzz" +"aa\udbbf\udffdzz" +"aa\udbff\udffdzz" +Done diff --git a/ext/json/tests/bug47644.phpt b/ext/json/tests/bug47644.phpt new file mode 100644 index 000000000..5e996b697 --- /dev/null +++ b/ext/json/tests/bug47644.phpt @@ -0,0 +1,43 @@ +--TEST-- +Bug #47644 (valid large integers are truncated) +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); + if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); +?> +--FILE-- +<?php + +for ($i = 10000000000000000; $i < 10000000000000006; $i++) { + var_dump(json_decode("[$i]")); +} + + +echo "Done\n"; +?> +--EXPECT-- +array(1) { + [0]=> + int(10000000000000000) +} +array(1) { + [0]=> + int(10000000000000001) +} +array(1) { + [0]=> + int(10000000000000002) +} +array(1) { + [0]=> + int(10000000000000003) +} +array(1) { + [0]=> + int(10000000000000004) +} +array(1) { + [0]=> + int(10000000000000005) +} +Done diff --git a/ext/json/tests/bug53946.phpt b/ext/json/tests/bug53946.phpt new file mode 100644 index 000000000..111438ddc --- /dev/null +++ b/ext/json/tests/bug53946.phpt @@ -0,0 +1,16 @@ +--TEST-- +bug #53946 (json_encode() with JSON_UNESCAPED_UNICODE) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +var_dump(json_encode("latin 1234 -/ russian мама мыла раму specialchars \x02 \x08 \n U+1D11E >𝄞<")); +var_dump(json_encode("latin 1234 -/ russian мама мыла раму specialchars \x02 \x08 \n U+1D11E >𝄞<", JSON_UNESCAPED_UNICODE)); +var_dump(json_encode("ab\xE0")); +var_dump(json_encode("ab\xE0", JSON_UNESCAPED_UNICODE)); +?> +--EXPECTF-- +string(156) ""latin 1234 -\/ russian \u043c\u0430\u043c\u0430 \u043c\u044b\u043b\u0430 \u0440\u0430\u043c\u0443 specialchars \u0002 \b \n U+1D11E >\ud834\udd1e<"" +string(100) ""latin 1234 -\/ russian мама мыла раму specialchars \u0002 \b \n U+1D11E >𝄞<"" +bool(false) +bool(false) diff --git a/ext/json/tests/bug54058.phpt b/ext/json/tests/bug54058.phpt new file mode 100644 index 000000000..df1b3130f --- /dev/null +++ b/ext/json/tests/bug54058.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #54058 (json_last_error() invalid UTF-8 produces wrong error) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$bad_utf8 = quoted_printable_decode('=B0'); + +json_encode($bad_utf8); +var_dump(json_last_error(), json_last_error_msg()); + +$a = new stdclass; +$a->foo = quoted_printable_decode('=B0'); +json_encode($a); +var_dump(json_last_error(), json_last_error_msg()); + +$b = new stdclass; +$b->foo = $bad_utf8; +$b->bar = 1; +json_encode($b); +var_dump(json_last_error(), json_last_error_msg()); + +$c = array( + 'foo' => $bad_utf8, + 'bar' => 1 +); +json_encode($c); +var_dump(json_last_error(), json_last_error_msg()); + +?> +--EXPECTF-- +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" diff --git a/ext/json/tests/bug54484.phpt b/ext/json/tests/bug54484.phpt new file mode 100644 index 000000000..d698ab541 --- /dev/null +++ b/ext/json/tests/bug54484.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #54484 (Empty string in json_decode doesn't reset json_last_error) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +json_decode('{"test":"test"}'); +var_dump(json_last_error()); + +json_decode(""); +var_dump(json_last_error()); + + +json_decode("invalid json"); +var_dump(json_last_error()); + + +json_decode(""); +var_dump(json_last_error()); +?> +--EXPECT-- +int(0) +int(0) +int(4) +int(0) diff --git a/ext/json/tests/bug55543.phpt b/ext/json/tests/bug55543.phpt new file mode 100644 index 000000000..8657fe776 --- /dev/null +++ b/ext/json/tests/bug55543.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #55543 (json_encode() with JSON_NUMERIC_CHECK & numeric string properties) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +$a = new stdClass; +$a->{"1"} = "5"; + +var_dump(json_encode($a, JSON_NUMERIC_CHECK)); +?> +--EXPECT-- +string(7) "{"1":5}" diff --git a/ext/json/tests/bug61537.phpt b/ext/json/tests/bug61537.phpt new file mode 100644 index 000000000..80ed051c9 --- /dev/null +++ b/ext/json/tests/bug61537.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bug #61537 (json_encode() incorrectly truncates/discards information) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +$invalid_utf8 = "\x9f"; + +var_dump(json_encode($invalid_utf8)); +var_dump(json_last_error(), json_last_error_msg()); + +var_dump(json_encode($invalid_utf8, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); + +echo "\n"; + +$invalid_utf8 = "an invalid sequen\xce in the middle of a string"; + +var_dump(json_encode($invalid_utf8)); +var_dump(json_last_error(), json_last_error_msg()); + +var_dump(json_encode($invalid_utf8, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); + +?> +--EXPECTF-- +bool(false) +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +string(4) "null" +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" + +bool(false) +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +string(4) "null" +int(5) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" diff --git a/ext/json/tests/bug61978.phpt b/ext/json/tests/bug61978.phpt new file mode 100644 index 000000000..c34b03f8f --- /dev/null +++ b/ext/json/tests/bug61978.phpt @@ -0,0 +1,43 @@ +--TEST-- +Bug #61978 (Object recursion not detected for classes that implement JsonSerializable) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +class JsonTest1 { + public $test; + public $me; + public function __construct() { + $this->test = '123'; + $this->me = $this; + } +} + +class JsonTest2 implements JsonSerializable { + public $test; + public function __construct() { + $this->test = '123'; + } + public function jsonSerialize() { + return array( + 'test' => $this->test, + 'me' => $this + ); + } +} + + +$obj1 = new JsonTest1(); +var_dump(json_encode($obj1, JSON_PARTIAL_OUTPUT_ON_ERROR)); + +echo "==\n"; + +$obj2 = new JsonTest2(); +var_dump(json_encode($obj2, JSON_PARTIAL_OUTPUT_ON_ERROR)); + +?> +--EXPECTF-- +string(44) "{"test":"123","me":{"test":"123","me":null}}" +== +string(44) "{"test":"123","me":{"test":"123","me":null}}" diff --git a/ext/json/tests/bug62369.phpt b/ext/json/tests/bug62369.phpt new file mode 100644 index 000000000..a5efd802c --- /dev/null +++ b/ext/json/tests/bug62369.phpt @@ -0,0 +1,34 @@ +--TEST-- +FR #62369 (Segfault on json_encode(deeply_nested_array) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$array = array(); +for ($i=0; $i<550; $i++) { + $array = array($array); +} + +json_encode($array, 0, 551); +switch (json_last_error()) { + case JSON_ERROR_NONE: + echo 'OK'.PHP_EOL; + break; + case JSON_ERROR_DEPTH: + echo 'ERROR'.PHP_EOL; + break; +} + +json_encode($array, 0, 540); +switch (json_last_error()) { + case JSON_ERROR_NONE: + echo 'OK'.PHP_EOL; + break; + case JSON_ERROR_DEPTH: + echo 'ERROR'.PHP_EOL; + break; +} +--EXPECTF-- +OK +ERROR diff --git a/ext/json/tests/bug63737.phpt b/ext/json/tests/bug63737.phpt new file mode 100644 index 000000000..1fb06d485 --- /dev/null +++ b/ext/json/tests/bug63737.phpt @@ -0,0 +1,32 @@ +--TEST-- +Bug #63737 (json_decode does not properly decode with options parameter) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +function decode($json) { + $x = json_decode($json); + var_dump($x); + $x = json_decode($json, false, 512, JSON_BIGINT_AS_STRING); + var_dump($x); +} + +decode('123456789012345678901234567890'); +decode('-123456789012345678901234567890'); + +// This shouldn't affect floats, but let's check that. +decode('123456789012345678901234567890.1'); +decode('-123456789012345678901234567890.1'); + +echo "Done\n"; +?> +--EXPECT-- +float(1.2345678901235E+29) +string(30) "123456789012345678901234567890" +float(-1.2345678901235E+29) +string(31) "-123456789012345678901234567890" +float(1.2345678901235E+29) +float(1.2345678901235E+29) +float(-1.2345678901235E+29) +float(-1.2345678901235E+29) +Done diff --git a/ext/json/tests/fail001.phpt b/ext/json/tests/fail001.phpt new file mode 100644 index 000000000..1bf9f1210 --- /dev/null +++ b/ext/json/tests/fail001.phpt @@ -0,0 +1,166 @@ +--TEST-- +JSON (http://www.crockford.com/JSON/JSON_checker/test/fail*.json) +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php + +$tests = array('"A JSON payload should be an object or array, not a string."', + '["Unclosed array"', + '{unquoted_key: "keys must be quoted}', + '["extra comma",]', + '["double extra comma",,]', + '[ , "<-- missing value"]', + '["Comma after the close"],', + '["Extra close"]]', + '{"Extra comma": true,}', + '{"Extra value after close": true} "misplaced quoted value"', + '{"Illegal expression": 1 + 2}', + '{"Illegal invocation": alert()}', + '{"Numbers cannot have leading zeroes": 013}', + '{"Numbers cannot be hex": 0x14}', + '["Illegal backslash escape: \\x15"]', + '["Illegal backslash escape: \\\'"]', + '["Illegal backslash escape: \\017"]', + '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]', + '{"Missing colon" null}', + '{"Double colon":: null}', + '{"Comma instead of colon", null}', + '["Colon instead of comma": false]', + '["Bad value", truth]', + "['single quote']"); + +foreach ($tests as $test) +{ + echo 'Testing: ' . $test . "\n"; + echo "AS OBJECT\n"; + var_dump(json_decode($test)); + echo "AS ARRAY\n"; + var_dump(json_decode($test, true)); +} + +?> +--EXPECT-- +Testing: "A JSON payload should be an object or array, not a string." +AS OBJECT +string(58) "A JSON payload should be an object or array, not a string." +AS ARRAY +string(58) "A JSON payload should be an object or array, not a string." +Testing: ["Unclosed array" +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {unquoted_key: "keys must be quoted} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["extra comma",] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["double extra comma",,] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: [ , "<-- missing value"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Comma after the close"], +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Extra close"]] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Extra comma": true,} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Extra value after close": true} "misplaced quoted value" +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Illegal expression": 1 + 2} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Illegal invocation": alert()} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Numbers cannot have leading zeroes": 013} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Numbers cannot be hex": 0x14} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Illegal backslash escape: \x15"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Illegal backslash escape: \'"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Illegal backslash escape: \017"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Missing colon" null} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Double colon":: null} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Comma instead of colon", null} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Colon instead of comma": false] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Bad value", truth] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ['single quote'] +AS OBJECT +NULL +AS ARRAY +NULL + diff --git a/ext/json/tests/inf_nan_error.phpt b/ext/json/tests/inf_nan_error.phpt new file mode 100644 index 000000000..f9deecc46 --- /dev/null +++ b/ext/json/tests/inf_nan_error.phpt @@ -0,0 +1,45 @@ +--TEST-- +An error is thrown when INF or NaN are encoded +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$inf = INF; + +var_dump($inf); + +var_dump(json_encode($inf)); +var_dump(json_last_error(), json_last_error_msg()); + +var_dump(json_encode($inf, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); + +echo "\n"; + +$nan = NAN; + +var_dump($nan); + +var_dump(json_encode($nan)); +var_dump(json_last_error(), json_last_error_msg()); + +var_dump(json_encode($nan, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); +?> +--EXPECTF-- +float(INF) +bool(false) +int(7) +string(34) "Inf and NaN cannot be JSON encoded" +string(1) "0" +int(7) +string(34) "Inf and NaN cannot be JSON encoded" + +float(NAN) +bool(false) +int(7) +string(34) "Inf and NaN cannot be JSON encoded" +string(1) "0" +int(7) +string(34) "Inf and NaN cannot be JSON encoded" diff --git a/ext/json/tests/json_decode_basic.phpt b/ext/json/tests/json_decode_basic.phpt new file mode 100644 index 000000000..6dbeadb36 --- /dev/null +++ b/ext/json/tests/json_decode_basic.phpt @@ -0,0 +1,187 @@ +--TEST-- +Test json_decode() function : basic functionality +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php +/* Prototype : mixed json_decode ( string $json [, bool $assoc ] ) + * Description: Decodes a JSON string + * Source code: ext/json/php_json.c + * Alias to functions: + */ +echo "*** Testing json_decode() : basic functionality ***\n"; + +// array with different values for $string +$inputs = array ( + '0', + '123', + '-123', + '2147483647', + '-2147483648', + '123.456', + '1230', + '-1230', + 'true', + 'false', + 'null', + '"abc"', + '"Hello World\r\n"', + '[]', + '[1,2,3,4,5]', + '{"myInt":99,"myFloat":123.45,"myNull":null,"myBool":true,"myString":"Hello World"}', + '{"Jan":31,"Feb":29,"Mar":31,"April":30,"May":31,"June":30}', + '""', + '{}' +); + +// loop through with each element of the $inputs array to test json_decode() function +$count = 1; +foreach($inputs as $input) { + echo "-- Iteration $count --\n"; + var_dump(json_decode($input)); + var_dump(json_decode($input, TRUE)); + $count ++; +} + +?> +===Done=== +--EXPECTF-- +*** Testing json_decode() : basic functionality *** +-- Iteration 1 -- +int(0) +int(0) +-- Iteration 2 -- +int(123) +int(123) +-- Iteration 3 -- +int(-123) +int(-123) +-- Iteration 4 -- +int(2147483647) +int(2147483647) +-- Iteration 5 -- +int(-2147483648) +int(-2147483648) +-- Iteration 6 -- +float(123.456) +float(123.456) +-- Iteration 7 -- +int(1230) +int(1230) +-- Iteration 8 -- +int(-1230) +int(-1230) +-- Iteration 9 -- +bool(true) +bool(true) +-- Iteration 10 -- +bool(false) +bool(false) +-- Iteration 11 -- +NULL +NULL +-- Iteration 12 -- +string(3) "abc" +string(3) "abc" +-- Iteration 13 -- +string(13) "Hello World +" +string(13) "Hello World +" +-- Iteration 14 -- +array(0) { +} +array(0) { +} +-- Iteration 15 -- +array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) +} +array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) +} +-- Iteration 16 -- +object(stdClass)#%d (5) { + ["myInt"]=> + int(99) + ["myFloat"]=> + float(123.45) + ["myNull"]=> + NULL + ["myBool"]=> + bool(true) + ["myString"]=> + string(11) "Hello World" +} +array(5) { + ["myInt"]=> + int(99) + ["myFloat"]=> + float(123.45) + ["myNull"]=> + NULL + ["myBool"]=> + bool(true) + ["myString"]=> + string(11) "Hello World" +} +-- Iteration 17 -- +object(stdClass)#%d (6) { + ["Jan"]=> + int(31) + ["Feb"]=> + int(29) + ["Mar"]=> + int(31) + ["April"]=> + int(30) + ["May"]=> + int(31) + ["June"]=> + int(30) +} +array(6) { + ["Jan"]=> + int(31) + ["Feb"]=> + int(29) + ["Mar"]=> + int(31) + ["April"]=> + int(30) + ["May"]=> + int(31) + ["June"]=> + int(30) +} +-- Iteration 18 -- +string(0) "" +string(0) "" +-- Iteration 19 -- +object(stdClass)#%d (0) { +} +array(0) { +} +===Done=== diff --git a/ext/json/tests/json_decode_error.phpt b/ext/json/tests/json_decode_error.phpt new file mode 100644 index 000000000..4d5d4e4be --- /dev/null +++ b/ext/json/tests/json_decode_error.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test json_decode() function : error conditions +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php +/* Prototype : mixed json_decode ( string $json [, bool $assoc=false [, int $depth=512 ]] ) + * Description: Decodes a JSON string + * Source code: ext/json/php_json.c + * Alias to functions: + */ +echo "*** Testing json_decode() : error conditions ***\n"; + +echo "\n-- Testing json_decode() function with no arguments --\n"; +var_dump( json_decode() ); + +echo "\n-- Testing json_decode() function with more than expected no. of arguments --\n"; +$extra_arg = 10; +var_dump( json_decode('"abc"', TRUE, 512, 0, $extra_arg) ); + +?> +===Done=== +--EXPECTF-- +*** Testing json_decode() : error conditions *** + +-- Testing json_decode() function with no arguments -- + +Warning: json_decode() expects at least 1 parameter, 0 given in %s on line %d +NULL + +-- Testing json_decode() function with more than expected no. of arguments -- + +Warning: json_decode() expects at most 4 parameters, 5 given in %s on line %d +NULL +===Done=== diff --git a/ext/json/tests/json_encode_basic.phpt b/ext/json/tests/json_encode_basic.phpt new file mode 100644 index 000000000..fc348eed8 --- /dev/null +++ b/ext/json/tests/json_encode_basic.phpt @@ -0,0 +1,156 @@ +--TEST-- +Test json_encode() function : basic functionality +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php +/* Prototype : string json_encode ( mixed $value ) + * Description: Returns the JSON representation of a value + * Source code: ext/json/php_json.c + * Alias to functions: + */ +echo "*** Testing json_encode() : basic functionality ***\n"; + +//get an unset variable +$unset_var = 10; +unset ($unset_var); + +// get a resource variable +$fp = fopen(__FILE__, "r"); + +// get an object +class sample { +} + +$obj = new sample(); +$obj->MyInt = 99; +$obj->MyFloat = 123.45; +$obj->MyBool = true; +$obj->MyNull = null; +$obj->MyString = "Hello World"; + +// array with different values for $string +$inputs = array ( + + // integers +/*1*/ 0, + 123, + -123, + 2147483647, + -2147483648, + + // floats +/*6*/ 123.456, + 1.23E3, + -1.23E3, + + // boolean +/*9*/ TRUE, + true, + FALSE, + false, + + // NULL +/*13*/ NULL, + null, + + // strings +/*15*/ "abc", + 'abc', + "Hello\t\tWorld\n", + + // arrays +/*18*/ array(), + array(1,2,3,4,5), + array(1 => "Sun", 2=>"Mon", 3 => "Tue", 4 => "Wed", 5 => "Thur", 6 => "Fri", 7 => "Sat"), + array("Jan" => 31, "Feb" => 29, "Mar" => 31, "April" => 30, "May" => 31, "June" => 30), + + // empty data +/*22*/ "", + '', + + // undefined data +/*24*/ @$undefined_var, + + // unset data +/*25*/ @$unset_var, + + // resource variable +/*26*/ $fp, + + // object variable +/*27*/ $obj + +); + +// loop through with each element of the $inputs array to test json_encode() function +$count = 1; +foreach($inputs as $input) { + echo "-- Iteration $count --\n"; + var_dump(json_encode($input)); + $count ++; +} + +?> +===Done=== +--EXPECTF-- +*** Testing json_encode() : basic functionality *** +-- Iteration 1 -- +string(1) "0" +-- Iteration 2 -- +string(3) "123" +-- Iteration 3 -- +string(4) "-123" +-- Iteration 4 -- +string(10) "2147483647" +-- Iteration 5 -- +string(11) "-2147483648" +-- Iteration 6 -- +string(7) "123.456" +-- Iteration 7 -- +string(4) "1230" +-- Iteration 8 -- +string(5) "-1230" +-- Iteration 9 -- +string(4) "true" +-- Iteration 10 -- +string(4) "true" +-- Iteration 11 -- +string(5) "false" +-- Iteration 12 -- +string(5) "false" +-- Iteration 13 -- +string(4) "null" +-- Iteration 14 -- +string(4) "null" +-- Iteration 15 -- +string(5) ""abc"" +-- Iteration 16 -- +string(5) ""abc"" +-- Iteration 17 -- +string(18) ""Hello\t\tWorld\n"" +-- Iteration 18 -- +string(2) "[]" +-- Iteration 19 -- +string(11) "[1,2,3,4,5]" +-- Iteration 20 -- +string(72) "{"1":"Sun","2":"Mon","3":"Tue","4":"Wed","5":"Thur","6":"Fri","7":"Sat"}" +-- Iteration 21 -- +string(58) "{"Jan":31,"Feb":29,"Mar":31,"April":30,"May":31,"June":30}" +-- Iteration 22 -- +string(2) """" +-- Iteration 23 -- +string(2) """" +-- Iteration 24 -- +string(4) "null" +-- Iteration 25 -- +string(4) "null" +-- Iteration 26 -- +bool(false) +-- Iteration 27 -- +string(82) "{"MyInt":99,"MyFloat":123.45,"MyBool":true,"MyNull":null,"MyString":"Hello World"}" +===Done=== diff --git a/ext/json/tests/json_encode_basic_utf8.phpt b/ext/json/tests/json_encode_basic_utf8.phpt new file mode 100644 index 000000000..a8e8b425e --- /dev/null +++ b/ext/json/tests/json_encode_basic_utf8.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test json_encode() function : basic functionality with UTF8 string input +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php +/* Prototype : string json_encode ( mixed $value ) + * Description: Returns the JSON representation of a value + * Source code: ext/json/php_json.c + * Alias to functions: + */ +echo "*** Testing json_encode() : basic functionality with UTF-8 input***\n"; + +$utf8_string = base64_decode('5pel5pys6Kqe44OG44Kt44K544OI44Gn44GZ44CCMDEyMzTvvJXvvJbvvJfvvJjvvJnjgII='); +var_dump(json_encode($utf8_string)); + +?> +===Done=== +--EXPECTF-- +*** Testing json_encode() : basic functionality with UTF-8 input*** +string(103) ""\u65e5\u672c\u8a9e\u30c6\u30ad\u30b9\u30c8\u3067\u3059\u300201234\uff15\uff16\uff17\uff18\uff19\u3002"" +===Done=== diff --git a/ext/json/tests/json_encode_error.phpt b/ext/json/tests/json_encode_error.phpt new file mode 100644 index 000000000..547c8bef1 --- /dev/null +++ b/ext/json/tests/json_encode_error.phpt @@ -0,0 +1,38 @@ +--TEST-- +Test json_encode() function : error conditions +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php +/* Prototype : string json_encode ( mixed $value [, int $options=0 ] ) + * Description: Returns the JSON representation of a value + * Source code: ext/json/php_json.c + * Alias to functions: + */ + +echo "*** Testing json_encode() : error conditions ***\n"; + +echo "\n-- Testing json_encode() function with no arguments --\n"; +var_dump( json_encode() ); + +echo "\n-- Testing json_encode() function with more than expected no. of arguments --\n"; +$extra_arg = 10; +var_dump( json_encode("abc", 0, $extra_arg) ); + +?> +===Done=== +--EXPECTF-- +*** Testing json_encode() : error conditions *** + +-- Testing json_encode() function with no arguments -- + +Warning: json_encode() expects at least 1 parameter, 0 given in %s on line %d +NULL + +-- Testing json_encode() function with more than expected no. of arguments -- +string(5) ""abc"" +===Done=== diff --git a/ext/json/tests/json_encode_numeric.phpt b/ext/json/tests/json_encode_numeric.phpt new file mode 100644 index 000000000..539235019 --- /dev/null +++ b/ext/json/tests/json_encode_numeric.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test json_encode() function with numeric flag +--SKIPIF-- +<?php +if (!extension_loaded("json")) { + die('skip JSON extension not available in this build'); +} +?> +--FILE-- +<?php +var_dump( + json_encode("1", JSON_NUMERIC_CHECK), + json_encode("9.4324", JSON_NUMERIC_CHECK), + json_encode(array("122321", "3232595.33423"), JSON_NUMERIC_CHECK), + json_encode("1"), + json_encode("9.4324"), + json_encode(array("122321", "3232595.33423")) +); +?> +--EXPECT-- +string(1) "1" +string(6) "9.4324" +string(22) "[122321,3232595.33423]" +string(3) ""1"" +string(8) ""9.4324"" +string(26) "["122321","3232595.33423"]" diff --git a/ext/json/tests/json_encode_pretty_print.phpt b/ext/json/tests/json_encode_pretty_print.phpt new file mode 100644 index 000000000..43b93aafe --- /dev/null +++ b/ext/json/tests/json_encode_pretty_print.phpt @@ -0,0 +1,40 @@ +--TEST-- +json_encode() with JSON_PRETTY_PRINT +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +function encode_decode($json) { + $struct = json_decode($json); + $pretty = json_encode($struct, JSON_PRETTY_PRINT); + echo "$pretty\n"; + $pretty = json_decode($pretty); + printf("Match: %d\n", $pretty == $struct); +} + +encode_decode('[1,2,3,[1,2,3]]'); +encode_decode('{"a":1,"b":[1,2],"c":{"d":42}}'); +?> +--EXPECT-- +[ + 1, + 2, + 3, + [ + 1, + 2, + 3 + ] +] +Match: 1 +{ + "a": 1, + "b": [ + 1, + 2 + ], + "c": { + "d": 42 + } +} +Match: 1 diff --git a/ext/json/tests/json_encode_unescaped_slashes.phpt b/ext/json/tests/json_encode_unescaped_slashes.phpt new file mode 100644 index 000000000..72ebae927 --- /dev/null +++ b/ext/json/tests/json_encode_unescaped_slashes.phpt @@ -0,0 +1,12 @@ +--TEST-- +json_decode() tests +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +var_dump(json_encode('a/b')); +var_dump(json_encode('a/b', JSON_UNESCAPED_SLASHES)); +?> +--EXPECT-- +string(6) ""a\/b"" +string(5) ""a/b"" diff --git a/ext/json/tests/pass001.1.phpt b/ext/json/tests/pass001.1.phpt new file mode 100644 index 000000000..a51f88578 --- /dev/null +++ b/ext/json/tests/pass001.1.phpt @@ -0,0 +1,890 @@ +--TEST-- +JSON (http://www.crockford.com/JSON/JSON_checker/test/pass1.json) +--INI-- +precision=14 +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); + if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only"); +?> +--FILE-- +<?php +/* Modified to test unescaped UNICODE as keys and values. + * Modified to test numbers with exponents without a decimal point. + * Modified to test empty string values. + * Modified to test a mix of integers and strings as keys. + */ +// Expect warnings about INF. +ini_set("error_reporting", E_ALL & ~E_WARNING); + +$test = " +[ + \"JSON Test Pattern pass1\", + {\"object with 1 member\":[\"array with 1 element\"]}, + {}, + [], + -42, + true, + false, + null, + { + \"integer\": 1234567890, + \"real\": -9876.543210, + \"e\": 0.123456789e-12, + \"E\": 1.234567890E+34, + \"\": 23456789012E666, + \"E no .\": 4E12, + \"zero\": 0, + \"one\": 1, + \"space\": \" \", + \"quote\": \"\\\"\", + \"backslash\": \"\\\\\", + \"controls\": \"\\b\\f\\n\\r\\t\", + \"slash\": \"/ & \\/\", + \"alpha\": \"abcdefghijklmnopqrstuvwyz\", + \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", + \"digit\": \"0123456789\", + \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", + \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", + \"unicode\": \"\\u30d7\\u30ec\\u30b9\\u30ad\\u30c3\\u30c8\", + \"プレスキット\": \"プレスキット\", + \"empty_string\": \"\", + \"true\": true, + \"false\": false, + \"null\": null, + \"array\":[ ], + \"object\":{ }, + \"123\":{\"456\":{\"abc\":{\"789\":\"def\",\"012\":[1,2,\"5\",500],\"ghi\":[1,2,\"five\",50,\"sixty\"]}}}, + \"address\": \"50 St. James Street\", + \"url\": \"http://www.JSON.org/\", + \"comment\": \"// /* <!-- --\", + \"# -- --> */\": \" \", + \" s p a c e d \" :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + \"compact\": [1,2,3,4,5,6,7], + \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", + \"quotes\": \"" \\u0022 %22 0x22 034 "\", + \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" +: \"A key can be any string\" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,\"rosebud\"] +"; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj, JSON_PARTIAL_OUTPUT_ON_ERROR); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr, JSON_PARTIAL_OUTPUT_ON_ERROR); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECTF-- +Testing: +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E666, + "E no .": 4E12, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "unicode": "\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8", + "プレスキット": "プレスキット", + "empty_string": "", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}}, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + "compact": [1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,"rosebud"] + +DECODE: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#%d (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#%d (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#%d (36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["E no ."]=> + float(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#%d (0) { + } + ["123"]=> + object(stdClass)#%d (1) { + ["456"]=> + object(stdClass)#%d (1) { + ["abc"]=> + object(stdClass)#%d (3) { + ["789"]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + [""]=> + float(INF) + ["E no ."]=> + float(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + [123]=> + array(1) { + [456]=> + array(1) { + ["abc"]=> + array(3) { + [789]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +ENCODE: FROM OBJECT +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +ENCODE: FROM ARRAY +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":[],"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +DECODE AGAIN: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#%d (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#%d (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#%d (36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["E no ."]=> + %s(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#%d (0) { + } + ["123"]=> + object(stdClass)#%d (1) { + ["456"]=> + object(stdClass)#%d (1) { + ["abc"]=> + object(stdClass)#%d (3) { + ["789"]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE AGAIN: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + [""]=> + int(0) + ["E no ."]=> + %s(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + [123]=> + array(1) { + [456]=> + array(1) { + ["abc"]=> + array(3) { + [789]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} diff --git a/ext/json/tests/pass001.1_64bit.phpt b/ext/json/tests/pass001.1_64bit.phpt new file mode 100644 index 000000000..ff2714436 --- /dev/null +++ b/ext/json/tests/pass001.1_64bit.phpt @@ -0,0 +1,890 @@ +--TEST-- +JSON (http://www.crockford.com/JSON/JSON_checker/test/pass1.json) +--INI-- +precision=14 +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); + if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); +?> +--FILE-- +<?php +/* Modified to test unescaped UNICODE as keys and values. + * Modified to test numbers with exponents without a decimal point. + * Modified to test empty string values. + * Modified to test a mix of integers and strings as keys. + */ +// Expect warnings about INF. +ini_set("error_reporting", E_ALL & ~E_WARNING); + +$test = " +[ + \"JSON Test Pattern pass1\", + {\"object with 1 member\":[\"array with 1 element\"]}, + {}, + [], + -42, + true, + false, + null, + { + \"integer\": 1234567890, + \"real\": -9876.543210, + \"e\": 0.123456789e-12, + \"E\": 1.234567890E+34, + \"\": 23456789012E666, + \"E no .\": 4E12, + \"zero\": 0, + \"one\": 1, + \"space\": \" \", + \"quote\": \"\\\"\", + \"backslash\": \"\\\\\", + \"controls\": \"\\b\\f\\n\\r\\t\", + \"slash\": \"/ & \\/\", + \"alpha\": \"abcdefghijklmnopqrstuvwyz\", + \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", + \"digit\": \"0123456789\", + \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", + \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", + \"unicode\": \"\\u30d7\\u30ec\\u30b9\\u30ad\\u30c3\\u30c8\", + \"プレスキット\": \"プレスキット\", + \"empty_string\": \"\", + \"true\": true, + \"false\": false, + \"null\": null, + \"array\":[ ], + \"object\":{ }, + \"123\":{\"456\":{\"abc\":{\"789\":\"def\",\"012\":[1,2,\"5\",500],\"ghi\":[1,2,\"five\",50,\"sixty\"]}}}, + \"address\": \"50 St. James Street\", + \"url\": \"http://www.JSON.org/\", + \"comment\": \"// /* <!-- --\", + \"# -- --> */\": \" \", + \" s p a c e d \" :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + \"compact\": [1,2,3,4,5,6,7], + \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", + \"quotes\": \"" \\u0022 %22 0x22 034 "\", + \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" +: \"A key can be any string\" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,\"rosebud\"] +"; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj, JSON_PARTIAL_OUTPUT_ON_ERROR); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr, JSON_PARTIAL_OUTPUT_ON_ERROR); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECTF-- +Testing: +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E666, + "E no .": 4E12, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "unicode": "\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8", + "プレスキット": "プレスキット", + "empty_string": "", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}}, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + "compact": [1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,"rosebud"] + +DECODE: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#%d (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#%d (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#%d (36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["E no ."]=> + float(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#%d (0) { + } + ["123"]=> + object(stdClass)#%d (1) { + ["456"]=> + object(stdClass)#%d (1) { + ["abc"]=> + object(stdClass)#%d (3) { + ["789"]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + [""]=> + float(INF) + ["E no ."]=> + float(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + [123]=> + array(1) { + [456]=> + array(1) { + ["abc"]=> + array(3) { + [789]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +ENCODE: FROM OBJECT +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +ENCODE: FROM ARRAY +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"E no .":4000000000000,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":[],"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +DECODE AGAIN: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#%d (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#%d (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#%d (36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["E no ."]=> + int(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#%d (0) { + } + ["123"]=> + object(stdClass)#%d (1) { + ["456"]=> + object(stdClass)#%d (1) { + ["abc"]=> + object(stdClass)#%d (3) { + ["789"]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE AGAIN: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + [""]=> + int(0) + ["E no ."]=> + int(4000000000000) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + [123]=> + array(1) { + [456]=> + array(1) { + ["abc"]=> + array(3) { + [789]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} diff --git a/ext/json/tests/pass001.phpt b/ext/json/tests/pass001.phpt new file mode 100644 index 000000000..1fd05fcdd --- /dev/null +++ b/ext/json/tests/pass001.phpt @@ -0,0 +1,702 @@ +--TEST-- +JSON (http://www.crockford.com/JSON/JSON_checker/test/pass1.json) +--INI-- +precision=14 +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php +// Expect warnings about INF. +ini_set("error_reporting", E_ALL & ~E_WARNING); + +$test = " +[ + \"JSON Test Pattern pass1\", + {\"object with 1 member\":[\"array with 1 element\"]}, + {}, + [], + -42, + true, + false, + null, + { + \"integer\": 1234567890, + \"real\": -9876.543210, + \"e\": 0.123456789e-12, + \"E\": 1.234567890E+34, + \"\": 23456789012E666, + \"zero\": 0, + \"one\": 1, + \"space\": \" \", + \"quote\": \"\\\"\", + \"backslash\": \"\\\\\", + \"controls\": \"\\b\\f\\n\\r\\t\", + \"slash\": \"/ & \\/\", + \"alpha\": \"abcdefghijklmnopqrstuvwyz\", + \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", + \"digit\": \"0123456789\", + \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", + \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", + \"true\": true, + \"false\": false, + \"null\": null, + \"array\":[ ], + \"object\":{ }, + \"address\": \"50 St. James Street\", + \"url\": \"http://www.JSON.org/\", + \"comment\": \"// /* <!-- --\", + \"# -- --> */\": \" \", + \" s p a c e d \" :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + \"compact\": [1,2,3,4,5,6,7], + \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", + \"quotes\": \"" \\u0022 %22 0x22 034 "\", + \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" +: \"A key can be any string\" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,\"rosebud\"] +"; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj, JSON_PARTIAL_OUTPUT_ON_ERROR); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr, JSON_PARTIAL_OUTPUT_ON_ERROR); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E666, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + "compact": [1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,"rosebud"] + +DECODE: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#1 (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#2 (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#3 (31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#4 (0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + [""]=> + float(INF) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +ENCODE: FROM OBJECT +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":{},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +ENCODE: FROM ARRAY +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":[],"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +DECODE AGAIN: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#5 (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#6 (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#7 (31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#8 (0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE AGAIN: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + [""]=> + int(0) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " +
" + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 +
`1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} diff --git a/ext/json/tests/pass002.phpt b/ext/json/tests/pass002.phpt new file mode 100644 index 000000000..24c7e3358 --- /dev/null +++ b/ext/json/tests/pass002.phpt @@ -0,0 +1,275 @@ +--TEST-- +JSON (http://www.crockford.com/JSON/JSON_checker/test/pass2.json) +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php + +$test = '[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]'; +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: [[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +DECODE: AS OBJECT +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} +DECODE: AS ARRAY +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} +ENCODE: FROM OBJECT +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +ENCODE: FROM ARRAY +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +DECODE AGAIN: AS OBJECT +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} +DECODE AGAIN: AS ARRAY +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} diff --git a/ext/json/tests/pass003.phpt b/ext/json/tests/pass003.phpt new file mode 100644 index 000000000..36da4a1de --- /dev/null +++ b/ext/json/tests/pass003.phpt @@ -0,0 +1,94 @@ +--TEST-- +JSON (http://www.crockford.com/JSON/JSON_checker/test/pass3.json) +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php + +$test = ' +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} +'; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} + +DECODE: AS OBJECT +object(stdClass)#1 (1) { + ["JSON Test Pattern pass3"]=> + object(stdClass)#2 (2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} +DECODE: AS ARRAY +array(1) { + ["JSON Test Pattern pass3"]=> + array(2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} +ENCODE: FROM OBJECT +{"JSON Test Pattern pass3":{"The outermost value":"must be an object or array.","In this test":"It is an object."}} +ENCODE: FROM ARRAY +{"JSON Test Pattern pass3":{"The outermost value":"must be an object or array.","In this test":"It is an object."}} +DECODE AGAIN: AS OBJECT +object(stdClass)#3 (1) { + ["JSON Test Pattern pass3"]=> + object(stdClass)#4 (2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} +DECODE AGAIN: AS ARRAY +array(1) { + ["JSON Test Pattern pass3"]=> + array(2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} diff --git a/ext/json/tests/serialize.phpt b/ext/json/tests/serialize.phpt new file mode 100644 index 000000000..5c513d58a --- /dev/null +++ b/ext/json/tests/serialize.phpt @@ -0,0 +1,80 @@ +--TEST-- +json_encode() Serialization tests +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +class NonSerializingTest +{ + public $data; + + public function __construct($data) + { + $this->data = $data; + } +} + +class SerializingTest extends NonSerializingTest implements JsonSerializable +{ + public function jsonSerialize() + { + return $this->data; + } +} + +class ValueSerializingTest extends SerializingTest +{ + public function jsonSerialize() + { + return array_values(is_array($this->data) ? $this->data : get_object_vars($this->data)); + } +} + +class SelfSerializingTest extends SerializingTest +{ + public function jsonSerialize() + { + return $this; + } +} + +$adata = array( + 'str' => 'foo', + 'int' => 1, + 'float' => 2.3, + 'bool' => false, + 'nil' => null, + 'arr' => array(1,2,3), + 'obj' => new StdClass, +); + +$ndata = array_values($adata); + +$odata = (object)$adata; + +foreach(array('NonSerializingTest','SerializingTest','ValueSerializingTest','SelfSerializingTest') as $class) { + echo "==$class==\n"; + echo json_encode(new $class($adata)), "\n"; + echo json_encode(new $class($ndata)), "\n"; + echo json_encode(new $class($odata)), "\n"; +} +--EXPECT-- +==NonSerializingTest== +{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}} +{"data":["foo",1,2.3,false,null,[1,2,3],{}]} +{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}} +==SerializingTest== +{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}} +["foo",1,2.3,false,null,[1,2,3],{}] +{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}} +==ValueSerializingTest== +["foo",1,2.3,false,null,[1,2,3],{}] +["foo",1,2.3,false,null,[1,2,3],{}] +["foo",1,2.3,false,null,[1,2,3],{}] +==SelfSerializingTest== +{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}} +{"data":["foo",1,2.3,false,null,[1,2,3],{}]} +{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}} + + diff --git a/ext/json/tests/unsupported_type_error.phpt b/ext/json/tests/unsupported_type_error.phpt new file mode 100644 index 000000000..45a167a5a --- /dev/null +++ b/ext/json/tests/unsupported_type_error.phpt @@ -0,0 +1,26 @@ +--TEST-- +An error is thrown when an unsupported type is encoded +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$resource = fopen(__FILE__, "r"); + +var_dump($resource); + +var_dump(json_encode($resource)); +var_dump(json_last_error(), json_last_error_msg()); + +var_dump(json_encode($resource, JSON_PARTIAL_OUTPUT_ON_ERROR)); +var_dump(json_last_error(), json_last_error_msg()); + +?> +--EXPECTF-- +resource(5) of type (stream) +bool(false) +int(8) +string(21) "Type is not supported" +string(4) "null" +int(8) +string(21) "Type is not supported" diff --git a/ext/json/utf8_decode.c b/ext/json/utf8_decode.c new file mode 100644 index 000000000..2d0422bed --- /dev/null +++ b/ext/json/utf8_decode.c @@ -0,0 +1,179 @@ +/* utf8_decode.c */ + +/* 2005-12-25 */ + +/* +Copyright (c) 2005 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "utf8_decode.h" + +/* + Very Strict UTF-8 Decoder + + UTF-8 is a multibyte character encoding of Unicode. A character can be + represented by 1-4 bytes. The bit pattern of the first byte indicates the + number of continuation bytes. + + Most UTF-8 decoders tend to be lenient, attempting to recover as much + information as possible, even from badly encoded input. This UTF-8 + decoder is not lenient. It will reject input which does not include + proper continuation bytes. It will reject aliases (or suboptimal + codings). It will reject surrogates. (Surrogate encoding should only be + used with UTF-16.) + + Code Contination Minimum Maximum + 0xxxxxxx 0 0 127 + 10xxxxxx error + 110xxxxx 1 128 2047 + 1110xxxx 2 2048 65535 excluding 55296 - 57343 + 11110xxx 3 65536 1114111 + 11111xxx error +*/ + + +/* + Get the next byte. It returns UTF8_END if there are no more bytes. +*/ +static int +get(json_utf8_decode *utf8) +{ + int c; + if (utf8->the_index >= utf8->the_length) { + return UTF8_END; + } + c = utf8->the_input[utf8->the_index] & 0xFF; + utf8->the_index += 1; + return c; +} + + +/* + Get the 6-bit payload of the next continuation byte. + Return UTF8_ERROR if it is not a contination byte. +*/ +static int +cont(json_utf8_decode *utf8) +{ + int c = get(utf8); + return ((c & 0xC0) == 0x80) ? (c & 0x3F) : UTF8_ERROR; +} + + +/* + Initialize the UTF-8 decoder. The decoder is not reentrant, +*/ +void +utf8_decode_init(json_utf8_decode *utf8, char p[], int length) +{ + utf8->the_index = 0; + utf8->the_input = p; + utf8->the_length = length; + utf8->the_char = 0; + utf8->the_byte = 0; +} + + +/* + Get the current byte offset. This is generally used in error reporting. +*/ +int +utf8_decode_at_byte(json_utf8_decode *utf8) +{ + return utf8->the_byte; +} + + +/* + Get the current character offset. This is generally used in error reporting. + The character offset matches the byte offset if the text is strictly ASCII. +*/ +int +utf8_decode_at_character(json_utf8_decode *utf8) +{ + return utf8->the_char > 0 ? utf8->the_char - 1 : 0; +} + + +/* + Extract the next character. + Returns: the character (between 0 and 1114111) + or UTF8_END (the end) + or UTF8_ERROR (error) +*/ +int +utf8_decode_next(json_utf8_decode *utf8) +{ + int c; /* the first byte of the character */ + int r; /* the result */ + + if (utf8->the_index >= utf8->the_length) { + return utf8->the_index == utf8->the_length ? UTF8_END : UTF8_ERROR; + } + utf8->the_byte = utf8->the_index; + utf8->the_char += 1; + c = get(utf8); +/* + Zero continuation (0 to 127) +*/ + if ((c & 0x80) == 0) { + return c; + } +/* + One contination (128 to 2047) +*/ + if ((c & 0xE0) == 0xC0) { + int c1 = cont(utf8); + if (c1 < 0) { + return UTF8_ERROR; + } + r = ((c & 0x1F) << 6) | c1; + return r >= 128 ? r : UTF8_ERROR; + } +/* + Two continuation (2048 to 55295 and 57344 to 65535) +*/ + if ((c & 0xF0) == 0xE0) { + int c1 = cont(utf8); + int c2 = cont(utf8); + if (c1 < 0 || c2 < 0) { + return UTF8_ERROR; + } + r = ((c & 0x0F) << 12) | (c1 << 6) | c2; + return r >= 2048 && (r < 55296 || r > 57343) ? r : UTF8_ERROR; + } +/* + Three continuation (65536 to 1114111) +*/ + if ((c & 0xF8) == 0xF0) { + int c1 = cont(utf8); + int c2 = cont(utf8); + int c3 = cont(utf8); + if (c1 < 0 || c2 < 0 || c3 < 0) { + return UTF8_ERROR; + } + r = ((c & 0x0F) << 18) | (c1 << 12) | (c2 << 6) | c3; + return r >= 65536 && r <= 1114111 ? r : UTF8_ERROR; + } + return UTF8_ERROR; +} diff --git a/ext/json/utf8_decode.h b/ext/json/utf8_decode.h new file mode 100644 index 000000000..cc0fc79f6 --- /dev/null +++ b/ext/json/utf8_decode.h @@ -0,0 +1,18 @@ +/* utf8_decode.h */ + +#define UTF8_END -1 +#define UTF8_ERROR -2 + +typedef struct json_utf8_decode +{ + int the_index; + char *the_input; + int the_length; + int the_char; + int the_byte; +} json_utf8_decode; + +extern int utf8_decode_at_byte(json_utf8_decode *utf8); +extern int utf8_decode_at_character(json_utf8_decode *utf8); +extern void utf8_decode_init(json_utf8_decode *utf8, char p[], int length); +extern int utf8_decode_next(json_utf8_decode *utf8); diff --git a/ext/opcache/shared_alloc_shm.c b/ext/opcache/shared_alloc_shm.c index d53236b3b..a88cc2e19 100644 --- a/ext/opcache/shared_alloc_shm.c +++ b/ext/opcache/shared_alloc_shm.c @@ -54,7 +54,7 @@ typedef struct { static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, char **error_in) { int i; - unsigned int allocate_size = 0, remaining_bytes = requested_size, seg_allocate_size; + size_t allocate_size = 0, remaining_bytes = requested_size, seg_allocate_size; int first_segment_id = -1; key_t first_segment_key = -1; struct shmid_ds sds; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 0bffae4d0..9f1940e06 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -52,6 +52,10 @@ typedef void (*zend_persist_func_t)(void * TSRMLS_DC); static void zend_persist_zval_ptr(zval **zp TSRMLS_DC); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +static const Bucket *uninitialized_bucket = NULL; +#endif + static void zend_hash_persist(HashTable *ht, void (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC) { Bucket *p = ht->pListHead; @@ -129,7 +133,7 @@ static void zend_hash_persist(HashTable *ht, void (*pPersistElement)(void *pElem zend_accel_store(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO } else { - ht->arBuckets = NULL; + ht->arBuckets = (Bucket**)&uninitialized_bucket; } #endif } diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 5405751ac..d752afea1 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -119,7 +119,7 @@ static void copy_shared_segments(void *to, void *from, int count, int size) } } -static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, int requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) +static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) { int res; g_shared_alloc_handler = he->handler; @@ -148,7 +148,7 @@ static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, int return ALLOC_FAILURE; } -int zend_shared_alloc_startup(int requested_size) +int zend_shared_alloc_startup(size_t requested_size) { zend_shared_segment **tmp_shared_segments; size_t shared_segments_array_size; diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index b7f36299b..e94ecab9b 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -117,7 +117,7 @@ extern zend_smm_shared_globals *smm_shared_globals; #define SHARED_ALLOC_REATTACHED (SUCCESS+1) -int zend_shared_alloc_startup(int requested_size); +int zend_shared_alloc_startup(size_t requested_size); void zend_shared_alloc_shutdown(void); /* allocate shared memory block */ diff --git a/ext/pdo/tests/bug61292.phpt b/ext/pdo/tests/bug61292.phpt index 2381fd1a2..05b2e9c01 100644 --- a/ext/pdo/tests/bug61292.phpt +++ b/ext/pdo/tests/bug61292.phpt @@ -17,8 +17,15 @@ class Database_SQL extends PDO { function __construct() { + $dsn = getenv('PDOTEST_DSN'); + $user = getenv('PDOTEST_USER'); + $pass = getenv('PDOTEST_PASS'); + + if ($user === false) $user = NULL; + if ($pass === false) $pass = NULL; $options = array(PDO::ATTR_PERSISTENT => TRUE); - parent::__construct(getenv("PDOTEST_DSN"), getenv("PDOTEST_USER"), getenv("PDOTEST_PASS"), $options); + + parent::__construct($dsn, $user, $pass, $options); } var $bar = array(); diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index ffc910177..9ed508736 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -32,6 +32,9 @@ #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" +/* Cache of the server supported datatypes, initialized in handle_factory */ +zval* pdo_dblib_datatypes; + static int dblib_fetch_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) { pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; @@ -262,17 +265,37 @@ static struct pdo_dbh_methods dblib_methods = { static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) { pdo_dblib_db_handle *H; - int i, ret = 0; + int i, nvars, nvers, ret = 0; + int *val; + + const pdo_dblib_keyval tdsver[] = { + {"4.2",DBVERSION_42} + ,{"4.6",DBVERSION_46} + ,{"5.0",DBVERSION_70} /* FIXME: This does not work with Sybase, but environ will */ + ,{"6.0",DBVERSION_70} + ,{"7.0",DBVERSION_70} + ,{"7.1",DBVERSION_71} + ,{"7.2",DBVERSION_72} + ,{"8.0",DBVERSION_72} + ,{"10.0",DBVERSION_100} + ,{"auto",0} /* Only works with FreeTDS. Other drivers will bork */ + + }; + + nvers = sizeof(tdsver)/sizeof(tdsver[0]); + struct pdo_data_src_parser vars[] = { - { "charset", NULL, 0 }, - { "appname", "PHP " PDO_DBLIB_FLAVOUR, 0 }, - { "host", "127.0.0.1", 0 }, - { "dbname", NULL, 0 }, - { "secure", NULL, 0 }, /* DBSETLSECURE */ - /* TODO: DBSETLVERSION ? */ + { "charset", NULL, 0 } + ,{ "appname", "PHP " PDO_DBLIB_FLAVOUR, 0 } + ,{ "host", "127.0.0.1", 0 } + ,{ "dbname", NULL, 0 } + ,{ "secure", NULL, 0 } /* DBSETLSECURE */ + ,{ "version", NULL, 0 } /* DBSETLVERSION */ }; - - php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 5); + + nvars = sizeof(vars)/sizeof(vars[0]); + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, nvars); H = pecalloc(1, sizeof(*H), dbh->is_persistent); H->login = dblogin(); @@ -282,11 +305,37 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ goto cleanup; } + DBERRHANDLE(H->login, (EHANDLEFUNC) error_handler); + DBMSGHANDLE(H->login, (MHANDLEFUNC) msg_handler); + + if(vars[5].optval) { + for(i=0;i<nvers;i++) { + if(strcmp(vars[5].optval,tdsver[i].key) == 0) { + if(FAIL==dbsetlversion(H->login, tdsver[i].value)) { + pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Failed to set version specified in connection string." TSRMLS_CC); + goto cleanup; + } + break; + } + } + + if (i==nvers) { + printf("Invalid version '%s'\n", vars[5].optval); + pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Invalid version specified in connection string." TSRMLS_CC); + goto cleanup; /* unknown version specified */ + } + } + if (dbh->username) { - DBSETLUSER(H->login, dbh->username); + if(FAIL == DBSETLUSER(H->login, dbh->username)) { + goto cleanup; + } } + if (dbh->password) { - DBSETLPWD(H->login, dbh->password); + if(FAIL == DBSETLPWD(H->login, dbh->password)) { + goto cleanup; + } } #if !PHP_DBLIB_IS_MSSQL @@ -297,14 +346,9 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ DBSETLAPP(H->login, vars[1].optval); -#if PHP_DBLIB_IS_MSSQL - dbprocerrhandle(H->login, (EHANDLEFUNC) error_handler); - dbprocmsghandle(H->login, (MHANDLEFUNC) msg_handler); -#endif - H->link = dbopen(H->login, vars[2].optval); - if (H->link == NULL) { + if (!H->link) { goto cleanup; } @@ -315,18 +359,35 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ DBSETOPT(H->link, DBTEXTSIZE, "2147483647"); /* allow double quoted indentifiers */ - DBSETOPT(H->link, DBQUOTEDIDENT, NULL); + DBSETOPT(H->link, DBQUOTEDIDENT, "1"); - if (vars[3].optval && FAIL == dbuse(H->link, vars[3].optval)) { - goto cleanup; + if (vars[3].optval) { + DBSETLDBNAME(H->login, vars[3].optval); } ret = 1; dbh->max_escaped_char_length = 2; dbh->alloc_own_columns = 1; +#if 0 + /* Cache the supported data types from the servers systypes table */ + if(dbcmd(H->link, "select usertype, name from systypes order by usertype") != FAIL) { + if(dbsqlexec(H->link) != FAIL) { + dbresults(H->link); + while (dbnextrow(H->link) == SUCCESS) { + val = dbdata(H->link, 1); + add_index_string(pdo_dblib_datatypes, *val, dbdata(H->link, 2), 1); + } + } + /* Throw out any remaining resultsets */ + dbcancel(H-link); + } +#endif + + + cleanup: - for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) { + for (i = 0; i < nvars; i++) { if (vars[i].freeme) { efree(vars[i].optval); } diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 1a2fefd47..51cebc48b 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -36,35 +36,51 @@ /* {{{ pdo_dblib_get_field_name * - * Updated for MSSQL 2008 SR2 extended types + * Return the data type name for a given TDS number * */ static char *pdo_dblib_get_field_name(int type) { + /* + * I don't return dbprtype(type) because it does not fully describe the type + * (example: varchar is reported as char by dbprtype) + * + * FIX ME: Cache datatypes from server systypes table in pdo_dblib_handle_factory() + * to make this future proof. + */ + switch (type) { + case 31: return "nvarchar"; case 34: return "image"; case 35: return "text"; case 36: return "uniqueidentifier"; + case 37: return "varbinary"; /* & timestamp - Sybase AS12 */ + case 38: return "bigint"; /* & bigintn - Sybase AS12 */ + case 39: return "varchar"; /* & sysname & nvarchar - Sybase AS12 */ case 40: return "date"; case 41: return "time"; case 42: return "datetime2"; case 43: return "datetimeoffset"; + case 45: return "binary"; /* Sybase AS12 */ + case 47: return "char"; /* & nchar & uniqueidentifierstr Sybase AS12 */ case 48: return "tinyint"; + case 50: return "bit"; /* Sybase AS12 */ case 52: return "smallint"; + case 55: return "decimal"; /* Sybase AS12 */ case 56: return "int"; case 58: return "smalldatetime"; case 59: return "real"; case 60: return "money"; case 61: return "datetime"; case 62: return "float"; + case 63: return "numeric"; /* or uint, ubigint, usmallint Sybase AS12 */ case 98: return "sql_variant"; case 99: return "ntext"; case 104: return "bit"; - case 106: return "decimal"; - case 108: return "numeric"; + case 106: return "decimal"; /* decimal n on sybase */ + case 108: return "numeric"; /* numeric n on sybase */ case 122: return "smallmoney"; case 127: return "bigint"; - case 240: return "geometry"; case 165: return "varbinary"; case 167: return "varchar"; case 173: return "binary"; @@ -72,23 +88,22 @@ static char *pdo_dblib_get_field_name(int type) case 189: return "timestamp"; case 231: return "nvarchar"; case 239: return "nchar"; + case 240: return "geometry"; case 241: return "xml"; - default: - return "unknown"; - break; + default: return "unknown"; } } /* }}} */ -static int dblib_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; /* Cancel any pending results */ dbcancel(H->link); - - efree(stmt->columns); + + efree(stmt->columns); stmt->columns = NULL; return 1; @@ -98,7 +113,8 @@ static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; - dblib_dblib_stmt_cursor_closer(stmt TSRMLS_CC); + efree(stmt->columns); + stmt->columns = NULL; efree(S); @@ -113,7 +129,12 @@ static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) ret = dbresults(H->link); - if (ret == FAIL || ret == NO_MORE_RESULTS) { + if (FAIL == ret) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL" TSRMLS_CC); + return 0; + } + + if(NO_MORE_RESULTS == ret) { return 0; } @@ -131,6 +152,8 @@ static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) dbsetuserdata(H->link, (BYTE*) &S->err); + pdo_dblib_stmt_cursor_closer(stmt TSRMLS_CC); + if (FAIL == dbcmd(H->link, stmt->active_query_string)) { return 0; } @@ -141,10 +164,6 @@ static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) ret = pdo_dblib_stmt_next_rowset(stmt TSRMLS_CC); - if (ret == 0) { - return 0; - } - stmt->row_count = DBCOUNT(H->link); stmt->column_count = dbnumcols(H->link); @@ -162,7 +181,12 @@ static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt, ret = dbnextrow(H->link); - if (ret == FAIL || ret == NO_MORE_ROWS) { + if (FAIL == ret) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL" TSRMLS_CC); + return 0; + } + + if(NO_MORE_ROWS == ret) { return 0; } @@ -174,6 +198,10 @@ static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; + if(colno >= stmt->column_count || colno < 0) { + return FAILURE; + } + struct pdo_column_data *col = &stmt->columns[colno]; col->name = (char*)dbcolname(H->link, colno+1); @@ -205,11 +233,12 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, } switch (coltype) { - case SQLCHAR: - case SQLTEXT: case SQLVARBINARY: case SQLBINARY: case SQLIMAGE: + case SQLTEXT: + /* FIXME: Above types should be returned as a stream as they can be VERY large */ + case SQLCHAR: case SQLVARCHAR: tmp_ptr = emalloc(*len + 1); memcpy(tmp_ptr, *ptr, *len); @@ -225,34 +254,26 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, *ptr = tmp_ptr; break; } -#ifdef SQLUNIQUE case SQLUNIQUE: { -#else - case 36: { /* FreeTDS hack, also used by ext/mssql */ -#endif *len = 36+1; tmp_ptr = emalloc(*len + 1); /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */ -#ifdef SQLUNIQUE *len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len); -#else - *len = dbconvert(NULL, 36, *ptr, *len, SQLCHAR, tmp_ptr, *len); -#endif php_strtoupper(tmp_ptr, *len); *ptr = tmp_ptr; break; } default: if (dbwillconvert(coltype, SQLCHAR)) { - tmp_len = 32 + (2 * (*len)); + tmp_len = 32 + (2 * (*len)); /* FIXME: We allocate more than we need here */ tmp_ptr = emalloc(tmp_len); *len = dbconvert(NULL, coltype, *ptr, *len, SQLCHAR, tmp_ptr, -1); *ptr = tmp_ptr; - } else { - *len = 0; - *ptr = NULL; - } + } else { + *len = 0; /* FIXME: Silently fails and returns null on conversion errors */ + *ptr = NULL; + } } *caller_frees = 1; @@ -270,17 +291,25 @@ static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *re { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; - + DBTYPEINFO* dbtypeinfo; + + if(colno >= stmt->column_count || colno < 0) { + return FAILURE; + } + array_init(return_value); - DBTYPEINFO* dbtypeinfo; dbtypeinfo = dbcoltypeinfo(H->link, colno+1); + + if(!dbtypeinfo) return FAILURE; add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) ); add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision ); add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale ); add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1), 1); add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(dbcoltype(H->link, colno+1)), 1); + add_assoc_long(return_value, "native_type_id", dbcoltype(H->link, colno+1)); + add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1)); return 1; } @@ -297,6 +326,6 @@ struct pdo_stmt_methods dblib_stmt_methods = { NULL, /* get attr */ pdo_dblib_stmt_get_column_meta, /* meta */ pdo_dblib_stmt_next_rowset, /* nextrow */ - dblib_dblib_stmt_cursor_closer + pdo_dblib_stmt_cursor_closer }; diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index ed79aea20..bc5d364ed 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -93,8 +93,12 @@ int error_handler(DBPROCESS *dbproc, int severity, int dberr, char *state = "HY000"; TSRMLS_FETCH(); - einfo = (pdo_dblib_err*)dbgetuserdata(dbproc); - if (!einfo) einfo = &DBLIB_G(err); + if(dbproc) { + einfo = (pdo_dblib_err*)dbgetuserdata(dbproc); + if (!einfo) einfo = &DBLIB_G(err); + } else { + einfo = &DBLIB_G(err); + } einfo->severity = severity; einfo->oserr = oserr; diff --git a/ext/pdo_dblib/php_pdo_dblib_int.h b/ext/pdo_dblib/php_pdo_dblib_int.h index dd06a1d94..2bdb83c32 100644 --- a/ext/pdo_dblib/php_pdo_dblib_int.h +++ b/ext/pdo_dblib/php_pdo_dblib_int.h @@ -71,6 +71,8 @@ # define SQLVARBINARY SYBVARBINARY # ifdef SYBUNIQUE # define SQLUNIQUE SYBUNIQUE +#else +# define SQLUNIQUE 36 /* FreeTDS Hack */ # endif # define DBERRHANDLE(a, b) dberrhandle(b) @@ -118,6 +120,12 @@ typedef struct { pdo_dblib_err err; } pdo_dblib_stmt; +typedef struct { + const char* key; + int value; +} pdo_dblib_keyval; + + ZEND_BEGIN_MODULE_GLOBALS(dblib) pdo_dblib_err err; char sqlstate[6]; diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index 5c3e435f7..2b57cd8ba 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -344,7 +344,7 @@ static int firebird_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, /* {{ if (n >= 0) { *len = slprintf(*ptr, CHAR_BUF_LEN, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -var->sqlscale, n % f); - } else if (n < -f) { + } else if (n <= -f) { *len = slprintf(*ptr, CHAR_BUF_LEN, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -var->sqlscale, -n % f); } else { @@ -535,12 +535,14 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat int force_null; case IS_LONG: - var->sqltype = sizeof(long) == 8 ? SQL_INT64 : SQL_LONG; + /* keep the allow-NULL flag */ + var->sqltype = (sizeof(long) == 8 ? SQL_INT64 : SQL_LONG) | (var->sqltype & 1); var->sqldata = (void*)&Z_LVAL_P(param->parameter); var->sqllen = sizeof(long); break; case IS_DOUBLE: - var->sqltype = SQL_DOUBLE; + /* keep the allow-NULL flag */ + var->sqltype = SQL_DOUBLE | (var->sqltype & 1); var->sqldata = (void*)&Z_DVAL_P(param->parameter); var->sqllen = sizeof(double); break; @@ -560,7 +562,8 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat force_null = (Z_STRLEN_P(param->parameter) == 0); } if (!force_null) { - var->sqltype = SQL_TEXT; + /* keep the allow-NULL flag */ + var->sqltype = SQL_TEXT | (var->sqltype & 1); var->sqldata = Z_STRVAL_P(param->parameter); var->sqllen = Z_STRLEN_P(param->parameter); break; diff --git a/ext/pdo_firebird/tests/bug_62024.phpt b/ext/pdo_firebird/tests/bug_62024.phpt new file mode 100644 index 000000000..e046879c2 --- /dev/null +++ b/ext/pdo_firebird/tests/bug_62024.phpt @@ -0,0 +1,51 @@ +--TEST-- +Bug #62024 Cannot insert second row with null using parametrized query (Firebird PDO) +--SKIPIF-- +<?php extension_loaded("pdo_firebird") or die("skip"); ?> +<?php function_exists("ibase_query") or die("skip"); ?> +--FILE-- +<?php + +require("testdb.inc"); + +$dbh = new PDO("firebird:dbname=$test_base",$user,$password) or die; +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); +$value = '2'; +@$dbh->exec('DROP TABLE test_insert'); +$dbh->exec("CREATE TABLE test_insert (ID INTEGER NOT NULL, TEXT VARCHAR(10))"); + +$dbh->commit(); + +//start actual test + +$sql = "insert into test_insert (id, text) values (?, ?)"; +$sttmt = $dbh->prepare($sql); + +$args_ok = array(1, "test1"); +$args_err = array(2, null); + +$res = $sttmt->execute($args_ok); +var_dump($res); + +$res = $sttmt->execute($args_err); +var_dump($res); + +$dbh->commit(); + + +//teardown test data +$sttmt = $dbh->prepare('DELETE FROM test_insert'); +$sttmt->execute(); + +$dbh->commit(); + +$dbh->exec('DROP TABLE test_insert'); + +unset($sttmt); +unset($dbh); + +?> +--EXPECT-- +bool(true) +bool(true) + diff --git a/ext/pdo_firebird/tests/bug_64037.phpt b/ext/pdo_firebird/tests/bug_64037.phpt new file mode 100644 index 000000000..f7b53e57a --- /dev/null +++ b/ext/pdo_firebird/tests/bug_64037.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bug #64037 Firebird return wrong value for numeric field +--SKIPIF-- +<?php extension_loaded("pdo_firebird") or die("skip"); ?> +<?php function_exists("ibase_query") or die("skip"); ?> +--FILE-- +<?php + +require("testdb.inc"); + +$dbh = new PDO("firebird:dbname=$test_base",$user,$password) or die; +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); +$value = '2'; +@$dbh->exec('DROP TABLE price'); +$dbh->exec("CREATE TABLE PRICE (ID INTEGER NOT NULL, TEXT VARCHAR(10), COST NUMERIC(15, 2))"); +$dbh->exec("INSERT INTO PRICE (ID, TEXT, COST) VALUES (1, 'test', -1.0)"); +$dbh->exec("INSERT INTO PRICE (ID, TEXT, COST) VALUES (2, 'test', -0.99)"); +$dbh->exec("INSERT INTO PRICE (ID, TEXT, COST) VALUES (3, 'test', -1.01)"); + +$dbh->commit(); + +$query = "SELECT * from price order by ID"; +$stmt = $dbh->prepare($query); +$stmt->execute(); +$rows = $stmt->fetchAll(); +var_dump($rows[0]['COST']); +var_dump($rows[1]['COST']); +var_dump($rows[2]['COST']); + + +$stmt = $dbh->prepare('DELETE FROM price'); +$stmt->execute(); + +$dbh->commit(); + +$dbh->exec('DROP TABLE price'); + +unset($stmt); +unset($dbh); + +?> +--EXPECT-- +string(5) "-1.00" +string(5) "-0.99" +string(5) "-1.01"
\ No newline at end of file diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index a54fccd0e..252bfff25 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -76,7 +76,7 @@ int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char * einfo->errmsg = NULL; } - if (sqlstate == NULL) { + if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) { strcpy(*pdo_err, "HY000"); } else { diff --git a/ext/pdo_pgsql/tests/copy_from.phpt b/ext/pdo_pgsql/tests/copy_from.phpt index 2858905d0..10967b0fe 100644 --- a/ext/pdo_pgsql/tests/copy_from.phpt +++ b/ext/pdo_pgsql/tests/copy_from.phpt @@ -110,8 +110,10 @@ $db->rollback(); echo "Exception! at line ", $e->getLine(), "\n"; var_dump($e->getMessage()); } -if(isset($filename)) { - @unlink($filename); + +// Clean up +foreach (array($filename, $filenameWithDifferentNullValues, $filenameWithDifferentNullValuesAndSelectedFields) as $f) { + @unlink($f); } ?> --EXPECT-- @@ -383,4 +385,4 @@ array(6) { NULL } Testing pgsqlCopyFromFile() with error -bool(false)
\ No newline at end of file +bool(false) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 7ee838a9f..dcc2c2869 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -4998,7 +4998,7 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z } smart_str_appends(&querystr, - "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims " + "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype = 'e' " "FROM pg_class as c, pg_attribute a, pg_type t, pg_namespace n " "WHERE a.attnum > 0 AND a.attrelid = c.oid AND c.relname = '"); tmp_name2 = php_addslashes(tmp_name2, strlen(tmp_name2), &new_len, 0 TSRMLS_CC); @@ -5044,6 +5044,12 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z add_assoc_bool(elem, "has default", 0); } add_assoc_long(elem, "array dims", atoi(PQgetvalue(pg_result,i,6))); + if (!strcmp(PQgetvalue(pg_result,i,7), "t")) { + add_assoc_bool(elem, "is enum", 1); + } + else { + add_assoc_bool(elem, "is enum", 0); + } name = PQgetvalue(pg_result,i,0); add_assoc_zval(meta, name, elem); } @@ -5077,7 +5083,18 @@ PHP_FUNCTION(pg_meta_data) zval_dtor(return_value); /* destroy array */ RETURN_FALSE; } -} + else { + HashPosition pos; + zval **val; + + for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(return_value), &pos); + zend_hash_get_current_data_ex(Z_ARRVAL_P(return_value), (void **)&val, &pos) == SUCCESS; + zend_hash_move_forward_ex(Z_ARRVAL_P(return_value), &pos)) { + /* delete newly added entry, in order to keep BC */ + zend_hash_del_key_or_index(Z_ARRVAL_PP(val), "is enum", sizeof("is enum"), 0, HASH_DEL_KEY); + } + } +} /* }}} */ /* {{{ php_pgsql_get_data_type @@ -5259,8 +5276,9 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con char *field = NULL; uint field_len = -1; ulong num_idx = -1; - zval *meta, **def, **type, **not_null, **has_default, **val, *new_val; + zval *meta, **def, **type, **not_null, **has_default, **is_enum, **val, *new_val; int new_len, key_type, err = 0, skip_field; + php_pgsql_data_type data_type; assert(pg_link != NULL); assert(Z_TYPE_P(values) == IS_ARRAY); @@ -5311,17 +5329,30 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'has default'"); err = 1; } + if (!err && zend_hash_find(Z_ARRVAL_PP(def), "is enum", sizeof("is enum"), (void **)&is_enum) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'is enum'"); + err = 1; + } if (!err && (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT || Z_TYPE_PP(val) == IS_CONSTANT_ARRAY)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scaler values as field values"); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scalar values as field values"); err = 1; } if (err) { break; /* break out for() */ } ALLOC_INIT_ZVAL(new_val); - switch(php_pgsql_get_data_type(Z_STRVAL_PP(type), Z_STRLEN_PP(type))) + + if (Z_BVAL_PP(is_enum)) { + /* enums need to be treated like strings */ + data_type = PG_TEXT; + } + else { + data_type = php_pgsql_get_data_type(Z_STRVAL_PP(type), Z_STRLEN_PP(type)); + } + + switch(data_type) { case PG_BOOL: switch (Z_TYPE_PP(val)) { diff --git a/ext/pgsql/tests/10pg_convert_85.phpt b/ext/pgsql/tests/10pg_convert_85.phpt index 4f1c92bf1..8b1cc8f53 100644 --- a/ext/pgsql/tests/10pg_convert_85.phpt +++ b/ext/pgsql/tests/10pg_convert_85.phpt @@ -12,6 +12,7 @@ error_reporting(E_ALL); include 'config.inc'; $db = pg_connect($conn_str); +pg_query($db, "SET standard_conforming_strings = 0"); $fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB'); $converted = pg_convert($db, $table_name, $fields); diff --git a/ext/pgsql/tests/12pg_insert_85.phpt b/ext/pgsql/tests/12pg_insert_85.phpt index a85dea036..5fbbe4b7a 100644 --- a/ext/pgsql/tests/12pg_insert_85.phpt +++ b/ext/pgsql/tests/12pg_insert_85.phpt @@ -12,6 +12,8 @@ error_reporting(E_ALL); include 'config.inc'; $db = pg_connect($conn_str); +pg_query($db, "SET standard_conforming_strings = 0"); + $fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB'); pg_insert($db, $table_name, $fields) or print "Error in test 1\n"; diff --git a/ext/pgsql/tests/14pg_update_85.phpt b/ext/pgsql/tests/14pg_update_85.phpt index f1c77eac1..06ca8c3de 100644 --- a/ext/pgsql/tests/14pg_update_85.phpt +++ b/ext/pgsql/tests/14pg_update_85.phpt @@ -12,6 +12,8 @@ error_reporting(E_ALL); include 'config.inc'; $db = pg_connect($conn_str); +pg_query($db, "SET standard_conforming_strings = 0"); + $fields = array('num'=>'1234', 'str'=>'ABC', 'bin'=>'XYZ'); $ids = array('num'=>'1234'); diff --git a/ext/pgsql/tests/18pg_escape_bytea.phpt b/ext/pgsql/tests/18pg_escape_bytea.phpt index 43f98c446..5f52a17d9 100644 --- a/ext/pgsql/tests/18pg_escape_bytea.phpt +++ b/ext/pgsql/tests/18pg_escape_bytea.phpt @@ -8,10 +8,11 @@ PostgreSQL pg_escape_bytea() functions include('config.inc'); +$db = pg_connect($conn_str); + $image = file_get_contents(dirname(__FILE__) . '/php.gif'); $esc_image = pg_escape_bytea($image); -$db = pg_connect($conn_str); pg_query($db, 'INSERT INTO '.$table_name.' (num, bin) VALUES (9876, \''.$esc_image.'\');'); $result = pg_query($db, 'SELECT * FROM '.$table_name.' WHERE num = 9876'); $rows = pg_fetch_all($result); diff --git a/ext/pgsql/tests/bug64609.phpt b/ext/pgsql/tests/bug64609.phpt new file mode 100644 index 000000000..0df63012d --- /dev/null +++ b/ext/pgsql/tests/bug64609.phpt @@ -0,0 +1,30 @@ +--TEST-- +Bug #64609 (pg_convert enum type support) +--SKIPIF-- +<?php +include("skipif.inc"); +skip_server_version('8.3', '<'); +?> +--FILE-- +<?php +error_reporting(E_ALL); + +include 'config.inc'; + +$db = pg_connect($conn_str); +pg_query("BEGIN"); +pg_query("CREATE TYPE t_enum AS ENUM ('ok', 'ko')"); +pg_query("CREATE TABLE test_enum (a t_enum)"); + +$fields = array('a' => 'ok'); +$converted = pg_convert($db, 'test_enum', $fields); + +pg_query("ROLLBACK"); + +var_dump($converted); +?> +--EXPECT-- +array(1) { + ["a"]=> + string(4) "'ok'" +} diff --git a/ext/standard/quot_print.c b/ext/standard/quot_print.c index 28dcc63f1..0df127362 100644 --- a/ext/standard/quot_print.c +++ b/ext/standard/quot_print.c @@ -151,7 +151,7 @@ PHPAPI unsigned char *php_quot_print_encode(const unsigned char *str, size_t len unsigned char c, *ret, *d; char *hex = "0123456789ABCDEF"; - ret = safe_emalloc(1, 3 * length + 3 * (((3 * length)/PHP_QPRINT_MAXL) + 1), 0); + ret = safe_emalloc(3, length + (((3 * length)/(PHP_QPRINT_MAXL-9)) + 1), 1); d = ret; while (length--) { @@ -286,4 +286,4 @@ PHP_FUNCTION(quoted_printable_encode) * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 - */
\ No newline at end of file + */ diff --git a/ext/standard/tests/strings/bug64879.phpt b/ext/standard/tests/strings/bug64879.phpt new file mode 100644 index 000000000..1df90c6d8 --- /dev/null +++ b/ext/standard/tests/strings/bug64879.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #64879: quoted_printable_encode() wrong size calculation (CVE-2013-2110) +--FILE-- +<?php + +quoted_printable_encode(str_repeat("\xf4", 1000)); +quoted_printable_encode(str_repeat("\xf4", 100000)); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/main/php_version.h b/main/php_version.h index 459c69924..629634e29 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 5 #define PHP_MINOR_VERSION 5 #define PHP_RELEASE_VERSION 0 -#define PHP_EXTRA_VERSION "RC2" -#define PHP_VERSION "5.5.0RC2" +#define PHP_EXTRA_VERSION "RC3" +#define PHP_VERSION "5.5.0RC3" #define PHP_VERSION_ID 50500 diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c index 10b867d00..d81e10150 100644 --- a/sapi/fpm/fpm/fpm_stdio.c +++ b/sapi/fpm/fpm/fpm_stdio.c @@ -291,7 +291,11 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */ } else { fpm_globals.error_log_fd = fd; +#if HAVE_UNISTD_H + if (fpm_global_config.daemonize || !isatty(STDERR_FILENO)) { +#else if (fpm_global_config.daemonize) { +#endif zlog_set_fd(fpm_globals.error_log_fd); } } |