package.xml0000644000175000017500000004351313177372464012331 0ustar tysontyson igbinary pecl.php.net igbinary extension Igbinary is a drop in replacement for the standard php serializer. Instead of time and space consuming textual representation, igbinary stores php data structures in a compact binary form. Savings are significant when using memcached or similar memory based storages for serialized data. Oleg Grenrus phadej oleg.grenrus@iki.fi yes Pierre Joye pajoye pierre@php.net yes Teddy Grenman tricky teddy.pecl@luuseri.com yes Tyson Andre tandre tysonandre775@hotmail.com yes 2017-11-04 2.0.5 1.1.1 stable stable BSD-3-Clause * Same as 2.0.5RC1 (no bugs were reported in that release candidate) 5.2.0 1.4.0b1 igbinary 2017-11-04 2.0.5 1.1.1 stable stable BSD-3-Clause * Same as 2.0.5RC1 (no bugs were reported in that release candidate) 2017-10-15 2.0.5RC1 1.1.1 beta stable BSD-3-Clause * Improve performance when unserializing objects/arrays and serializing objects/arrays/strings in php 5/7. * Update unserialization of integer object keys for php 7.2: Make those keys accessible when unserializing. * Properly pick up presence of gcc for default compiler flags (`cc --version` doesn't contain gcc). Add -O2 to default gcc compiler flags. * Use empty string for serializing empty $_SESSION array, similar to "session.serialize_handler=php". Older igbinary releases already unserialize the empty string to the empty array. 2017-04-14 2.0.4 1.1.1 stable stable BSD-3-Clause * Fixes bug #129: Should not call __wakeup() on data which was created by Serializable::unserialize() 2017-03-31 2.0.3 1.1.1 stable stable BSD-3-Clause - Fixes bug #126: Fatal error: "igbinary_serialize_zval: zval has unknown type 0" (IS_UNDEF) Make this a warning instead of a fatal error (and serialize as null instead), since IS_UNDEF is a known type. Later releases will fix the root cause of the warning, and consistently omit array/object/other entries for IS_UNDEF. 2017-02-28 2.0.2 1.1.1 stable stable BSD-3-Clause - Compatible with PHP 5.2 - 7.1 - Fixes crash in Memcached->setMulti (in php 7.0+) when the first level of array elements have references as values. Other extensions using igbinary shouldn't be affected. 2016-11-19 2.0.1 1.1.1 stable stable BSD-3-Clause - Compatible with PHP 5.2 - 7.0 - Fixes bug in session decoder not calling wakeup in php 7.0+ - (Enhancement) Reuses identical strings when unserializing objects and arrays in php 7.0+ 2016-10-30 2.0.0 1.1.1 stable stable BSD-3-Clause - Compatible with PHP 5.2 - 7.0 (Adds PHP 7 support) - Serialization format is unchanged - Performance improvements for serialization and unserialization in PHP 5.2 - 7.0 - (PHP 7) Don't call __destruct if __wakeup threw an exception (or __wakeup wasn't called yet) - Ports integration with other extensions to PHP 7.0 (session serialization, Memcached, Redis, APCu, etc.) - Fixes Windows PECL builds for PHP 5.6+ - Reword warnings for invalid header bytes of serialized data (in igbinary_unserialize). 2014-08-29 1.2.1 1.1.1 stable stable BSD-3-Clause-3-Clause - Compatible with PHP 5.2 - 5.6 2014-08-29 1.2.0 1.1.1 stable stable BSD-3-Clause - PECL bug #22614, igbinary_unserialize(FALSE) must return FALSE - PHP bug #54662, unserializing nested objects cause crash - Other fixes 2011-03-14 1.1.1 1.1.1 stable stable BSD-3-Clause - Initial PECL release 2009-09-02 1.0.2 1.0.2 stable stable PHP like license - Use Z_ADDREF_PP or Z_ADDREF et al. when increasing refcount. igbinary-2.0.5/tests/igbinary_001.phpt0000644000175000017500000000106413177372464016676 0ustar tysontyson--TEST-- Check for igbinary presence --SKIPIF-- --FILE-- --EXPECT-- igbinary extension is available igbinary-2.0.5/tests/igbinary_002.phpt0000644000175000017500000000144613177372464016703 0ustar tysontyson--TEST-- Check for null serialisation --SKIPIF-- --FILE-- --EXPECT-- null 00 OK igbinary-2.0.5/tests/igbinary_003.phpt0000644000175000017500000000153513177372464016703 0ustar tysontyson--TEST-- Check for bool serialisation --SKIPIF-- --FILE-- --EXPECT-- bool true 05 OK bool false 04 OK igbinary-2.0.5/tests/igbinary_004.phpt0000644000175000017500000000214113177372464016676 0ustar tysontyson--TEST-- Check for integer serialisation --SKIPIF-- --FILE-- --EXPECT-- zero: 0 0600 OK small: 1 0601 OK small: -1 0701 OK medium: 1000 0803e8 OK medium: -1000 0903e8 OK large: 100000 0a000186a0 OK large: -100000 0b000186a0 OK igbinary-2.0.5/tests/igbinary_005.phpt0000644000175000017500000000152113177372464016700 0ustar tysontyson--TEST-- Check for double serialisation --SKIPIF-- --FILE-- --EXPECT-- double: 123.456 0c405edd2f1a9fbe77 OK igbinary-2.0.5/tests/igbinary_006.phpt0000644000175000017500000000160013177372464016677 0ustar tysontyson--TEST-- Check for simple string serialization --SKIPIF-- --FILE-- --EXPECT-- empty: "" 0d OK string: "foobar" 1106666f6f626172 OK igbinary-2.0.5/tests/igbinary_007.phpt0000644000175000017500000000217513177372464016710 0ustar tysontyson--TEST-- Check for simple array serialization --SKIPIF-- --FILE-- --EXPECT-- empty array: 1400 OK array(1, 2, 3) 1403060006010601060206020603 OK array(array(1, 2, 3), arr... 1403060014030600060106010602060206030601140306000604060106050602060606021403060006070601060806020609 OK igbinary-2.0.5/tests/igbinary_008.phpt0000644000175000017500000000203713177372464016706 0ustar tysontyson--TEST-- Check for array+string serialization --SKIPIF-- --FILE-- 1, "two" => 2))', array("one" => 1, "two" => 2)); test('array("kek" => "lol", "lol" => "kek")', array("kek" => "lol", "lol" => "kek")); test('array("" => "empty")', array("" => "empty")); ?> --EXPECT-- array("foo", "foo", "foo") 140306001103666f6f06010e0006020e00 OK array("one" => 1, "two" => 2)) 140211036f6e650601110374776f0602 OK array("kek" => "lol", "lol" => "kek") 140211036b656b11036c6f6c0e010e00 OK array("" => "empty") 14010d1105656d707479 OK igbinary-2.0.5/tests/igbinary_009.phpt0000644000175000017500000000270513177372464016711 0ustar tysontyson--TEST-- Check for reference serialisation --SKIPIF-- array( 'b' => 'c', 'd' => 'e' ), 'f' => array( 'g' => 'h' ) ); test('array', $a, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- array 140211016114021101621101631101641101651101661401110167110168 OK igbinary-2.0.5/tests/igbinary_012.phpt0000644000175000017500000000215113177372464016676 0ustar tysontyson--TEST-- Object test --SKIPIF-- --FILE-- a = $a; $this->b = $b; $this->c = $c; } } $o = new Obj(1, 2, 3); test('object', $o); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a140311016106011104002a006206021106004f626a00630603 OK igbinary-2.0.5/tests/igbinary_013.phpt0000644000175000017500000000201213177372464016673 0ustar tysontyson--TEST-- Object-Array test --SKIPIF-- --FILE-- a = $a; $this->b = $b; } } $o = array(new Obj(1, 2), new Obj(3, 4)); test('object', $o, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- object 1402060017034f626a14021101610601110162060206011a0014020e0106030e020604 OK igbinary-2.0.5/tests/igbinary_014.phpt0000644000175000017500000000122713177372464016703 0ustar tysontyson--TEST-- Object-Reference test --SKIPIF-- --FILE-- a = $a; $this->b = $b; } } $o = new Obj(1, 2); $a = array(&$o, &$o); test('object', $a, false); --EXPECT-- object 140206002517034f626a1402110161060111016206020601252201 OK igbinary-2.0.5/tests/igbinary_015.phpt0000644000175000017500000000253313177372464016705 0ustar tysontyson--TEST-- Check for serialization handler --SKIPIF-- --EXPECT-- 2 14011103666f6f0602 igbinary-2.0.5/tests/igbinary_015b.phpt0000644000175000017500000000256113177372464017050 0ustar tysontyson--TEST-- Check for serialization handler, ini-directive --SKIPIF-- --EXPECT-- 2 14011103666f6f0602 igbinary-2.0.5/tests/igbinary_015c.phpt0000644000175000017500000000266513177372464017056 0ustar tysontyson--TEST-- Check for serialization handler --SKIPIF-- --FILE-- --EXPECT-- read(abcdef10231512dfaz_12311) write(abcdef10231512dfaz_12311): data:() igbinary-2.0.5/tests/igbinary_016.phpt0000644000175000017500000000227713177372464016713 0ustar tysontyson--TEST-- Object test, __sleep --SKIPIF-- --FILE-- a = $a; $this->b = $b; $this->c = $c; $this->d = $d; } function __sleep() { return array('a', 'b', 'c'); } # function __wakeup() { # $this->d = $this->a + $this->b + $this->c; # } } $o = new Obj(1, 2, 3, 4); test('object', $o, true); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a140311016106011104002a006206021106004f626a00630603 OK igbinary-2.0.5/tests/igbinary_017.phpt0000644000175000017500000000200513177372464016701 0ustar tysontyson--TEST-- Object test, __wakeup --SKIPIF-- --FILE-- b == 3 ? 'OK' : 'ERROR'; echo "\n"; } class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } function __wakeup() { $this->b = $this->a * 3; } } $o = new Obj(1, 2); test('object', $o, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a140211016106011101620602 OK igbinary-2.0.5/tests/igbinary_018.phpt0000644000175000017500000000252213177372464016706 0ustar tysontyson--TEST-- Object test, __sleep error cases --SKIPIF-- --FILE-- a = $a; $this->b = $b; } function __sleep() { return array('c'); } # function __wakeup() { # $this->b = $this->a * 3; # } } class Opj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } function __sleep() { return array(1); } # function __wakeup() { # # } } $o = new Obj(1, 2); $p = new Opj(1, 2); test('nonexisting', $o, true); test('wrong', $p, true); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- nonexisting 17034f626a140111016300 OK wrong 17034f706a140100 OK igbinary-2.0.5/tests/igbinary_019.phpt0000644000175000017500000000207013177372464016705 0ustar tysontyson--TEST-- Object test, __autoload --SKIPIF-- --FILE-- b == 2 ? 'OK' : 'ERROR'; echo "\n"; } function test_autoload($classname) { class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } } } spl_autoload_register("test_autoload"); test('autoload', '0000000217034f626a140211016106011101620602', false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- autoload 17034f626a140211016106011101620602 OK igbinary-2.0.5/tests/igbinary_020.phpt0000644000175000017500000000174313177372464016703 0ustar tysontyson--TEST-- Object test, incomplete class --SKIPIF-- --FILE-- --EXPECTF-- incom 17034f626a140211016106011101620602 object(__PHP_Incomplete_Class)#%d (3) { ["__PHP_Incomplete_Class_Name"]=> string(3) "Obj" ["a"]=> int(1) ["b"]=> int(2) } igbinary-2.0.5/tests/igbinary_021.phpt0000644000175000017500000000226713177372464016706 0ustar tysontyson--TEST-- Object Serializable interface --SKIPIF-- --FILE-- a = $a; $this->b = $b; } public function serialize() { return pack('NN', $this->a, $this->b); } public function unserialize($serialized) { $tmp = unpack('N*', $serialized); $this->__construct($tmp[1], $tmp[2]); } } $o = new Obj(1, 2); test('object', $o, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a1d080000000100000002 OK igbinary-2.0.5/tests/igbinary_022.phpt0000644000175000017500000000210513177372464016676 0ustar tysontyson--TEST-- Object test, unserialize_callback_func --SKIPIF-- --INI-- unserialize_callback_func=autoload --FILE-- b == 2 ? 'OK' : 'ERROR'; echo "\n"; } function autoload($classname) { class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } } } test('autoload', '0000000217034f626a140211016106011101620602', false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- autoload 17034f626a140211016106011101620602 OK igbinary-2.0.5/tests/igbinary_023.phpt0000644000175000017500000000075213177372464016705 0ustar tysontyson--TEST-- Resource --SKIPIF-- a = $a; $this->b = $b; $this->c = $c; } } class Obj2 { public $aa; protected $bb; private $cc; private $obj; function __construct($a, $b, $c) { $this->a = $a; $this->b = $b; $this->c = $c; $this->obj = new Obj($a, $b, $c); } } class Obj3 { private $objs; function __construct($a, $b, $c) { $this->objs = array(); for ($i = $a; $i < $c; $i += $b) { $this->objs[] = new Obj($a, $i, $c); } } } class Obj4 { private $a; private $obj; function __construct($a) { $this->a = $a; } public function set($obj) { $this->obj = $obj; } } $o2 = new Obj2(1, 2, 3); test('objectrec', $o2, false); $o3 = new Obj3(0, 1, 4); test('objectrecarr', $o3, false); $o4 = new Obj4(100); $o4->set($o4); test('objectselfrec', $o4, true); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- objectrec 17044f626a32140711026161001105002a006262001108004f626a32006363001109004f626a32006f626a17034f626a140311016106011104002a006206021106004f626a006306030e06060111016206021101630603 OK objectrecarr 17044f626a331401110a004f626a33006f626a731404060017034f626a140311016106001104002a006206001106004f626a0063060406011a0214030e0306000e0406010e05060406021a0214030e0306000e0406020e05060406031a0214030e0306000e0406030e050604 OK objectselfrec 17044f626a3414021107004f626a34006106641109004f626a34006f626a2200 OK igbinary-2.0.5/tests/igbinary_025.phpt0000644000175000017500000000400613177372464016703 0ustar tysontyson--TEST-- Object test, array of objects with __sleep --SKIPIF-- --FILE-- a = $a; $this->b = $b; $this->c = $c; $this->d = $d; } function __sleep() { return array('a', 'b', 'c'); } # function __wakeup() { # $this->d = $this->a + $this->b + $this->c; # } } $array = array( new Obj("aa", "bb", "cc", "dd"), new Obj("ee", "ff", "gg", "hh"), new Obj(1, 2, 3, 4), ); test('array', $array, true); ?> --EXPECTREGEX-- array\(3\) { \[0\]=> object\(Obj\)#1 \(4\) { \["a"\]=> string\(2\) "aa" \[("b":protected|"b:protected")\]=> string\(2\) "bb" \["?c"?:("Obj":)?private"?\]=> string\(2\) "cc" \["d"\]=> string\(2\) "dd" } \[1\]=> object\(Obj\)#2 \(4\) { \["a"\]=> string\(2\) "ee" \["?b"?:protected"?\]=> string\(2\) "ff" \["?c"?:("Obj":)?private"?\]=> string\(2\) "gg" \["d"\]=> string\(2\) "hh" } \[2\]=> object\(Obj\)#3 \(4\) { \["a"\]=> int\(1\) \["?b"?:protected"?\]=> int\(2\) \["?c"?:("Obj":)?private"?\]=> int\(3\) \["d"\]=> int\(4\) } } array\(3\) { \[0\]=> object\(Obj\)#4 \(4\) { \["a"\]=> string\(2\) "aa" \["?b"?:protected"?\]=> string\(2\) "bb" \["?c"?:("Obj":)?private"?\]=> string\(2\) "cc" \["d"\]=> NULL } \[1\]=> object\(Obj\)#5 \(4\) { \["a"\]=> string\(2\) "ee" \["?b"?:protected"?\]=> string\(2\) "ff" \["?c"?:("Obj":)?private"?\]=> string\(2\) "gg" \["d"\]=> NULL } \[2\]=> object\(Obj\)#6 \(4\) { \["a"\]=> int\(1\) \["?b"?:protected"?\]=> int\(2\) \["?c"?:("Obj":)?private"?\]=> int\(3\) \["d"\]=> NULL } } igbinary-2.0.5/tests/igbinary_026.phpt0000644000175000017500000000231213177372464016702 0ustar tysontyson--TEST-- Cyclic array test --INI-- report_memleaks=0 --SKIPIF-- array( 'b' => 'c', 'd' => 'e' ), ); $a['f'] = &$a; test('array', $a, false); $a = array("foo" => &$b); $b = array(1, 2, $a); $exp = $a; $act = igbinary_unserialize(igbinary_serialize($a)); ob_start(); var_dump($exp); $dump_exp = ob_get_clean(); ob_start(); var_dump($act); $dump_act = ob_get_clean(); if ($dump_act !== $dump_exp) { echo "Var dump differs:\n", $dump_act, "\n", $dump_exp, "\n"; } else { echo "Var dump OK\n"; } $act['foo'][1] = 'test value'; $exp['foo'][1] = 'test value'; if ($act['foo'][1] !== $act['foo'][2]['foo'][1]) { echo "Recursive elements differ:\n"; var_dump($act); var_dump($act['foo']); var_dump($exp); var_dump($exp['foo']); } ?> --EXPECT-- array 140211016114021101621101631101641101651101662514020e0001010e05250102 OK Var dump OK igbinary-2.0.5/tests/igbinary_026b.phpt0000644000175000017500000000175013177372464017051 0ustar tysontyson--TEST-- Cyclic array test 2 --INI-- report_memleaks=0 --SKIPIF-- &$b); $b = array(1, 2, $a); /* all three statements below should produce same output however PHP stock * unserialize/serialize produces different output (5.2.16). I consider this is * a PHP bug. - Oleg Grenrus */ //$k = $a; //$k = unserialize(serialize($a)); $k = igbinary_unserialize(igbinary_serialize($a)); function check($a, $k) { ob_start(); var_dump($a); $a_str = ob_get_clean(); ob_start(); var_dump($k); $k_str = ob_get_clean(); if ($a_str !== $k_str) { echo "Output differs\n"; echo "Expected:\n", $a_str, "\n"; echo "Actual:\n", $k_str, "\n"; } else { echo "OK\n"; } } check($a, $k); $a["foo"][2]["foo"][1] = "b"; $k["foo"][2]["foo"][1] = "b"; check($a, $k); ?> --EXPECT-- OK OK igbinary-2.0.5/tests/igbinary_027.phpt0000644000175000017500000000304013177372464016702 0ustar tysontyson--TEST-- Check for serialization handler --SKIPIF-- --EXPECT-- bool(true) read wrote: 14021103666f6f06011104746573741106666f6f626172 igbinary-2.0.5/tests/igbinary_028.phpt0000644000175000017500000000455113177372464016713 0ustar tysontyson--TEST-- Serialize object into session, full set --SKIPIF-- d1 = $foo; $this->d2 = $foo; $this->d3 = $foo; } } class Bar { private static $s1 = array(); protected static $s2 = array(); public static $s3 = array(); public $d1; private $d2; protected $d3; public function __construct() { } public function set($foo) { $this->d1 = $foo; $this->d2 = $foo; $this->d3 = $foo; } } if(!extension_loaded('igbinary')) { dl('igbinary.' . PHP_SHLIB_SUFFIX); } $output = ''; function open($path, $name) { return true; } function close() { return true; } function read($id) { global $output; $output .= "read\n"; $a = new Bar(); $b = new Foo($a); $a->set($b); $session = array('old' => $b); return igbinary_serialize($session); } function write($id, $data) { global $output; $output .= "write: "; $output .= substr(bin2hex($data), 8). "\n"; return true; } function destroy($id) { return true; } function gc($time) { return true; } ini_set('session.serialize_handler', 'igbinary'); session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc'); session_start(); $_SESSION['test'] = "foobar"; $a = new Bar(); $b = new Foo($a); $a->set($b); $_SESSION['new'] = $a; session_write_close(); echo $output; /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- read write: 140311036f6c641703466f6f1403110700466f6f0064311703426172140311026431220111070042617200643222011105002a00643322011105002a00643222021102643322021104746573741106666f6f62617211036e65771a0314030e041a0114030e0222030e0722030e0822030e0522040e062204 igbinary-2.0.5/tests/igbinary_029.phpt0000644000175000017500000000070613177372464016712 0ustar tysontyson--TEST-- Igbinary module info --SKIPIF-- --FILE-- enabled igbinary version => %s igbinary AP%s serializer ABI => %s igbinary session support => %s igbinary.compact_strings => %s => %s igbinary-2.0.5/tests/igbinary_030_php72.phpt0000644000175000017500000000206713177372464017724 0ustar tysontyson--TEST-- Unserialize invalid data (php 7.2+) --SKIPIF-- --FILE-- {"1"} = "manual"; $datas = array( 87817, -1, array(1,2,3,"testing" => 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), $o, ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); // truncated for ($i = 0; $i < $len - 1; $i++) { $v = igbinary_unserialize(substr($str, 0, $i)); if (is_object($data) && $v !== null && $v == $data) { continue; } elseif ($v !== null && $v != FALSE && $v !== $data) { echo "output at $i:\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } // padded $str .= "98398afa\000y21_ "; $v = igbinary_unserialize($str); if ($v !== $data && !(is_object($data) && $v == $data)) { echo "padded should get original\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } ?> --EXPECT-- igbinary-2.0.5/tests/igbinary_030_php7.phpt0000644000175000017500000000241613177372464017640 0ustar tysontyson--TEST-- Unserialize invalid data --SKIPIF-- = 70200 || PHP_VERSION_ID < 70000) { echo "Skip php 7.1 or 7.0 required\n"; } ?> --FILE-- {"1"} = "manual"; $datas = array( 87817, -1, array(1,2,3,"testing" => 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), $o, ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); // truncated for ($i = 0; $i < $len - 1; $i++) { $v = igbinary_unserialize(substr($str, 0, $i)); if (is_object($data) && $v !== null && $v == $data) { continue; } elseif ($v !== null && $v != FALSE && $v !== $data) { echo "output at $i:\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } // padded $str .= "98398afa\000y21_ "; $v = igbinary_unserialize($str); if ($v !== $data && !(is_object($data) && $v == $data)) { echo "padded should get original\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } ?> --EXPECT-- padded should get original object(stdClass)#3 (3) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) } vs. object(stdClass)#2 (3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } igbinary-2.0.5/tests/igbinary_030.phpt0000644000175000017500000000274513177372464016707 0ustar tysontyson--TEST-- Unserialize invalid data (PHP 5) --SKIPIF-- = 70000) { echo "Skip php 5.6 or older required\n"; } ?> --FILE-- {"1"} = "manual"; $datas = array( 87817, -1, array(1,2,3,"testing" => 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), $o, // some weirdness unserializing with zend_hash_update on strings that are integers. ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); // truncated for ($i = 0; $i < $len - 1; $i++) { $v = igbinary_unserialize(substr($str, 0, $i)); if (is_object($data) && $v !== null && $v == $data) { continue; } elseif ($v !== null && $v != FALSE && $v !== $data) { echo "output at $i:\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } // padded $str .= "98398afa\000y21_ "; $v = igbinary_unserialize($str); if ($v !== $data && !(is_object($data) && $v == $data)) { echo "padded should get original\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } ?> --EXPECT-- padded should get original object(stdClass)#8 (3) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) } vs. object(stdClass)#2 (3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } padded should get original object(stdClass)#11 (1) { [1]=> string(6) "manual" } vs. object(stdClass)#1 (1) { ["1"]=> string(6) "manual" } igbinary-2.0.5/tests/igbinary_031.phpt0000644000175000017500000000333113177372464016700 0ustar tysontyson--TEST-- Object Serializable interface throws exceptions --SKIPIF-- --FILE-- a = $a; $this->b = $b; } public function serialize() { $c = self::$count++; echo "call serialize, ", ($this->a ? "throw" : "no throw"),"\n"; if ($this->a) { throw new Exception("exception in serialize $c"); } return pack('NN', $this->a, $this->b); } public function unserialize($serialized) { $tmp = unpack('N*', $serialized); $this->__construct($tmp[1], $tmp[2]); $c = self::$count++; echo "call unserialize, ", ($this->b ? "throw" : "no throw"),"\n"; if ($this->b) { throw new Exception("exception in unserialize $c"); } } } $a = new Obj(1, 0); $a = new Obj(0, 0); $b = new Obj(0, 0); $c = new Obj(1, 0); $d = new Obj(0, 1); echo "a, a, c\n"; try { test(array($a, $a, $c)); } catch (Exception $e) { if (version_compare(phpversion(), "5.3.0", ">=")) { if ($e->getPrevious()) { $e = $e->getPrevious(); } } echo $e->getMessage(), "\n"; } echo "b, b, d\n"; try { test(array($b, $b, $d)); } catch (Exception $e) { if (version_compare(phpversion(), "5.3.0", ">=")) { if ($e->getPrevious()) { $e = $e->getPrevious(); } } echo $e->getMessage(), "\n"; } --EXPECT-- a, a, c call serialize, no throw call serialize, throw exception in serialize 2 b, b, d call serialize, no throw call serialize, no throw call unserialize, no throw call unserialize, throw exception in unserialize 6 igbinary-2.0.5/tests/igbinary_032.phpt0000644000175000017500000000260513177372464016704 0ustar tysontyson--TEST-- Object test, __sleep and __wakeup exceptions --SKIPIF-- a = $a; $this->b = $b; } function __sleep() { $c = self::$count++; if ($this->a) { throw new Exception("exception in __sleep $c"); } return array('a', 'b'); } function __wakeup() { $c = self::$count++; if ($this->b) { throw new Exception("exception in __wakeup $c"); } $this->b = $this->a * 3; } } $a = new Obj(1, 0); $b = new Obj(0, 1); $c = new Obj(0, 0); try { test($a); } catch (Exception $e) { echo $e->getMessage(), "\n"; } try { test($b); } catch (Exception $e) { echo $e->getMessage(), "\n"; } try { test($c); } catch (Exception $e) { echo $e->getMessage(), "\n"; } /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see php5/README.TESTING for further information on * writing regression tests */ ?> --EXPECT-- exception in __sleep 0 exception in __wakeup 2 igbinary-2.0.5/tests/igbinary_033.phpt0000644000175000017500000000155513177372464016710 0ustar tysontyson--TEST-- Object test, cyclic references --SKIPIF-- parent = null; $this->children = array(); } public function addChild(Foo $obj) { $this->children[] = $obj; $obj->setParent($this); } public function setParent(Foo $obj) { $this->parent = $obj; } } $obj1 = new Foo(); for ($i = 0; $i < 10; $i++) { $obj = new Foo(); $obj1->addChild($obj); } $o = igbinary_unserialize(igbinary_serialize($obj1->children)); foreach ($obj1->children as $k => $v) { $obj_v = $v; $o_v = $o[$k]; echo gettype($obj_v), "\t", gettype($o_v), "\n"; } --EXPECT-- object object object object object object object object object object object object object object object object object object object object igbinary-2.0.5/tests/igbinary_034.phpt0000644000175000017500000000116413177372464016705 0ustar tysontyson--TEST-- Unserialize invalid random data --SKIPIF-- 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); for ($j = 0; $j < 200; $j++) { for ($i = 0; $i < $len - 1; $i++) { $sub = substr($str, 0, $i); $sub .= mcrypt_create_iv(30, MCRYPT_DEV_URANDOM); $php_errormsg = null; $v = igbinary_unserialize($sub); } } } --EXPECT-- igbinary-2.0.5/tests/igbinary_040.phpt0000644000175000017500000000234713177372464016706 0ustar tysontyson--TEST-- b0rked random data test --SKIPIF-- --FILE-- --EXPECT-- igbinary-2.0.5/tests/igbinary_041.phpt0000644000175000017500000000416013177372464016702 0ustar tysontyson--TEST-- Check for double NaN, Inf, -Inf, 0, and -0. IEEE 754 doubles --FILE-- --INI-- igbinary.compact_strings=Off --FILE-- $object) { if (!isset($actual_array[$key])) { $error = 'ERROR'; echo "Key $key is missing from result.\n"; echo "Expected key/value:\n"; var_dump($key, $object); var_dump($object); break; } if (!is_object($actual_array[$key]) || get_class($object) !== get_class($actual_array[$key])) { $error = 'ERROR'; echo "Array mismatch on $key\n"; echo "Expected key/value:\n"; var_dump($key, $object); echo "Actual key/value:\n"; var_dump($key, $actual_array[$key]); break; } } echo $error, "\n"; --EXPECT-- OK igbinary-2.0.5/tests/igbinary_044.phpt0000644000175000017500000001122413177372464016704 0ustar tysontyson--TEST-- Check for double extremes --FILE-- getVersion(), '3.1.7', '<')) { echo "skip require APC version 3.1.7 or above"; } --INI-- apc.enable_cli=1 apc.serializer=igbinary --FILE-- int(10) } igbinary-2.0.5/tests/igbinary_045b.phpt0000644000175000017500000000107213177372464017047 0ustar tysontyson--TEST-- APCu serializer registration --SKIPIF-- getVersion(), '4.0.2', '<')) { echo "skip require APCu version 4.0.2 or above"; } --INI-- apc.enable_cli=1 apc.serializer=igbinary extension=apcu.so --FILE-- int(10) } igbinary-2.0.5/tests/igbinary_045c.phpt0000644000175000017500000000350613177372464017054 0ustar tysontyson--TEST-- APCu serializer registration - more data types --INI-- apc.enable_cli=1 apc.serializer=igbinary extension=apcu.so --SKIPIF-- getVersion(), '4.0.2') < 0) { echo "skip require APCu version 4.0.2 or above"; return; } if (PHP_MAJOR_VERSION >= 7) { if (version_compare($ext->getVersion(), '5.1.6') < 0) { echo "skip require APCu version 5.1.6 or above"; return; } } ?> --FILE-- foo = $a; apcu_store('objloop', $a); unset($a); var_dump(apcu_fetch('objloop')); apcu_store('nullval', null); var_dump(apcu_fetch('nullval')); apcu_store('intval', 777); var_dump(apcu_fetch('intval')); $o = new stdClass(); $o->prop = 5; $a = [$o, $o]; apcu_store('simplearrayval', $a); $unserialized = apcu_fetch('simplearrayval'); var_dump($unserialized); if ($unserialized[0] === $unserialized[1]) { echo "SAME\n"; } else { echo "DIFFERENT\n"; printf("%s\n", bin2hex(igbinary_serialize($a))); } unset($o); unset($a); unset($unserialized); $o = new stdClass(); $o->prop = 6; $a = [&$o, &$o]; apcu_store('refarrayval', $a); $unserialized = apcu_fetch('refarrayval'); var_dump($unserialized); if ($unserialized[0] === $unserialized[1]) { echo "SAME\n"; } else { echo "DIFFERENT\n"; printf("%s\n", bin2hex(igbinary_serialize($a))); } ?> --EXPECTF-- igbinary object(Bar)#%d (1) { ["foo"]=> *RECURSION* } NULL int(777) array(2) { [0]=> object(stdClass)#%d (1) { ["prop"]=> int(5) } [1]=> object(stdClass)#%d (1) { ["prop"]=> int(5) } } SAME array(2) { [0]=> &object(stdClass)#%d (1) { ["prop"]=> int(6) } [1]=> &object(stdClass)#%d (1) { ["prop"]=> int(6) } } SAME igbinary-2.0.5/tests/igbinary_046.phpt0000644000175000017500000000106413177372464016707 0ustar tysontyson--TEST-- Correctly unserialize scalar refs. --SKIPIF-- &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" } igbinary-2.0.5/tests/igbinary_046b.phpt0000644000175000017500000000130113177372464017043 0ustar tysontyson--TEST-- Correctly unserialize multiple object refs. --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" } igbinary-2.0.5/tests/igbinary_046c.phpt0000644000175000017500000000121313177372464017046 0ustar tysontyson--TEST-- Correctly unserialize multiple array refs. --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" } igbinary-2.0.5/tests/igbinary_046d.phpt0000644000175000017500000000252013177372464017051 0ustar tysontyson--TEST-- Correctly unserialize multiple object refs and non-refs. --SKIPIF-- object(stdClass)#%d (0) { } [1]=> &object(stdClass)#%d (0) { } [2]=> &object(stdClass)#%d (0) { } [3]=> object(stdClass)#%d (0) { } } a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;r:2;} 00000002140406001708737464436c61737314000601252201060225220106032201 a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;r:2;} array(4) { [0]=> object(stdClass)#%d (0) { } [1]=> &object(stdClass)#%d (0) { } [2]=> &object(stdClass)#%d (0) { } [3]=> object(stdClass)#%d (0) { } } array(4) { [0]=> object(stdClass)#%d (0) { } [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> object(stdClass)#%d (0) { } } igbinary-2.0.5/tests/igbinary_047.phpt0000644000175000017500000000404613177372464016713 0ustar tysontyson--TEST-- Check for serialization handler, SessionHandlerInterface --SKIPIF-- = 5.4.0) if (version_compare(phpversion(), "5.4.0", "<")) { exit("skip php version less than 5.4.x"); } if (!extension_loaded('session')) { exit('skip session extension not loaded'); } ob_start(); phpinfo(INFO_MODULES); $str = ob_get_clean(); $array = explode("\n", $str); $array = preg_grep('/^igbinary session support.*yes/', $array); if (!$array) { exit('skip igbinary session handler not available'); } --FILE-- --EXPECT-- bool(true) read wrote: 14021103666f6f06011104746573741106666f6f626172 igbinary-2.0.5/tests/igbinary_048.phpt0000644000175000017500000000146113177372464016712 0ustar tysontyson--TEST-- Object test, __set not called for private attr in extended class --SKIPIF-- = 5.4.0) if (version_compare(phpversion(), "5.4.0", "<")) { exit("skip php version less than 5.4.x"); } --FILE-- a = [1, 2, 3]; $x->nonexistent = 'aaa'; igbinary_unserialize(igbinary_serialize($x)); --EXPECT-- magic function called for nonexistent with 'aaa' igbinary-2.0.5/tests/igbinary_048b.phpt0000644000175000017500000000133113177372464017050 0ustar tysontyson--TEST-- Object test, __set not called for private attr in extended class --SKIPIF-- = 5.4.0) if (version_compare(phpversion(), "5.4.0", "<")) { exit("skip php version less than 5.4.x"); } --FILE-- a = [1, 2, 3]; $x->nonexistent = 'aaa'; unserialize(serialize($x)); --EXPECT-- magic function called for nonexistent with 'aaa' igbinary-2.0.5/tests/igbinary_049.phpt0000644000175000017500000000346613177372464016722 0ustar tysontyson--TEST-- Correctly unserialize multiple references in arrays --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- prop = &$a[8]; $a[11] = &$a[10]; $a[12] = $a[9]; $ig_ser = igbinary_serialize($a); printf("ig_ser=%s\n", bin2hex($ig_ser)); $ig = igbinary_unserialize($ig_ser); var_dump($ig); $f = &$ig[3]; $f = 'V'; $g = &$ig[5]; $g = 'H'; $h = $ig[10]; $h->prop = 'S'; var_dump($ig); --EXPECTF-- ig_ser=00000002140d0600251101410601250101060225010106032501010604250406052501020606251703466f6f1400060725220306082522030609140106000621060a251708737464436c6173731401110470726f70252203060b252205060c0104 array(13) { [0]=> &string(1) "A" [1]=> &string(1) "A" [2]=> &string(1) "A" [3]=> &string(1) "A" [4]=> &bool(false) [5]=> &bool(false) [6]=> &object(Foo)#%d (0) { } [7]=> &object(Foo)#%d (0) { } [8]=> &object(Foo)#%d (0) { } [9]=> array(1) { [0]=> int(33) } [10]=> &object(stdClass)#%d (1) { ["prop"]=> &object(Foo)#%d (0) { } } [11]=> &object(stdClass)#%d (1) { ["prop"]=> &object(Foo)#%d (0) { } } [12]=> array(1) { [0]=> int(33) } } array(13) { [0]=> &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" [4]=> &string(1) "H" [5]=> &string(1) "H" [6]=> &string(1) "S" [7]=> &string(1) "S" [8]=> &string(1) "S" [9]=> array(1) { [0]=> int(33) } [10]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } [11]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } [12]=> array(1) { [0]=> int(33) } } igbinary-2.0.5/tests/igbinary_049b.phpt0000644000175000017500000000264713177372464017064 0ustar tysontyson--TEST-- Correctly unserialize multiple references in objects --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- x0 = NULL; $a->x1 = &$a->x0; $a->x2 = &$a->x1; $a->x3 = &$a->x2; $a->x4 = false; $a->x5 = &$a->x4; $a->x6 = new Foo(); $a->x7 = &$a->x6; $a->x8 = &$a->x7; $a->x9 = array(33); $a->x10 = new stdClass(); $a->x10->prop = &$a->x8; $a->x11 = &$a->x10; $a->x12 = $a->x9; $ig_ser = igbinary_serialize($a); printf("ig_ser=%s\n", bin2hex($ig_ser)); $ig = igbinary_unserialize($ig_ser); $f = &$ig->x3; $f = 'V'; $g = &$ig->x5; $g = 'H'; $h = $ig->x10; $h->prop = 'S'; var_dump($ig); --EXPECTF-- ig_ser=000000021708737464436c617373140d1102783025001102783125010111027832250101110278332501011102783425041102783525010211027836251703466f6f14001102783725220311027838252203110278391401060006211103783130251a001401110470726f70252203110378313125220511037831320104 object(stdClass)#%d (13) { ["x0"]=> &string(1) "V" ["x1"]=> &string(1) "V" ["x2"]=> &string(1) "V" ["x3"]=> &string(1) "V" ["x4"]=> &string(1) "H" ["x5"]=> &string(1) "H" ["x6"]=> &string(1) "S" ["x7"]=> &string(1) "S" ["x8"]=> &string(1) "S" ["x9"]=> array(1) { [0]=> int(33) } ["x10"]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } ["x11"]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } ["x12"]=> array(1) { [0]=> int(33) } } igbinary-2.0.5/tests/igbinary_050.phpt0000644000175000017500000000202513177372464016700 0ustar tysontyson--TEST-- Correctly unserialize cyclic object references --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- foo = &$a; $a->bar = &$a; $b = new stdClass(); $b->cyclic = &$a; printf("%s\n", serialize($b)); $ig_ser = igbinary_serialize($b); printf("%s\n", bin2hex($ig_ser)); $ig = igbinary_unserialize($ig_ser); printf("%s\n", serialize($ig)); var_dump($ig); $f = &$ig->cyclic->foo; $f = 'V'; var_dump($ig); // Note: While the php7 unserializer consistently makes a distinction between refs to an object and non-refs, // the php5 serializer does not yet. --EXPECTF-- O:8:"stdClass":1:{s:6:"cyclic";O:8:"stdClass":2:{s:3:"foo";R:2;s:3:"bar";R:2;}} 000000021708737464436c617373140111066379636c6963251a0014021103666f6f2522011103626172252201 O:8:"stdClass":1:{s:6:"cyclic";O:8:"stdClass":2:{s:3:"foo";R:2;s:3:"bar";R:2;}} object(stdClass)#3 (1) { ["cyclic"]=> &object(stdClass)#4 (2) { ["foo"]=> *RECURSION* ["bar"]=> *RECURSION* } } object(stdClass)#3 (1) { ["cyclic"]=> &string(1) "V" } igbinary-2.0.5/tests/igbinary_051.phpt0000644000175000017500000000145713177372464016711 0ustar tysontyson--TEST-- Object test, __wakeup (With multiple references) --SKIPIF-- --FILE-- a = $a; $this->b = $b; } function __wakeup() { $this->b = $this->a * 3; } } function main() { $o = new Obj(1, 2); $variable = array(&$o, &$o); $serialized = igbinary_serialize($variable); $unserialized = igbinary_unserialize($serialized); echo substr(bin2hex($serialized), 8), "\n"; echo $unserialized[0]->b === 3 && $unserialized[0]->a === 1 ? 'OK' : 'ERROR'; echo "\n"; $unserialized[0] = 'a'; var_dump($unserialized); } main(); --EXPECT-- 140206002517034f626a1402110161060111016206020601252201 OK array(2) { [0]=> &string(1) "a" [1]=> &string(1) "a" } igbinary-2.0.5/tests/igbinary_052.phpt0000644000175000017500000000276513177372464016715 0ustar tysontyson--TEST-- Object Serializable interface can be serialized in references --SKIPIF-- --FILE-- a = $a; $this->b = $b; } public function serialize() { $c = self::$count++; echo "call serialize\n"; return pack('NN', $this->a, $this->b); } public function unserialize($serialized) { $tmp = unpack('N*', $serialized); $this->__construct($tmp[1], $tmp[2]); $c = self::$count++; echo "call unserialize\n"; } } function main() { $a = new Obj(1, 0); $b = new Obj(42, 43); $variable = array(&$a, &$a, $b); $serialized = igbinary_serialize($variable); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); $unserialized[0] = 'A'; var_dump($unserialized[1]); } main(); --EXPECTF-- call serialize call serialize 00000002140306002517034f626a1d080000000100000000060125220106021a001d080000002a0000002b call unserialize call unserialize array(3) { [0]=> &object(Obj)#%d (2) { ["a"]=> int(1) ["b"]=> int(0) } [1]=> &object(Obj)#%d (2) { ["a"]=> int(1) ["b"]=> int(0) } [2]=> object(Obj)#%d (2) { ["a"]=> int(42) ["b"]=> int(43) } } string(1) "A" igbinary-2.0.5/tests/igbinary_053.phpt0000644000175000017500000000213713177372464016707 0ustar tysontyson--TEST-- __wakeup can modify properties without affecting other objects --SKIPIF-- a = $a; } public function __wakeup() { echo "call wakeup\n"; $this->a[] = "end"; } } function main() { $array = ["test"]; // array (not a reference, but should be copied on write) $a = new Obj($array); $b = new Obj($array); $variable = [$a, $b]; $serialized = igbinary_serialize($variable); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); } main(); --EXPECTF-- 000000021402060017034f626a14011101611401060011047465737406011a0014010e010102 call wakeup call wakeup array(2) { [0]=> object(Obj)#%d (1) { ["a"]=> array(2) { [0]=> string(4) "test" [1]=> string(3) "end" } } [1]=> object(Obj)#%d (1) { ["a"]=> array(2) { [0]=> string(4) "test" [1]=> string(3) "end" } } } igbinary-2.0.5/tests/igbinary_054.phpt0000644000175000017500000000261013177372464016704 0ustar tysontyson--TEST-- __wakeup can add dynamic properties without affecting other objects --SKIPIF-- --FILE-- a being a dynamic property. function __construct($a) { $this->a = $a; } public function __wakeup() { echo "Calling __wakeup\n"; for ($i = 0; $i < 10000; $i++) { $this->{'b' . $i} = 42; } } } function main() { $array = array("roh"); // array (not a reference, but should be copied on write) $a = new Obj($array); $b = new Obj($array); $c = new Obj(null); $variable = array($a, $b, $c); $serialized = igbinary_serialize($variable); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); echo "Called igbinary_unserialize\n"; for ($a = 0; $a < 3; $a++) { for ($i = 0; $i < 10000; $i++) { if ($unserialized[$a]->{'b' . $i} !== 42) { echo "Fail $a b$i\n"; return; } unset($unserialized[$a]->{'b' . $i}); } } var_dump($unserialized); } main(); --EXPECTF-- 000000021403060017034f626a1401110161140106001103726f6806011a0014010e01010206021a0014010e0100 Calling __wakeup Calling __wakeup Calling __wakeup Called igbinary_unserialize array(3) { [0]=> object(Obj)#%d (1) { ["a"]=> array(1) { [0]=> string(3) "roh" } } [1]=> object(Obj)#%d (1) { ["a"]=> array(1) { [0]=> string(3) "roh" } } [2]=> object(Obj)#%d (1) { ["a"]=> NULL } } igbinary-2.0.5/tests/igbinary_055.phpt0000644000175000017500000000121613177372464016706 0ustar tysontyson--TEST-- __wakeup can replace a copy of the object referring to the root node. --SKIPIF-- --FILE-- a = $a; } public function __wakeup() { echo "Calling __wakeup\n"; $this->a = "replaced"; } } $a = new stdClass(); $a->obj = new Obj($a);; $serialized = igbinary_serialize($a); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); --EXPECTF-- 000000021708737464436c617373140111036f626a17034f626a14011101612200 Calling __wakeup object(stdClass)#%d (1) { ["obj"]=> object(Obj)#%d (1) { ["a"]=> string(8) "replaced" } } igbinary-2.0.5/tests/igbinary_057.phpt0000644000175000017500000000174713177372464016721 0ustar tysontyson--TEST-- Test serializing more strings than the capacity of the initial strings table. --SKIPIF-- --FILE-- --EXPECT-- 1432060011016106011101620602110163060311016406041101650605110166060611016706071101680608110169060911016a060a11016b060b11016c060c11016d060d11016e060e11016f060f11017006101101710611110172061211017306131101740614110175061511017606161101770617110178061811017906190e00061a0e01061b0e02061c0e03061d0e04061e0e05061f0e0606200e0706210e0806220e0906230e0a06240e0b06250e0c06260e0d06270e0e06280e0f06290e10062a0e11062b0e12062c0e13062d0e14062e0e15062f0e1606300e1706310e18 a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y igbinary-2.0.5/tests/igbinary_058.phpt0000644000175000017500000000270713177372464016717 0ustar tysontyson--TEST-- Should not call __destruct if __wakeup throws an exception --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- id = $id; $this->throws = $throws; $this->dynamic = "original"; } public function __wakeup() { printf("Calling __wakeup %s\n", $this->id); $this->dynamic = "copy"; if ($this->throws) { throw new Exception("__wakeup threw"); } } public function __destruct() { printf("Calling __destruct %s dynamic=%s\n", $this->id, $this->dynamic); } } function main() { $a = new Thrower("a", true); $serialized = igbinary_serialize($a); try { igbinary_unserialize($serialized); } catch (Exception $e) { printf("Caught %s\n", $e->getMessage()); } $a = null; print("Done a\n"); $b = new Thrower("b", false); $serialized = igbinary_serialize($b); $bCopy = igbinary_unserialize($serialized); print("Unserialized b\n"); var_dump($bCopy); $bCopy = null; $b = null; print("Done b\n"); } main(); --EXPECT-- Calling __wakeup a Caught __wakeup threw Calling __destruct a dynamic=original Done a Calling __wakeup b Unserialized b object(Thrower)#2 (3) { ["id"]=> string(1) "b" ["throws"]=> bool(false) ["dynamic"]=> string(4) "copy" } Calling __destruct b dynamic=copy Calling __destruct b dynamic=original Done b igbinary-2.0.5/tests/igbinary_058b.phpt0000644000175000017500000000272113177372464017055 0ustar tysontyson--TEST-- Should not call __destruct if __wakeup throws an exception (in arrays) --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- id = $id; $this->throws = $throws; $this->dynamic = "original"; } public function __wakeup() { printf("Calling __wakeup %s\n", $this->id); $this->dynamic = "copy"; if ($this->throws) { throw new Exception("__wakeup threw for id " . $this->id); } } public function __destruct() { printf("Calling __destruct %s dynamic=%s\n", $this->id, $this->dynamic); } } function main() { $values = [ 0 => new Thrower("a", false), 'foo' => 'last', 'key1' => new Thrower("b", false), 2 => new Thrower("c", true), 'last' => new Thrower("d", false), ]; $serialized = igbinary_serialize($values); $values = null; printf("Going to unserialize\n"); try { igbinary_unserialize($serialized); } catch (Exception $e) { printf("Caught %s\n", $e->getMessage()); } } main(); --EXPECT-- Calling __destruct a dynamic=original Calling __destruct b dynamic=original Calling __destruct c dynamic=original Calling __destruct d dynamic=original Going to unserialize Calling __wakeup a Calling __wakeup b Calling __wakeup c Calling __destruct a dynamic=copy Calling __destruct b dynamic=copy Caught __wakeup threw for id c igbinary-2.0.5/tests/igbinary_059.phpt0000644000175000017500000000236513177372464016720 0ustar tysontyson--TEST-- igbinary_unserialize should never convert from packed array to hash when references exist (Bug #48) --SKIPIF-- --FILE-- = 16) is added (converted to a hash), causing a segfault. foreach (array(0, 50) as $i) { $inner = new stdClass(); $inner->a = $i; $result[0][0][$i] = $inner; $result[1][] = $inner; } $serialized = igbinary_serialize($result); printf("%s\n", bin2hex(substr($serialized, 4))); flush(); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); } main(); ?> --EXPECTF-- 1402060014010600140206001708737464436c6173731401110161060006321a0014010e010632060114020600220306012204 array(2) { [0]=> array(1) { [0]=> array(2) { [0]=> object(stdClass)#%d (1) { ["a"]=> int(0) } [50]=> object(stdClass)#%d (1) { ["a"]=> int(50) } } } [1]=> array(2) { [0]=> object(stdClass)#%d (1) { ["a"]=> int(0) } [1]=> object(stdClass)#%d (1) { ["a"]=> int(50) } } } igbinary-2.0.5/tests/igbinary_060.phpt0000644000175000017500000000251713177372464016707 0ustar tysontyson--TEST-- Igbinary_unserialize_header warning --SKIPIF-- --FILE-- c = $this->b . 'OnWakeup'; } } error_reporting(E_ALL); // Start session session_start(); // The serialization of DateTime varies in php5 and php 7. $_SESSION['date'] = new \DateTime('@1234567890'); $a = new A(); $a->b = 'value'; $_SESSION['a'] = $a; var_dump($_SESSION['date']->getTimestamp()); $mydata = session_encode(); unset($_SESSION['date']); var_dump(session_decode($mydata)); var_dump($_SESSION['date']->getTimestamp()); var_dump($_SESSION['a']->c); ?> --EXPECT-- int(1234567890) bool(true) int(1234567890) string(13) "valueOnWakeup" igbinary-2.0.5/tests/igbinary_062.phpt0000644000175000017500000000206313177372464016705 0ustar tysontyson--TEST-- igbinary should not call __wakeup() if Serializable::unserialize was used to unserialize the object data (like `unserialize`) --FILE-- prop . "\n"; return $this->prop; } public function unserialize($data) { echo "In unserialize $data\n"; $this->prop = $data; } public function __wakeup() { echo "In __wakeup, unexpectedly\n"; } } function testA() { $a = new A(); $a->prop = 'other'; $ser = serialize($a); $b = unserialize($ser); var_dump($b); $c = new A(); $c->prop = 'igprop'; $serC = igbinary_serialize($c); echo bin2hex($serC) . "\n"; $d = igbinary_unserialize($serC); var_dump($d); } testA(); ?> --EXPECTF-- In serialize other In unserialize other object(A)#%d (1) { ["prop"]=> string(5) "other" } In serialize igprop 000000021701411d06696770726f70 In unserialize igprop object(A)#%d (1) { ["prop"]=> string(6) "igprop" } igbinary-2.0.5/tests/igbinary_063_php72.phpt0000644000175000017500000000227613177372464017734 0ustar tysontyson--TEST-- Accessing unserialized numbers. --SKIPIF-- --FILE-- 'x', 1234 => 33); var_dump($data); $x = "1"; $y = 1; $z = "1234"; $w = 1234; var_dump(isset($data->{$x}) ? $data->{$x} : "unset"); error_reporting(0); $str = igbinary_serialize($data); $unserialized = igbinary_unserialize($str); var_dump($unserialized); var_dump(isset($unserialized->{$x}) ? $unserialized->{$x} : "unset str"); var_dump(isset($unserialized->{$y}) ? $unserialized->{$y} : "unset int"); var_dump(isset($unserialized->{$z}) ? $unserialized->{$z} : "unset str 1234"); var_dump(isset($unserialized->{$w}) ? $unserialized->{$w} : "unset int 1234"); var_dump(isset($unserialized->{-1}) ? $unserialized->{-1} : "unset int -1"); ?> --EXPECT-- object(stdClass)#1 (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) object(stdClass)#2 (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) int(2) int(33) int(33) string(1) "x" igbinary-2.0.5/tests/igbinary_063_php7.phpt0000644000175000017500000000234413177372464017646 0ustar tysontyson--TEST-- Accessing unserialized numbers. --SKIPIF-- = 70200 || PHP_VERSION_ID < 70000) { echo "Skip php 7.1 or 7.0 required"; } ?> --FILE-- 'x', 1234 => 33); var_dump($data); $x = "1"; $y = 1; $z = "1234"; $w = 1234; var_dump(isset($data->{$x}) ? $data->{$x} : "unset"); error_reporting(0); $str = igbinary_serialize($data); $unserialized = igbinary_unserialize($str); var_dump($unserialized); var_dump(isset($unserialized->{$x}) ? $unserialized->{$x} : "unset str"); var_dump(isset($unserialized->{$y}) ? $unserialized->{$y} : "unset int"); var_dump(isset($unserialized->{$z}) ? $unserialized->{$z} : "unset str 1234"); var_dump(isset($unserialized->{$w}) ? $unserialized->{$w} : "unset int 1234"); var_dump(isset($unserialized->{-1}) ? $unserialized->{-1} : "unset int -1"); ?> --EXPECT-- object(stdClass)#1 (5) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [-1]=> string(1) "x" [1234]=> int(33) } string(5) "unset" object(stdClass)#2 (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) int(2) int(33) int(33) string(1) "x" igbinary-2.0.5/tests/igbinary_063.phpt0000644000175000017500000000231613177372464016707 0ustar tysontyson--TEST-- Accessing unserialized numbers. --SKIPIF-- = 70000) { echo "Skip php 5.6 or lower required"; } ?> --FILE-- 'x', 1234 => 33); var_dump($data); $x = "1"; $y = 1; $z = "1234"; $w = 1234; var_dump(isset($data->{$x}) ? $data->{$x} : "unset"); error_reporting(0); $str = igbinary_serialize($data); $unserialized = igbinary_unserialize($str); var_dump($unserialized); var_dump(isset($unserialized->{$x}) ? $unserialized->{$x} : "unset str"); var_dump(isset($unserialized->{$y}) ? $unserialized->{$y} : "unset int"); var_dump(isset($unserialized->{$z}) ? $unserialized->{$z} : "unset str 1234"); var_dump(isset($unserialized->{$w}) ? $unserialized->{$w} : "unset int 1234"); var_dump(isset($unserialized->{-1}) ? $unserialized->{-1} : "unset int -1"); ?> --EXPECTF-- object(stdClass)#%d (5) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [-1]=> string(1) "x" [1234]=> int(33) } string(5) "unset" object(stdClass)#%d (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) int(2) int(33) int(33) string(1) "x" igbinary-2.0.5/tests/igbinary_064.phpt0000644000175000017500000000424313177372464016711 0ustar tysontyson--TEST-- Works when there are hash collisions in strings when serializing. --SKIPIF-- --FILE-- x = $x; } } class Ez { public $FyEz = 'EzEz'; } class G8 { public $FyG8; } $data = array(new Fy('G8G8'), new Fy('EzG8'), new Ez(), new G8(), new Ez(), 'G8' => new G8(), 'F8Ez' => new G8(), array(new G8())); var_dump($data); echo "\n"; $str = igbinary_serialize($data); echo bin2hex($str) . "\n"; $unserialized = igbinary_unserialize($str); var_dump($unserialized); echo "\n"; var_export(serialize($data) === serialize($unserialized)); ?> --EXPECT-- array(8) { [0]=> object(Fy)#1 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "G8G8" } [1]=> object(Fy)#2 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "EzG8" } [2]=> object(Ez)#3 (1) { ["FyEz"]=> string(4) "EzEz" } [3]=> object(G8)#4 (1) { ["FyG8"]=> NULL } [4]=> object(Ez)#5 (1) { ["FyEz"]=> string(4) "EzEz" } ["G8"]=> object(G8)#6 (1) { ["FyG8"]=> NULL } ["F8Ez"]=> object(G8)#7 (1) { ["FyG8"]=> NULL } [5]=> array(1) { [0]=> object(G8)#8 (1) { ["FyG8"]=> NULL } } } 00000002140806001702467914021104457a4679060211017811044738473806011a0014020e0106020e021104457a473806021702457a140111044679457a1104457a457a06031702473814011104467947380006041a0514010e060e070e081a0814010e090011044638457a1a0814010e09000605140106001a0814010e0900 array(8) { [0]=> object(Fy)#9 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "G8G8" } [1]=> object(Fy)#10 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "EzG8" } [2]=> object(Ez)#11 (1) { ["FyEz"]=> string(4) "EzEz" } [3]=> object(G8)#12 (1) { ["FyG8"]=> NULL } [4]=> object(Ez)#13 (1) { ["FyEz"]=> string(4) "EzEz" } ["G8"]=> object(G8)#14 (1) { ["FyG8"]=> NULL } ["F8Ez"]=> object(G8)#15 (1) { ["FyG8"]=> NULL } [5]=> array(1) { [0]=> object(G8)#16 (1) { ["FyG8"]=> NULL } } } true igbinary-2.0.5/tests/igbinary_bug54662.phpt0000644000175000017500000000110313177372464017554 0ustar tysontyson--TEST-- Nested objects cause segfault, php bug #54662 --SKIPIF-- --FILE-- append(new Storage); $ser = igbinary_serialize($collection); $new_collection = igbinary_unserialize($ser); var_dump($new_collection[0]->storage); --EXPECT-- string(8) "a string" igbinary-2.0.5/tests/igbinary_bug72134.phpt0000644000175000017500000000077613177372464017565 0ustar tysontyson--TEST-- igbinary_unserialize causes segfault on 3rd call for objects with dynamic property --FILE-- i = 1; $igb = igbinary_serialize($value); for ($i=0; $i < 30; $i++) { // This used to segfault at the third attempt echo igbinary_unserialize($igb)->bar . PHP_EOL; } --EXPECT-- test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test igbinary-2.0.5/tests/igbinary_unserialize_v1_compatible.phpt0000644000175000017500000000526613177372464023545 0ustar tysontyson--TEST-- Unserialize backwards compatible with v1. --SKIPIF-- --FILE-- 'b:1;', 'type' => 'boolean', 'description' => 'bool true', 'data' => 'AAAAAQU=', 'version' => 1, ), array( 'var' => 'b:0;', 'type' => 'boolean', 'description' => 'bool false', 'data' => 'AAAAAQQ=', 'version' => 1, ), array( 'var' => 'd:1.2881887378882661554513333612703718245029449462890625;', 'type' => 'double', 'description' => 'double', 'data' => 'AAAAAQw/9Jxry0Tj0Q==', 'version' => 1, ), array( 'var' => 'i:29913;', 'type' => 'integer', 'description' => 'int', 'data' => 'AAAAAQh02Q==', 'version' => 1, ), array( 'var' => 'N;', 'type' => 'NULL', 'description' => 'null', 'data' => 'AAAAAQA=', 'version' => 1, ), array( 'var' => 's:0:"";', 'type' => 'string', 'description' => 'string', 'data' => 'AAAAAQ0=', 'version' => 1, ), array( 'var' => 's:13:"asdf' . "\0" . 'asdfasdf";', 'type' => 'string', 'description' => 'string', 'data' => 'AAAAARENYXNkZgBhc2RmYXNkZg==', 'version' => 1, ), array( 'var' => 'a:4:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;}', 'type' => 'array', 'description' => 'array', 'data' => 'AAAAARQEBgAGAQYBBgIGAgYDBgMGBA==', 'version' => 1, ), array( 'var' => 'O:8:"stdClass":4:{s:1:"a";i:1;s:1:"b";i:2;s:1:"c";i:3;s:1:"d";i:4;}', 'var_e' => (object)array("a"=>1, "b"=>2, "c"=>3, "d"=>4), 'type' => 'object', 'description' => 'object', 'data' => 'AAAAAhcIc3RkQ2xhc3MUBBEBYQYBEQFiBgIRAWMGAxEBZAYE', 'version' => 1, ), array( 'var' => 'a:2:{i:0;a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}i:1;R:2;}', 'type' => 'array', 'description' => 'reference', 'data' => 'AAAAARQCBgAUAwYAEQFhBgERAWIGAhEBYwYBAQE=', 'version' => 1, ), ); $all_passed = true; foreach ($data as $item) { if (isset($item['var_e'])) { $var = $item['var_e']; } else { $var = unserialize($item['var']); } $unserialized = igbinary_unserialize(base64_decode($item['data'])); ob_start(); var_dump($var); $dump_expected = ob_get_clean(); ob_start(); var_dump($unserialized); $dump_actual = ob_get_clean(); // replace all object ids to 0 $dump_expected = preg_replace('/#\d+/', '#0', $dump_expected); $dump_actual = preg_replace('/#\d+/', '#0', $dump_actual); if ($dump_expected !== $dump_actual) { if ($item['description'] == 'reference') { echo "reference deserialization works, but the result is not a reference.\n"; continue; } echo "Differing unserialized: {$item['description']}\n"; echo "Expected:\n", $dump_expected, "\n"; echo "Actual:\n", $dump_actual, "\n"; } } echo $all_passed ? 'OK' : 'ERROR', "\n"; --EXPECT-- reference deserialization works, but the result is not a reference. OK igbinary-2.0.5/config.m40000644000175000017500000000622313177372464014164 0ustar tysontysondnl config.m4 for extension igbinary dnl Comments in this file start with the string 'dnl'. dnl Remove where necessary. This file will not work dnl without editing. dnl If your extension references something external, use with: dnl PHP_ARG_WITH(igbinary, for igbinary support, dnl Make sure that the comment is aligned: dnl [ --with-igbinary Include igbinary support]) dnl Otherwise use enable: PHP_ARG_ENABLE(igbinary, whether to enable igbinary support, [ --enable-igbinary Enable igbinary support]) if test "$PHP_IGBINARY" != "no"; then AC_CHECK_HEADERS([stdbool.h],, AC_MSG_ERROR([stdbool.h not exists])) AC_CHECK_HEADERS([stddef.h],, AC_MSG_ERROR([stddef.h not exists])) AC_CHECK_HEADERS([stdint.h],, AC_MSG_ERROR([stdint.h not exists])) AC_MSG_CHECKING(PHP version) AC_TRY_COMPILE([ #include <$phpincludedir/main/php_version.h> ],[ #if PHP_MAJOR_VERSION > 5 #error PHP > 5 #endif ],[ subdir=src/php5 PHP_IGBINARY_SRC_FILES="$subdir/igbinary.c $subdir/hash_si.c $subdir/hash_si_ptr.c" AC_MSG_RESULT([PHP 5]) ],[ subdir=src/php7 PHP_IGBINARY_SRC_FILES="$subdir/igbinary.c $subdir/hash_si.c $subdir/hash_si_ptr.c" AC_MSG_RESULT([PHP 7]) ]) AC_MSG_CHECKING([for APC/APCU includes]) if test -f "$phpincludedir/ext/apcu/apc_serializer.h"; then apc_inc_path="$phpincludedir" AC_MSG_RESULT([APCU in $apc_inc_path]) AC_DEFINE(HAVE_APCU_SUPPORT,1,[Whether to enable apcu support]) elif test "$subdir" == src/php5 && test -f "$phpincludedir/ext/apc/apc_serializer.h"; then apc_inc_path="$phpincludedir" AC_MSG_RESULT([APC in $apc_inc_path]) AC_DEFINE(HAVE_APC_SUPPORT,1,[Whether to enable apc support]) elif test "$subdir" == src/php5 && test -f "${srcdir}/$subdir/apc_serializer.h"; then AC_MSG_RESULT([apc_serializer.h bundled]) AC_DEFINE(HAVE_APC_SUPPORT,1,[Whether to enable apc support]) AC_DEFINE(USE_BUNDLED_APC,1,[Whether to use bundled apc includes]) else AC_MSG_RESULT([not found]) fi AC_CHECK_SIZEOF([long]) dnl GCC AC_MSG_CHECKING(compiler type) if test ! -z "`$CC --version | grep -i CLANG`"; then AC_MSG_RESULT(clang) if test -z "`echo $CFLAGS | grep -- -O0`"; then PHP_IGBINARY_CFLAGS="$CFLAGS -Wall -O2" fi elif test "$GCC" = yes; then AC_MSG_RESULT(gcc) if test -z "`echo $CFLAGS | grep -- '-O[0123]'`"; then PHP_IGBINARY_CFLAGS="$CFLAGS -O2 -Wall -Wpointer-arith -Wmissing-prototypes -Wstrict-prototypes -Wcast-align -Wshadow -Wwrite-strings -Wswitch -finline-limit=10000 --param large-function-growth=10000 --param inline-unit-growth=10000" fi elif test "$ICC" = yes; then AC_MSG_RESULT(icc) if test -z "`echo $CFLAGS | grep -- -O0`"; then PHP_IGBINARY_CFLAGS="$CFLAGS -no-prec-div -O3 -x0 -unroll2" fi else AC_MSG_RESULT(other) fi PHP_ADD_MAKEFILE_FRAGMENT(Makefile.bench) PHP_INSTALL_HEADERS([ext/igbinary], [igbinary.h $subdir/igbinary.h php_igbinary.h $subdir/php_igbinary.h]) PHP_NEW_EXTENSION(igbinary, $PHP_IGBINARY_SRC_FILES, $ext_shared,, $PHP_IGBINARY_CFLAGS) PHP_ADD_EXTENSION_DEP(igbinary, session, true) PHP_ADD_BUILD_DIR($abs_builddir/$subdir, 1) PHP_SUBST(IGBINARY_SHARED_LIBADD) fi igbinary-2.0.5/config.w320000644000175000017500000000410213177372464014251 0ustar tysontyson// $Id$ // vim:ft=javascript ARG_ENABLE("igbinary", "whether to enable igbinary support", "no"); if (PHP_IGBINARY == "yes") { var dll = get_define('PHPDLL'); var is_php5 = null != dll.match(/^php5/); var is_php7 = !is_php5 && null != dll.match(/^php7/) if (CHECK_HEADER_ADD_INCLUDE("apc_serializer.h", "CFLAGS_IGBINARY", "..\\pecl\\apcu;ext\\apcu")) { AC_DEFINE('HAVE_APCU_SUPPORT', 1, "Whether to enable apcu support"); if (!CHECK_HEADER_ADD_INCLUDE("apc_serializer.h", "CFLAGS_IGBINARY", "ext\\apcu")) { // Workaround to allow configuring and making apcu and igbinary at the same time. // If they aren't available in ext/apcu, expect them in ../pecl/apcu AC_DEFINE('HAVE_APCU_HEADERS_IN_PECL', 1, "Whether or not apcu headers exist only in pecl folder") } } else if (is_php5 && CHECK_HEADER_ADD_INCLUDE("apc_serializer.h", "CFLAGS_IGBINARY", "..\\pecl\\apc;ext\\apc")) { // Don't try ext/apc unless this is a php 5 build. // For some reason, the header files still exist for pecl.php.net 7.0 builds. AC_DEFINE('HAVE_APC_SUPPORT', 1, 'Whether to enable apc support') } var old_conf_dir = configure_module_dirname; var php_igbinary_src_files; var subdir; /* Copied from solr config.w32 */ /* XXX tricky job here, override configure_module_dirname, define the basic extension, then set it back*/ if (is_php5) { subdir = "src\\php5" php_igbinary_src_files = "igbinary.c hash_si.c hash_si_ptr.c" } else if (is_php7) { subdir = "src\\php7"; php_igbinary_src_files = "igbinary.c hash_si.c hash_si_ptr.c" } else { ERROR("Cannot match any known PHP version with '" + dll + "'"); } configure_module_dirname = configure_module_dirname + "\\" + subdir; EXTENSION("igbinary", php_igbinary_src_files); configure_module_dirname = old_conf_dir; AC_DEFINE('HAVE_IGBINARY', 1, 'Have igbinary support', false); ADD_EXTENSION_DEP('igbinary', 'session'); PHP_INSTALL_HEADERS('ext/igbinary', 'igbinary.h php_igbinary.h ' + subdir + '\\igbinary.h ' + subdir + '\\php_igbinary.h'); } igbinary-2.0.5/igbinary.h0000644000175000017500000000044713177372464014434 0ustar tysontyson#ifndef PHPEXT_IGBINARY_BASE_IGBINARY_H #define PHPEXT_IGBINARY_BASE_IGBINARY_H #include "php_version.h" #if PHP_MAJOR_VERSION == 5 #include "src/php5/igbinary.h" #elif PHP_MAJOR_VERSION == 7 #include "src/php7/igbinary.h" #else #error "Unsupported php version for igbinary build" #endif #endif igbinary-2.0.5/php_igbinary.h0000644000175000017500000000155513177372464015304 0ustar tysontyson#ifndef PHPEXT_IGBINARY_BASE_PHP_IGBINARY_H #define PHPEXT_IGBINARY_BASE_PHP_IGBINARY_H #include "php_version.h" #if PHP_MAJOR_VERSION == 5 #include "ext/igbinary/src/php5/php_igbinary.h" #elif PHP_MAJOR_VERSION == 7 #include "ext/igbinary/src/php7/php_igbinary.h" #else #error "Unsupported php version for igbinary build" #endif /** * The below line is redundant and identical to php5 and php7's line * (and just mentioning phpext_ in a comment is sufficient). * This block is only here to make php-src/build/print_include.awk include this file, * when igbinary is placed in php-src/ext/igbinary/ (instead of compiled separately with `phpize; ...`) */ #ifndef phpext_igbinary_ptr extern zend_module_entry igbinary_module_entry; #define phpext_igbinary_ptr &igbinary_module_entry #endif /** End line needed for putting igbinary in php-src/ext/igbinary to work */ #endif igbinary-2.0.5/src/php5/hash.h0000644000175000017500000000726413177372464015222 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef HASH_H #define HASH_H #include #ifdef PHP_WIN32 # include "ig_win32.h" #else # include /* defines uint32_t etc */ #endif #include /** Key/value pair of hash_si. * @author Oleg Grenrus * @see hash_si */ struct hash_si_pair { char *key; /**< Pointer to key. */ size_t key_len; /**< Key length. */ uint32_t key_hash; /**< Key hash. */ uint32_t value; /**< Value. */ }; enum hash_si_code { hash_si_code_inserted, hash_si_code_exists, hash_si_code_exception }; struct hash_si_result { enum hash_si_code code; uint32_t value; }; /** Hash-array. * Like c++ map. * Current implementation uses linear probing. * @author Oleg Grenrus */ struct hash_si { size_t size; /**< Allocated size of array. */ size_t used; /**< Used size of array. */ struct hash_si_pair *data; /**< Pointer to array or pairs of data. */ }; /** Inits hash_si structure. * @param h pointer to hash_si struct. * @param size initial size of the hash array. * @return 0 on success, 1 else. */ int hash_si_init (struct hash_si *h, size_t size); /** Frees hash_si structure. * Doesn't call free(h). * @param h pointer to hash_si struct. */ void hash_si_deinit (struct hash_si *h); /** Inserts value into hash_si. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param value Value. * @return 0 on success, 1 or 2 else. */ /* int hash_si_insert (struct hash_si *h, const char *key, size_t key_len, uint32_t value); */ /** Finds value from hash_si. * Value returned thru value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ /* int hash_si_find (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); */ /** Finds value from hash_si. * Value returned thru value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ struct hash_si_result hash_si_find_or_insert(struct hash_si *h, const char *key, size_t key_len, uint32_t value); /** Remove value from hash_si. * Removed value is available thru value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Removed value. * @return 0 ivalue removed, 1 if not existed. */ /* int hash_si_remove (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); */ /** Travarses hash_si. * Calls traverse_function on every item. Traverse function should not modify hash * @param h Pointer to hash_si struct. * @param traverse_function Function to call on every item of hash_si. */ /* void hash_si_traverse (struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)); */ /** Returns size of hash_si. * @param h Pointer to hash_si struct. * @return Size of hash_si. */ size_t hash_si_size (struct hash_si *h); /** Returns capacity of hash_si. * @param h Pointer to hash_si struct. * @return Capacity of hash_si. */ size_t hash_si_capacity (struct hash_si *h); #endif /* HASH_H */ igbinary-2.0.5/src/php5/hash_ptr.h0000644000175000017500000000653113177372464016103 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | | This defines hash_si_ptr. | | It is like hash_si, but the key is always a non-zero zend_uintptr_t | +----------------------------------------------------------------------+ */ #ifndef HASH_PTR_H #define HASH_PTR_H #include #ifdef PHP_WIN32 # include "ig_win32.h" #else # include /* defines uint32_t etc */ #endif #include #include "zend_types.h" // NULL converted to an integer, on sane platforms. #define HASH_PTR_KEY_INVALID 0 /** Key/value pair of hash_si_ptr. * @author Oleg Grenrus * @see hash_si_ptr */ struct hash_si_ptr_pair { zend_uintptr_t key; /**< The key: The address of a pointer, casted to an int (won't be dereferenced). */ uint32_t value; /**< Value. */ }; /** Hash-array. * Like c++ std::unordered_map, but does not allow HASH_PTR_KEY_INVALID as a key. * Current implementation uses linear probing. * @author Oleg Grenrus */ struct hash_si_ptr { size_t size; /**< Allocated size of array. */ size_t used; /**< Used size of array. */ struct hash_si_ptr_pair *data; /**< Pointer to array or pairs of data. */ }; /** Inits hash_si_ptr structure. * @param h pointer to hash_si_ptr struct. * @param size initial size of the hash array. * @return 0 on success, 1 else. */ int hash_si_ptr_init(struct hash_si_ptr *h, size_t size); /** Frees hash_si_ptr structure. * Doesn't call free(h). * @param h pointer to hash_si_ptr struct. */ void hash_si_ptr_deinit(struct hash_si_ptr *h); /** Inserts value into hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @param key_len Key length. * @param value Value. * @return 0 on success, 1 or 2 else. */ /* int hash_si_ptr_insert (struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value); */ /** Finds value from hash_si_ptr. * Value returned thru value param. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ /* int hash_si_ptr_find (struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t * value); */ /** Returns size of hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @return Size of hash_si_ptr. */ size_t hash_si_ptr_size (struct hash_si_ptr *h); /** * If the key does not exist, add a mapping from key to value and returns SIZE_MAX * If the key does exist, return the corresponding value * @param h Pointer to hash_si_ptr struct. * @param key key to look up or add * @param value value to insert if it doesn't already exist * * @return SIZE_MAX or old, unmodified key */ size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value); /** Returns capacity of hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @return Capacity of hash_si_ptr. */ size_t hash_si_ptr_capacity (struct hash_si_ptr *h); #endif /* HASH_PTR_H */ igbinary-2.0.5/src/php5/hash_si.c0000644000175000017500000001233113177372464015677 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef PHP_WIN32 # include "ig_win32.h" #endif #include #include #include #include #include "hash.h" #include "zend.h" #include "igbinary_macros.h" /* {{{ nextpow2 */ /** Next power of 2. * @param n Integer. * @return next to n power of 2 . */ inline static uint32_t nextpow2(uint32_t n) { uint32_t m = 1; while (m < n) { m = m << 1; } return m; } /* }}} */ /* {{{ hash_si_init */ int hash_si_init(struct hash_si *h, size_t size) { size = nextpow2(size); h->size = size; h->used = 0; h->data = (struct hash_si_pair *) emalloc(sizeof(struct hash_si_pair) * size); if (h->data == NULL) { return 1; } memset(h->data, 0, sizeof(struct hash_si_pair) * size); return 0; } /* }}} */ /* {{{ hash_si_deinit */ void hash_si_deinit(struct hash_si *h) { size_t i; for (i = 0; i < h->size; i++) { if (h->data[i].key != NULL) { efree(h->data[i].key); } } efree(h->data); h->size = 0; h->used = 0; } /* }}} */ /* {{{ _hash_si_find */ /** Returns index of key, or where it should be. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param key_hash precomputed key hash of bytes of key (=zend_inline_hash_func(key, key_len)) * @return index. */ inline static size_t _hash_si_find(const struct hash_si *h, const char *key, const size_t key_len, const uint32_t key_hash) { uint32_t hv; size_t size; size_t mask; assert(h != NULL); size = h->size; mask = size - 1; hv = key_hash & mask; while (size > 0 && h->data[hv].key != NULL && (h->data[hv].key_hash != key_hash || h->data[hv].key_len != key_len || UNEXPECTED(memcmp(h->data[hv].key, key, key_len) != 0))) { /* linear prob */ hv = (hv + 1) & mask; size--; } return hv; } /* }}} */ /* {{{ hash_si_rehash */ /** Rehash/resize hash_si. * @param h Pointer to hash_si struct. */ inline static void hash_si_rehash(struct hash_si *h) { size_t i; struct hash_si newh; assert(h != NULL); hash_si_init(&newh, h->size * 2); for (i = 0; i < h->size; i++) { const struct hash_si_pair *old_pair = &(h->data[i]); if (old_pair->key != NULL) { uint32_t hv = _hash_si_find(&newh, old_pair->key, old_pair->key_len, old_pair->key_hash); newh.data[hv] = *old_pair; } } efree(h->data); h->data = newh.data; h->size *= 2; } /* }}} */ /* {{{ hash_si_insert */ /* int hash_si_insert(struct hash_si *h, const char *key, size_t key_len, uint32_t value) { uint32_t hv; if (h->size / 4 * 3 < h->used + 1) { hash_si_rehash(h); } hv = _hash_si_find(h, key, key_len); if (h->data[hv].key == NULL) { h->data[hv].key = (char *) emalloc(key_len + 1); if (h->data[hv].key == NULL) { return 1; } memcpy(h->data[hv].key, key, key_len); h->data[hv].key[key_len] = '\0'; h->data[hv].key_len = key_len; h->used++; } else { return 2; } h->data[hv].value = value; return 0; } */ /* }}} */ /* {{{ hash_si_find */ /* int hash_si_find(struct hash_si *h, const char *key, size_t key_len, uint32_t *value) { uint32_t hv; assert(h != NULL); hv = _hash_si_find(h, key, key_len); if (h->data[hv].key == NULL) { return 1; } else { *value = h->data[hv].value; return 0; } } */ /* }}} */ /* {{{ hash_si_find_or_insert */ struct hash_si_result hash_si_find_or_insert(struct hash_si *h, const char *key, size_t key_len, uint32_t value) { uint32_t hv; uint32_t key_hash; struct hash_si_result result; struct hash_si_pair *pair; assert(h != NULL); key_hash = zend_inline_hash_func(key, key_len); hv = _hash_si_find(h, key, key_len, key_hash); pair = &h->data[hv]; if (pair->key == NULL) { char* copy = emalloc(key_len); if (copy == NULL) { result.code = hash_si_code_exception; return result; } memcpy(copy, key, key_len); pair->key = copy; pair->key_len = key_len; pair->key_hash = key_hash; pair->value = value; h->used++; if (h->size / 4 * 3 < h->used) { hash_si_rehash(h); } result.code = hash_si_code_inserted; return result; } else { result.code = hash_si_code_exists; result.value = h->data[hv].value; return result; } } /* }}} */ /* {{{ hash_si_traverse */ /* void hash_si_traverse(struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)) { size_t i; assert(h != NULL && traverse_function != NULL); for (i = 0; i < h->size; i++) { if (h->data[i].key != NULL && traverse_function(h->data[i].key, h->data[i].key_len, h->data[i].value) != 1) { return; } } } */ /* }}} */ /* {{{ hash_si_size */ size_t hash_si_size(struct hash_si *h) { assert(h != NULL); return h->used; } /* }}} */ /* {{{ hash_si_capacity */ size_t hash_si_capacity(struct hash_si *h) { assert(h != NULL); return h->size; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-2.0.5/src/php5/hash_si_ptr.c0000644000175000017500000001201713177372464016565 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | | This is a specialized hash map mapping uintprt_t to int32_t | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | Modified by Tyson Andre for fixed size, removing unused functions | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else # include #endif #include #include #include #include #include "hash_ptr.h" #include "zend.h" /* Function similar to zend_inline_hash_func. This is not identical. */ inline static uint32_t inline_hash_of_address(zend_uintptr_t ptr) { register uint32_t hash = 5381; /* Note: Hash the least significant bytes first - Those need to influence the final result as much as possible. */ hash = ((hash << 5) + hash) + (ptr & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 8) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 16) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 24) & 0xff); #if UINTPTR_MAX > UINT32_MAX hash = ((hash << 5) + hash) + ((ptr >> 32) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 40) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 48) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 56) & 0xff); #endif return hash; } /* {{{ nextpow2 */ /** Next power of 2. * @param n Integer. * @return next to n power of 2 . */ inline static uint32_t nextpow2(uint32_t n) { uint32_t m = 1; while (m < n) { m = m << 1; } return m; } /* }}} */ /* {{{ hash_si_ptr_init */ int hash_si_ptr_init(struct hash_si_ptr *h, size_t size) { size = nextpow2(size); h->size = size; h->used = 0; h->data = (struct hash_si_ptr_pair*) malloc(sizeof(struct hash_si_ptr_pair) * size); if (h->data == NULL) { return 1; } memset(h->data, 0, sizeof(struct hash_si_ptr_pair) * size); /* Set everything to 0. sets keys to HASH_PTR_KEY_INVALID. */ return 0; } /* }}} */ /* {{{ hash_si_ptr_deinit */ void hash_si_ptr_deinit(struct hash_si_ptr *h) { size_t i; free(h->data); h->data = NULL; h->size = 0; h->used = 0; } /* }}} */ /* {{{ _hash_si_ptr_find */ /** Returns index of key, or where it should be. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @return index. */ inline static size_t _hash_si_ptr_find(struct hash_si_ptr *h, const zend_uintptr_t key) { uint32_t hv; size_t size; assert(h != NULL); size = h->size; hv = inline_hash_of_address(key) & (h->size-1); while (size > 0 && h->data[hv].key != HASH_PTR_KEY_INVALID && h->data[hv].key != key) { /* linear prob */ hv = (hv + 1) & (h->size-1); size--; } return hv; } /* }}} */ /* }}} */ /* {{{ hash_si_ptr_rehash */ /** Rehash/resize hash_si_ptr. * @param h Pointer to hash_si_ptr struct. */ inline static void hash_si_ptr_rehash(struct hash_si_ptr *h) { uint32_t hv; size_t i; struct hash_si_ptr newh; assert(h != NULL); hash_si_ptr_init(&newh, h->size * 2); for (i = 0; i < h->size; i++) { if (h->data[i].key != HASH_PTR_KEY_INVALID) { hv = _hash_si_ptr_find(&newh, h->data[i].key); newh.data[hv].key = h->data[i].key; newh.data[hv].value = h->data[i].value; } } free(h->data); h->data = newh.data; h->size *= 2; } /* }}} */ /* {{{ hash_si_ptr_insert */ /* int hash_si_ptr_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value) { uint32_t hv; if (h->size / 4 * 3 < h->used + 1) { hash_si_ptr_rehash(h); } hv = _hash_si_ptr_find(h, key); if (h->data[hv].key == HASH_PTR_KEY_INVALID) { h->data[hv].key = key; h->used++; } else { return 2; } h->data[hv].value = value; return 0; } */ /* }}} */ /* {{{ hash_si_ptr_find */ /* int hash_si_ptr_find(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t *value) { uint32_t hv; assert(h != NULL); hv = _hash_si_ptr_find(h, key); if (h->data[hv].key == HASH_PTR_KEY_INVALID) { return 1; } else { *value = h->data[hv].value; return 0; } } */ /* }}} */ /* {{{ hash_si_ptr_find_or_insert */ size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value) { uint32_t hv; hv = _hash_si_ptr_find(h, key); if (h->data[hv].key == HASH_PTR_KEY_INVALID) { h->data[hv].key = key; h->data[hv].value = value; h->used++; /* The size increased, so check if we need to expand the map */ if (h->size / 4 * 3 < h->used) { hash_si_ptr_rehash(h); } return SIZE_MAX; } else { return h->data[hv].value; } } /* }}} */ /* {{{ hash_si_ptr_size */ size_t hash_si_ptr_size(struct hash_si_ptr *h) { assert(h != NULL); return h->used; } /* }}} */ /* {{{ hash_si_ptr_capacity */ size_t hash_si_ptr_capacity(struct hash_si_ptr *h) { assert(h != NULL); return h->size; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-2.0.5/src/php5/igbinary.c0000644000175000017500000022077313177372464016100 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PHP_WIN32 # include "ig_win32.h" #endif #include "php.h" #include "php_ini.h" #include "zend_dynamic_array.h" #include "zend_alloc.h" #include "ext/standard/info.h" #include "ext/standard/php_var.h" #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) # include "ext/session/php_session.h" #endif /* HAVE_PHP_SESSION */ #include "ext/standard/php_incomplete_class.h" #if defined(HAVE_APCU_SUPPORT) # if defined(HAVE_APCU_HEADERS_IN_PECL) # include "../pecl/apcu/apc_serializer.h" # else # include "ext/apcu/apc_serializer.h" # endif #elif defined(HAVE_APC_SUPPORT) # if USE_BUNDLED_APC # include "apc_serializer.h" # else # include "ext/apc/apc_serializer.h" # endif #endif /* HAVE_APCU_SUPPORT || HAVE_APC_SUPPORT */ #include "php_igbinary.h" #include "igbinary.h" #include #include #ifndef PHP_WIN32 # include # include # include #endif #include #include "hash.h" #include "hash_ptr.h" #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) /** Session serializer function prototypes. */ PS_SERIALIZER_FUNCS(igbinary); #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ #if defined(HAVE_APC_SUPPORT) || defined(HAVE_APCU_SUPPORT) /** Apc serializer function prototypes */ static int APC_SERIALIZER_NAME(igbinary) (APC_SERIALIZER_ARGS); static int APC_UNSERIALIZER_NAME(igbinary) (APC_UNSERIALIZER_ARGS); #endif /* {{{ Types */ enum igbinary_type { /* 00 */ igbinary_type_null, /**< Null. */ /* 01 */ igbinary_type_ref8, /**< Array reference. */ /* 02 */ igbinary_type_ref16, /**< Array reference. */ /* 03 */ igbinary_type_ref32, /**< Array reference. */ /* 04 */ igbinary_type_bool_false, /**< Boolean true. */ /* 05 */ igbinary_type_bool_true, /**< Boolean false. */ /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */ /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */ /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */ /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */ /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */ /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */ /* 0c */ igbinary_type_double, /**< Double. */ /* 0d */ igbinary_type_string_empty, /**< Empty string. */ /* 0e */ igbinary_type_string_id8, /**< String id. */ /* 0f */ igbinary_type_string_id16, /**< String id. */ /* 10 */ igbinary_type_string_id32, /**< String id. */ /* 11 */ igbinary_type_string8, /**< String. */ /* 12 */ igbinary_type_string16, /**< String. */ /* 13 */ igbinary_type_string32, /**< String. */ /* 14 */ igbinary_type_array8, /**< Array. */ /* 15 */ igbinary_type_array16, /**< Array. */ /* 16 */ igbinary_type_array32, /**< Array. */ /* 17 */ igbinary_type_object8, /**< Object. */ /* 18 */ igbinary_type_object16, /**< Object. */ /* 19 */ igbinary_type_object32, /**< Object. */ /* 1a */ igbinary_type_object_id8, /**< Object string id. */ /* 1b */ igbinary_type_object_id16, /**< Object string id. */ /* 1c */ igbinary_type_object_id32, /**< Object string id. */ /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. */ /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */ /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */ /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */ /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */ /* 22 */ igbinary_type_objref8, /**< Object reference. */ /* 23 */ igbinary_type_objref16, /**< Object reference. */ /* 24 */ igbinary_type_objref32, /**< Object reference. */ /* 25 */ igbinary_type_ref, /**< Simple reference */ }; /** Serializer data. * @author Oleg Grenrus */ struct igbinary_serialize_data { uint8_t *buffer; /**< Buffer. */ size_t buffer_size; /**< Buffer size. */ size_t buffer_capacity; /**< Buffer capacity. */ bool scalar; /**< Serializing scalar. */ bool compact_strings; /**< Check for duplicate strings. */ struct hash_si strings; /**< Hash of already serialized strings. */ struct hash_si_ptr objects; /**< Hash of already serialized objects. */ int string_count; /**< Serialized string count, used for back referencing */ int error; /**< Error number. Not used. */ struct igbinary_memory_manager mm; /**< Memory management functions. */ }; /** String/len pair for the igbinary_unserializer_data. * @author Oleg Grenrus * @see igbinary_unserialize_data. */ struct igbinary_unserialize_string_pair { char *data; /**< Data. */ size_t len; /**< Data length. */ }; /** Unserializer data. * @author Oleg Grenrus */ struct igbinary_unserialize_data { uint8_t *buffer; /**< Buffer. */ size_t buffer_size; /**< Buffer size. */ size_t buffer_offset; /**< Current read offset. */ struct igbinary_unserialize_string_pair *strings; /**< Unserialized strings. */ size_t strings_count; /**< Unserialized string count. */ size_t strings_capacity; /**< Unserialized string array capacity. */ void **references; /**< Unserialized Arrays/Objects. */ size_t references_count; /**< Unserialized array/objects count. */ size_t references_capacity; /**< Unserialized array/object array capacity. */ int error; /**< Error number. Not used. */ smart_str string0_buf; /**< Temporary buffer for strings */ }; /* }}} */ /* {{{ Memory allocator wrapper prototypes */ static inline void *igbinary_mm_wrapper_malloc(size_t size, void *context); static inline void *igbinary_mm_wrapper_realloc(void *ptr, size_t size, void *context); static inline void igbinary_mm_wrapper_free(void *ptr, void *context); /* }}} */ /* {{{ Serializing functions prototypes */ inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar, struct igbinary_memory_manager *memory_manager TSRMLS_DC); inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd, int free_buffer TSRMLS_DC); inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd TSRMLS_DC); inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i TSRMLS_DC); inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i TSRMLS_DC); inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i TSRMLS_DC); inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i TSRMLS_DC); inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd TSRMLS_DC); inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b TSRMLS_DC); inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, long l TSRMLS_DC); inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d TSRMLS_DC); inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, char *s, size_t len TSRMLS_DC); inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len TSRMLS_DC); inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class TSRMLS_DC); inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object TSRMLS_DC); inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *ht, zend_class_entry *ce, bool incomplete_class TSRMLS_DC); inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, const char *name, size_t name_len TSRMLS_DC); inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC); static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC); /* }}} */ /* {{{ Unserializing functions prototypes */ inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd TSRMLS_DC); inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, long *ret TSRMLS_DC); inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret TSRMLS_DC); inline static int igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC); inline static int igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC); inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, int object TSRMLS_DC); inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC); inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, zend_class_entry *ce TSRMLS_DC); inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC); static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval **z TSRMLS_DC); /* }}} */ /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_serialize, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_unserialize, 0, 0, 1) ZEND_ARG_INFO(0, str) ZEND_END_ARG_INFO() /* }}} */ /* {{{ igbinary_functions[] */ /** Exported php functions. */ zend_function_entry igbinary_functions[] = { PHP_FE(igbinary_serialize, arginfo_igbinary_serialize) PHP_FE(igbinary_unserialize, arginfo_igbinary_unserialize) {NULL, NULL, NULL} }; /* }}} */ /* {{{ igbinary dependencies */ #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep igbinary_module_deps[] = { ZEND_MOD_REQUIRED("standard") #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) ZEND_MOD_REQUIRED("session") #endif #if defined(HAVE_APCU_SUPPORT) ZEND_MOD_OPTIONAL("apcu") #elif defined(HAVE_APC_SUPPORT) ZEND_MOD_OPTIONAL("apc") #endif {NULL, NULL, NULL} }; #endif /* }}} */ /* {{{ igbinary_module_entry */ zend_module_entry igbinary_module_entry = { #if ZEND_MODULE_API_NO >= 20050922 STANDARD_MODULE_HEADER_EX, NULL, igbinary_module_deps, #elif ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "igbinary", igbinary_functions, PHP_MINIT(igbinary), PHP_MSHUTDOWN(igbinary), NULL, NULL, PHP_MINFO(igbinary), #if ZEND_MODULE_API_NO >= 20010901 PHP_IGBINARY_VERSION, /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ ZEND_DECLARE_MODULE_GLOBALS(igbinary) /* {{{ ZEND_GET_MODULE */ #ifdef COMPILE_DL_IGBINARY ZEND_GET_MODULE(igbinary) #endif /* }}} */ /* {{{ INI entries */ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("igbinary.compact_strings", "1", PHP_INI_ALL, OnUpdateBool, compact_strings, zend_igbinary_globals, igbinary_globals) PHP_INI_END() /* }}} */ /* {{{ php_igbinary_init_globals */ static void php_igbinary_init_globals(zend_igbinary_globals *igbinary_globals) { igbinary_globals->compact_strings = 1; } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(igbinary) { (void) type; (void) module_number; ZEND_INIT_MODULE_GLOBALS(igbinary, php_igbinary_init_globals, NULL); #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) php_session_register_serializer("igbinary", PS_SERIALIZER_ENCODE_NAME(igbinary), PS_SERIALIZER_DECODE_NAME(igbinary)); #endif #if defined(HAVE_APC_SUPPORT) || defined(HAVE_APCU_SUPPORT) apc_register_serializer("igbinary", APC_SERIALIZER_NAME(igbinary), APC_UNSERIALIZER_NAME(igbinary), NULL TSRMLS_CC); #endif REGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(igbinary) { (void) type; (void) module_number; #ifdef ZTS ts_free_id(igbinary_globals_id); #endif /* * unregister serializer? */ UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(igbinary) { (void) zend_module; php_info_print_table_start(); php_info_print_table_row(2, "igbinary support", "enabled"); php_info_print_table_row(2, "igbinary version", PHP_IGBINARY_VERSION); #if defined(HAVE_APCU_SUPPORT) php_info_print_table_row(2, "igbinary APCU serializer ABI", APC_SERIALIZER_ABI); #elif defined(HAVE_APC_SUPPORT) php_info_print_table_row(2, "igbinary APC serializer ABI", APC_SERIALIZER_ABI); #else php_info_print_table_row(2, "igbinary APC serializer ABI", "no"); #endif #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) php_info_print_table_row(2, "igbinary session support", "yes"); #else php_info_print_table_row(2, "igbinary session support", "no"); #endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* {{{ Memory allocator wrappers */ static inline void *igbinary_mm_wrapper_malloc(size_t size, void *context) { return emalloc(size); } static inline void *igbinary_mm_wrapper_realloc(void *ptr, size_t size, void *context) { return erealloc(ptr, size); } static inline void igbinary_mm_wrapper_free(void *ptr, void *context) { efree(ptr); } /* }}} */ /* {{{ int igbinary_serialize(uint8_t**, size_t*, zval*) */ IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z TSRMLS_DC) { return igbinary_serialize_ex(ret, ret_len, z, NULL TSRMLS_CC); } /* }}} */ /* {{{ int igbinary_serialize_ex(uint8_t**, size_t*, zval*, igbinary_memory_manager*) */ IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager TSRMLS_DC) { struct igbinary_serialize_data igsd; uint8_t *tmpbuf; if (igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY, memory_manager TSRMLS_CC)) { zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); return 1; } if (igbinary_serialize_header(&igsd TSRMLS_CC) != 0) { zend_error(E_WARNING, "igbinary_serialize: cannot write header"); igbinary_serialize_data_deinit(&igsd, 1 TSRMLS_CC); return 1; } if (igbinary_serialize_zval(&igsd, z TSRMLS_CC) != 0) { igbinary_serialize_data_deinit(&igsd, 1 TSRMLS_CC); return 1; } /* Explicit nul termination */ if (igbinary_serialize8(&igsd, 0 TSRMLS_CC) != 0) { igbinary_serialize_data_deinit(&igsd, 1 TSRMLS_CC); return 1; } /* shrink buffer to the real length, ignore errors */ tmpbuf = (uint8_t *) igsd.mm.realloc(igsd.buffer, igsd.buffer_size, igsd.mm.context); if (tmpbuf != NULL) { igsd.buffer = tmpbuf; } /* Set return values */ *ret_len = igsd.buffer_size - 1; *ret = igsd.buffer; igbinary_serialize_data_deinit(&igsd, 0 TSRMLS_CC); return 0; } /* }}} */ /* {{{ int igbinary_unserialize(const uint8_t *, size_t, zval **) */ IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval **z TSRMLS_DC) { struct igbinary_unserialize_data igsd; igbinary_unserialize_data_init(&igsd TSRMLS_CC); igsd.buffer = (uint8_t *) buf; igsd.buffer_size = buf_len; if (igbinary_unserialize_header(&igsd TSRMLS_CC)) { igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); return 1; } if (igbinary_unserialize_zval(&igsd, z TSRMLS_CC)) { igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); return 1; } igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); return 0; } /* }}} */ /* {{{ proto string igbinary_unserialize(mixed value) */ PHP_FUNCTION(igbinary_unserialize) { char *string; int string_len; (void) return_value_ptr; (void) this_ptr; (void) return_value_used; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &string_len) == FAILURE) { RETURN_NULL(); } if (string_len <= 0) { RETURN_FALSE; } if (igbinary_unserialize((uint8_t *) string, string_len, &return_value TSRMLS_CC) != 0) { RETURN_NULL(); } } /* }}} */ /* {{{ proto mixed igbinary_serialize(string value) */ PHP_FUNCTION(igbinary_serialize) { zval *z; uint8_t *string; size_t string_len; (void) return_value_ptr; (void) this_ptr; (void) return_value_used; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) { RETURN_NULL(); } if (igbinary_serialize(&string, &string_len, z TSRMLS_CC) != 0) { RETURN_NULL(); } RETVAL_STRINGL((char *)string, string_len, 0); } /* }}} */ #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) /* {{{ Serializer encode function */ PS_SERIALIZER_ENCODE_FUNC(igbinary) { struct igbinary_serialize_data igsd; uint8_t *tmpbuf; zval *session_vars = PS(http_session_vars); HashTable *h = HASH_OF(session_vars); long num_elements = h ? zend_hash_num_elements(h) : 0; if (num_elements == 0) { // Return the empty string for the empty array. smart_str buf = {0}; smart_str_0(&buf); if (newlen) { *newlen = 0; } *newstr = buf.c; return SUCCESS; } if (igbinary_serialize_data_init(&igsd, false, NULL TSRMLS_CC)) { zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); return FAILURE; } if (igbinary_serialize_header(&igsd TSRMLS_CC) != 0) { zend_error(E_WARNING, "igbinary_serialize: cannot write header"); igbinary_serialize_data_deinit(&igsd, 1 TSRMLS_CC); return FAILURE; } if (igbinary_serialize_array(&igsd, session_vars, false, false TSRMLS_CC) != 0) { igbinary_serialize_data_deinit(&igsd, 1 TSRMLS_CC); return FAILURE; } if (igbinary_serialize8(&igsd, 0 TSRMLS_CC) != 0) { igbinary_serialize_data_deinit(&igsd, 1 TSRMLS_CC); return FAILURE; } /* shrink buffer to the real length, ignore errors */ tmpbuf = (uint8_t *)igsd.mm.realloc(igsd.buffer, igsd.buffer_size, igsd.mm.context); if (tmpbuf != NULL) { igsd.buffer = tmpbuf; } *newstr = (char *)igsd.buffer; if (newlen) { *newlen = igsd.buffer_size - 1; } igbinary_serialize_data_deinit(&igsd, 0 TSRMLS_CC); return SUCCESS; } /* }}} */ /* {{{ Serializer decode function */ PS_SERIALIZER_DECODE_FUNC(igbinary) { HashPosition tmp_hash_pos; HashTable *tmp_hash; char *key_str; ulong key_long; int tmp_int; uint key_len; zval *z; zval **d; struct igbinary_unserialize_data igsd; if (!val || vallen==0) return SUCCESS; if (igbinary_unserialize_data_init(&igsd TSRMLS_CC) != 0) { return FAILURE; } igsd.buffer = (uint8_t *)val; igsd.buffer_size = vallen; if (igbinary_unserialize_header(&igsd TSRMLS_CC)) { igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); return FAILURE; } ALLOC_INIT_ZVAL(z); if (igbinary_unserialize_zval(&igsd, &z TSRMLS_CC)) { igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); zval_dtor(z); FREE_ZVAL(z); return FAILURE; } igbinary_unserialize_data_deinit(&igsd TSRMLS_CC); tmp_hash = HASH_OF(z); zend_hash_internal_pointer_reset_ex(tmp_hash, &tmp_hash_pos); while (zend_hash_get_current_data_ex(tmp_hash, (void *) &d, &tmp_hash_pos) == SUCCESS) { tmp_int = zend_hash_get_current_key_ex(tmp_hash, &key_str, &key_len, &key_long, 0, &tmp_hash_pos); switch (tmp_int) { case HASH_KEY_IS_LONG: /* ??? */ break; case HASH_KEY_IS_STRING: php_set_session_var(key_str, key_len-1, *d, NULL TSRMLS_CC); php_add_session_var(key_str, key_len-1 TSRMLS_CC); break; } zend_hash_move_forward_ex(tmp_hash, &tmp_hash_pos); } zval_dtor(z); FREE_ZVAL(z); return SUCCESS; } /* }}} */ #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ #if defined(HAVE_APC_SUPPORT) || defined(HAVE_APCU_SUPPORT) /* {{{ apc_serialize function */ static int APC_SERIALIZER_NAME(igbinary) ( APC_SERIALIZER_ARGS ) { (void)config; if (igbinary_serialize(buf, buf_len, (zval *)value TSRMLS_CC) == 0) { /* flipped semantics */ return 1; } return 0; } /* }}} */ /* {{{ apc_unserialize function */ static int APC_UNSERIALIZER_NAME(igbinary) ( APC_UNSERIALIZER_ARGS ) { (void)config; if (igbinary_unserialize(buf, buf_len, value TSRMLS_CC) == 0) { /* flipped semantics */ return 1; } zval_dtor(*value); (*value)->type = IS_NULL; return 0; } /* }}} */ #endif /* {{{ igbinary_serialize_data_init */ /** Inits igbinary_serialize_data. */ inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar, struct igbinary_memory_manager *memory_manager TSRMLS_DC) { int r = 0; if (memory_manager == NULL) { igsd->mm.alloc = igbinary_mm_wrapper_malloc; igsd->mm.realloc = igbinary_mm_wrapper_realloc; igsd->mm.free = igbinary_mm_wrapper_free; igsd->mm.context = NULL; } else { igsd->mm = *memory_manager; } igsd->buffer = NULL; igsd->buffer_size = 0; igsd->buffer_capacity = 32; igsd->string_count = 0; igsd->error = 0; igsd->buffer = (uint8_t *) igsd->mm.alloc(igsd->buffer_capacity, igsd->mm.context); if (igsd->buffer == NULL) { return 1; } igsd->scalar = scalar; if (!igsd->scalar) { hash_si_init(&igsd->strings, 16); hash_si_ptr_init(&igsd->objects, 16); } igsd->compact_strings = (bool)IGBINARY_G(compact_strings); return r; } /* }}} */ /* {{{ igbinary_serialize_data_deinit */ /** Deinits igbinary_serialize_data. */ inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd, int free_buffer TSRMLS_DC) { if (free_buffer && igsd->buffer) { igsd->mm.free(igsd->buffer, igsd->mm.context); } if (!igsd->scalar) { hash_si_deinit(&igsd->strings); hash_si_ptr_deinit(&igsd->objects); } } /* }}} */ /* {{{ igbinary_serialize_header */ /** Serializes header. */ inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd TSRMLS_DC) { return igbinary_serialize32(igsd, IGBINARY_FORMAT_VERSION TSRMLS_CC); /* version */ } /* }}} */ /* {{{ igbinary_serialize_resize */ /** Expands igbinary_serialize_data. */ inline static int igbinary_serialize_resize(struct igbinary_serialize_data *igsd, size_t size TSRMLS_DC) { if (igsd->buffer_size + size < igsd->buffer_capacity) { return 0; } while (igsd->buffer_size + size >= igsd->buffer_capacity) { igsd->buffer_capacity *= 2; } igsd->buffer = (uint8_t *) igsd->mm.realloc(igsd->buffer, igsd->buffer_capacity, igsd->mm.context); if (igsd->buffer == NULL) return 1; return 0; } /* }}} */ /* {{{ igbinary_serialize8 */ /** Serialize 8bit value. */ inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i TSRMLS_DC) { if (UNEXPECTED(igbinary_serialize_resize(igsd, 1 TSRMLS_CC))) { return 1; } igsd->buffer[igsd->buffer_size++] = i; return 0; } /* }}} */ /* {{{ igbinary_serialize16 */ /** Serialize 16bit value. */ inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i TSRMLS_DC) { uint8_t* append_buffer; if (UNEXPECTED(igbinary_serialize_resize(igsd, 2 TSRMLS_CC))) { return 1; } append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t) (i >> 8 & 0xff); append_buffer[1] = (uint8_t) (i & 0xff); igsd->buffer_size += 2; return 0; } /* }}} */ /* {{{ igbinary_serialize32 */ /** Serialize 32bit value. */ inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i TSRMLS_DC) { uint8_t* append_buffer; if (UNEXPECTED(igbinary_serialize_resize(igsd, 4 TSRMLS_CC))) { return 1; } append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t) (i >> 24 & 0xff); append_buffer[1] = (uint8_t) (i >> 16 & 0xff); append_buffer[2] = (uint8_t) (i >> 8 & 0xff); append_buffer[3] = (uint8_t) (i & 0xff); igsd->buffer_size += 4; return 0; } /* }}} */ /* {{{ igbinary_serialize64 */ /** Serialize 64bit value. */ inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i TSRMLS_DC) { uint8_t* append_buffer; if (UNEXPECTED(igbinary_serialize_resize(igsd, 8 TSRMLS_CC))) { return 1; } append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t) (i >> 56 & 0xff); append_buffer[1] = (uint8_t) (i >> 48 & 0xff); append_buffer[2] = (uint8_t) (i >> 40 & 0xff); append_buffer[3] = (uint8_t) (i >> 32 & 0xff); append_buffer[4] = (uint8_t) (i >> 24 & 0xff); append_buffer[5] = (uint8_t) (i >> 16 & 0xff); append_buffer[6] = (uint8_t) (i >> 8 & 0xff); append_buffer[7] = (uint8_t) (i & 0xff); igsd->buffer_size += 8; return 0; } /* }}} */ /* {{{ igbinary_serialize_null */ /** Serializes null. */ inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd TSRMLS_DC) { return igbinary_serialize8(igsd, igbinary_type_null TSRMLS_CC); } /* }}} */ /* {{{ igbinary_serialize_bool */ /** Serializes bool. */ inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b TSRMLS_DC) { return igbinary_serialize8(igsd, (uint8_t) (b ? igbinary_type_bool_true : igbinary_type_bool_false) TSRMLS_CC); } /* }}} */ /* {{{ igbinary_serialize_long */ /** Serializes long. */ inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, long l TSRMLS_DC) { long k = l >= 0 ? l : -l; bool p = l >= 0 ? true : false; /* -LONG_MIN is 0 otherwise. */ if (l == LONG_MIN) { #if SIZEOF_LONG == 8 if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_long64n TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize64(igsd, (uint64_t) 0x8000000000000000 TSRMLS_CC) != 0) { return 1; } #elif SIZEOF_LONG == 4 if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_long32n TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) 0x80000000 TSRMLS_CC) != 0) { return 1; } #else #error "Strange sizeof(long)." #endif return 0; } if (k <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long8p : igbinary_type_long8n) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) k TSRMLS_CC) != 0) { return 1; } } else if (k <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long16p : igbinary_type_long16n) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) k TSRMLS_CC) != 0) { return 1; } #if SIZEOF_LONG == 8 } else if (k <= 0xffffffff) { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) k TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long64p : igbinary_type_long64n) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize64(igsd, (uint64_t) k TSRMLS_CC) != 0) { return 1; } } #elif SIZEOF_LONG == 4 } else { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) k TSRMLS_CC) != 0) { return 1; } } #else #error "Strange sizeof(long)." #endif return 0; } /* }}} */ /* {{{ igbinary_serialize_double */ /** Serializes double. */ inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d TSRMLS_DC) { union { double d; uint64_t u; } u; if (igbinary_serialize8(igsd, igbinary_type_double TSRMLS_CC) != 0) { return 1; } u.d = d; return igbinary_serialize64(igsd, u.u TSRMLS_CC); } /* }}} */ /* {{{ igbinary_serialize_string */ /** Serializes string. * Serializes each string once, after first time uses pointers. */ inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, char *s, size_t len TSRMLS_DC) { if (len == 0) { if (igbinary_serialize8(igsd, igbinary_type_string_empty TSRMLS_CC) != 0) { return 1; } return 0; } if (!igsd->scalar && igsd->compact_strings) { struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, s, len, igsd->string_count); if (result.code == hash_si_code_exists) { uint32_t value = result.value; if (value <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id8 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) value TSRMLS_CC) != 0) { return 1; } } else if (value <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id16 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) value TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id32 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) value TSRMLS_CC) != 0) { return 1; } } return 0; } else if (EXPECTED(result.code == hash_si_code_inserted)) { /* Fall through to igbinary_serialize_chararray */ } else { return 1; /* Failed to allocate copy of string */ } } igsd->string_count++; /* A new string is being serialized - update count so that duplicate class names can be used. */ if (igbinary_serialize_chararray(igsd, s, len TSRMLS_CC) != 0) { return 1; } return 0; } /* }}} */ /* {{{ igbinary_serialize_chararray */ /** Serializes string data. */ inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len TSRMLS_DC) { if (len <= 0xff) { if (igbinary_serialize8(igsd, igbinary_type_string8 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, len TSRMLS_CC) != 0) { return 1; } } else if (len <= 0xffff) { if (igbinary_serialize8(igsd, igbinary_type_string16 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, len TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, igbinary_type_string32 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, len TSRMLS_CC) != 0) { return 1; } } if (igbinary_serialize_resize(igsd, len TSRMLS_CC)) { return 1; } memcpy(igsd->buffer+igsd->buffer_size, s, len); igsd->buffer_size += len; return 0; } /* }}} */ /* {{{ igbinay_serialize_array */ /** Serializes array or objects inner properties. */ inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class TSRMLS_DC) { HashTable *h; HashPosition pos; size_t n; zval **d; char *key; uint key_len; int key_type; ulong key_index; /* hash */ h = object ? Z_OBJPROP_P(z) : HASH_OF(z); /* hash size */ n = h ? zend_hash_num_elements(h) : 0; /* incomplete class magic member */ if (n > 0 && incomplete_class) { --n; } if (!object && igbinary_serialize_array_ref(igsd, z, object TSRMLS_CC) == 0) { return 0; } if (n <= 0xff) { if (igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, n TSRMLS_CC) != 0) { return 1; } } else if (n <= 0xffff) { if (igbinary_serialize8(igsd, igbinary_type_array16 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, n TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, igbinary_type_array32 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, n TSRMLS_CC) != 0) { return 1; } } if (n == 0) { return 0; } /* serialize properties. */ zend_hash_internal_pointer_reset_ex(h, &pos); for (;; zend_hash_move_forward_ex(h, &pos)) { key_type = zend_hash_get_current_key_ex(h, &key, &key_len, &key_index, 0, &pos); /* last */ if (key_type == HASH_KEY_NON_EXISTANT) { break; } /* skip magic member in incomplete classes */ if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { continue; } switch (key_type) { case HASH_KEY_IS_LONG: if (igbinary_serialize_long(igsd, key_index TSRMLS_CC) != 0) { return 1; } break; case HASH_KEY_IS_STRING: if (igbinary_serialize_string(igsd, key, key_len-1 TSRMLS_CC) != 0) { return 1; } break; default: zend_error(E_ERROR, "igbinary_serialize_array: key is not string nor array"); /* not reached */ return 1; } /* we should still add element even if it's not OK, * since we already wrote the length of the array before */ if (zend_hash_get_current_data_ex(h, (void *) &d, &pos) != SUCCESS || d == NULL) { if (igbinary_serialize_null(igsd TSRMLS_CC)) { return 1; } } else { if (igbinary_serialize_zval(igsd, *d TSRMLS_CC)) { return 1; } } } return 0; } /* }}} */ /* {{{ igbinary_serialize_array_ref */ /** Serializes array reference. */ inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object TSRMLS_DC) { /* Integer representation of pointer to a zend_object or zval */ size_t t; zend_uintptr_t key; if (object && Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_class_entry) { key = (zend_uintptr_t) zend_objects_get_address(z TSRMLS_CC); } else { key = (zend_uintptr_t) z; } t = hash_si_ptr_find_or_insert(&igsd->objects, key, igsd->objects.used); if (t == SIZE_MAX) { return 1; } else { enum igbinary_type type; if (t <= 0xff) { type = object ? igbinary_type_objref8 : igbinary_type_ref8; if (igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) t TSRMLS_CC) != 0) { return 1; } } else if (t <= 0xffff) { type = object ? igbinary_type_objref16 : igbinary_type_ref16; if (igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) t TSRMLS_CC) != 0) { return 1; } } else { type = object ? igbinary_type_objref32 : igbinary_type_ref32; if (igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) t TSRMLS_CC) != 0) { return 1; } } return 0; } return 1; } /* }}} */ /* {{{ igbinary_serialize_array_sleep */ /** Serializes object's properties array with __sleep -function. */ inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, zend_class_entry *ce, bool incomplete_class TSRMLS_DC) { HashPosition pos; size_t n = zend_hash_num_elements(h); zval **d; zval **v; char *key; uint key_len; int key_type; ulong key_index; /* Decrease array size by one, because of magic member (with class name) */ if (n > 0 && incomplete_class) { --n; } /* Serialize array id. */ if (n <= 0xff) { if (igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, n TSRMLS_CC) != 0) { return 1; } } else if (n <= 0xffff) { if (igbinary_serialize8(igsd, igbinary_type_array16 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, n TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, igbinary_type_array32 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, n TSRMLS_CC) != 0) { return 1; } } if (n == 0) { return 0; } zend_hash_internal_pointer_reset_ex(h, &pos); for (;; zend_hash_move_forward_ex(h, &pos)) { key_type = zend_hash_get_current_key_ex(h, &key, &key_len, &key_index, 0, &pos); /* last */ if (key_type == HASH_KEY_NON_EXISTANT) { break; } /* skip magic member in incomplete classes */ if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { continue; } if (zend_hash_get_current_data_ex(h, (void *) &d, &pos) != SUCCESS || d == NULL || Z_TYPE_PP(d) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only " "containing the names of instance-variables to " "serialize"); /* we should still add element even if it's not OK, * since we already wrote the length of the array before * serialize null as key-value pair */ if (igbinary_serialize_null(igsd TSRMLS_CC) != 0) { return 1; } } else { if (zend_hash_find(Z_OBJPROP_P(z), Z_STRVAL_PP(d), Z_STRLEN_PP(d) + 1, (void *) &v) == SUCCESS) { if (igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize_zval(igsd, *v TSRMLS_CC) != 0) { return 1; } } else if (ce) { char *prot_name = NULL; char *priv_name = NULL; int prop_name_length; do { /* try private */ zend_mangle_property_name(&priv_name, &prop_name_length, ce->name, ce->name_length, Z_STRVAL_PP(d), Z_STRLEN_PP(d), ce->type & ZEND_INTERNAL_CLASS); if (zend_hash_find(Z_OBJPROP_P(z), priv_name, prop_name_length+1, (void *) &v) == SUCCESS) { if (igbinary_serialize_string(igsd, priv_name, prop_name_length TSRMLS_CC) != 0) { efree(priv_name); return 1; } efree(priv_name); if (igbinary_serialize_zval(igsd, *v TSRMLS_CC) != 0) { return 1; } break; } efree(priv_name); /* try protected */ zend_mangle_property_name(&prot_name, &prop_name_length, "*", 1, Z_STRVAL_PP(d), Z_STRLEN_PP(d), ce->type & ZEND_INTERNAL_CLASS); if (zend_hash_find(Z_OBJPROP_P(z), prot_name, prop_name_length+1, (void *) &v) == SUCCESS) { if (igbinary_serialize_string(igsd, prot_name, prop_name_length TSRMLS_CC) != 0) { efree(prot_name); return 1; } efree(prot_name); if (igbinary_serialize_zval(igsd, *v TSRMLS_CC) != 0) { return 1; } break; } efree(prot_name); /* no win */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", Z_STRVAL_PP(d)); if (igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize_null(igsd TSRMLS_CC) != 0) { return 1; } } while (0); } else { // if all else fails, just serialize the value in anyway. if (igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize_zval(igsd, *v TSRMLS_CC) != 0) { return 1; } } } } return 0; } /* }}} */ /* {{{ igbinary_serialize_object_name */ /** Serialize object name. */ inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, const char *class_name, size_t name_len TSRMLS_DC) { struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, class_name, name_len, igsd->string_count); if (result.code == hash_si_code_inserted) { igsd->string_count += 1; if (name_len <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object8 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) name_len TSRMLS_CC) != 0) { return 1; } } else if (name_len <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object16 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) name_len TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object32 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) name_len TSRMLS_CC) != 0) { return 1; } } if (igbinary_serialize_resize(igsd, name_len TSRMLS_CC)) { return 1; } memcpy(igsd->buffer+igsd->buffer_size, class_name, name_len); igsd->buffer_size += name_len; } else if (EXPECTED(result.code == hash_si_code_exists)) { /* already serialized string */ uint32_t value = result.value; if (value <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id8 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) value TSRMLS_CC) != 0) { return 1; } } else if (value <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id16 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) value TSRMLS_CC) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id32 TSRMLS_CC) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) value TSRMLS_CC) != 0) { return 1; } } } else { return 1; /* Failed to allocate copy of string */ } return 0; } /* }}} */ /* {{{ igbinary_serialize_object */ /** Serialize object. * @see ext/standard/var.c * */ inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC) { zend_class_entry *ce; zval f; zval *h = NULL; int r = 0; unsigned char *serialized_data = NULL; zend_uint serialized_len; PHP_CLASS_ATTRIBUTES; if (igbinary_serialize_array_ref(igsd, z, true TSRMLS_CC) == 0) { return r; } ce = Z_OBJCE_P(z); /* custom serializer */ if (ce && ce->serialize != NULL) { if (ce->serialize(z, &serialized_data, &serialized_len, (zend_serialize_data *)NULL TSRMLS_CC) == SUCCESS && !EG(exception)) { if (igbinary_serialize_object_name(igsd, ce->name, ce->name_length TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (serialized_len <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser8 TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (igbinary_serialize8(igsd, (uint8_t) serialized_len TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } } else if (serialized_len <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser16 TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (igbinary_serialize16(igsd, (uint16_t) serialized_len TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser32 TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (igbinary_serialize32(igsd, (uint32_t) serialized_len TSRMLS_CC) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } } if (igbinary_serialize_resize(igsd, serialized_len TSRMLS_CC)) { if (serialized_data) { efree(serialized_data); } return 1; } memcpy(igsd->buffer+igsd->buffer_size, serialized_data, serialized_len); igsd->buffer_size += serialized_len; } else if (EG(exception)) { /* exception, return failure */ r = 1; } else { /* Serialization callback failed, assume null output */ r = igbinary_serialize_null(igsd TSRMLS_CC); } if (serialized_data) { efree(serialized_data); } return r; } /* serialize class name */ PHP_SET_CLASS_ATTRIBUTES(z); if (igbinary_serialize_object_name(igsd, class_name, name_len TSRMLS_CC) != 0) { PHP_CLEANUP_CLASS_ATTRIBUTES(); return 1; } PHP_CLEANUP_CLASS_ATTRIBUTES(); if (ce && ce != PHP_IC_ENTRY && zend_hash_exists(&ce->function_table, "__sleep", sizeof("__sleep"))) { /* function name string */ INIT_PZVAL(&f); ZVAL_STRINGL(&f, "__sleep", sizeof("__sleep") - 1, 0); /* calling z->__sleep */ r = call_user_function_ex(CG(function_table), &z, &f, &h, 0, 0, 1, NULL TSRMLS_CC); if (r == SUCCESS && !EG(exception)) { r = 0; if (h) { if (Z_TYPE_P(h) == IS_ARRAY) { r = igbinary_serialize_array_sleep(igsd, z, HASH_OF(h), ce, incomplete_class TSRMLS_CC); } else { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only " "containing the names of instance-variables to " "serialize"); /* empty array */ r = igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC); if (r == 0) { r = igbinary_serialize8(igsd, 0 TSRMLS_CC); } } } } else { r = 1; } /* cleanup */ if (h) { zval_ptr_dtor(&h); } return r; } else { return igbinary_serialize_array(igsd, z, true, incomplete_class TSRMLS_CC); } } /* }}} */ /* {{{ igbinary_serialize_zval */ /** Serialize zval. */ static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC) { if (Z_ISREF_P(z)) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_ref TSRMLS_CC) != 0) { return 1; } /* Complex types serialize a reference, scalars do not... */ /* FIXME: Absolutely wrong level to check this. */ switch (Z_TYPE_P(z)) { case IS_RESOURCE: case IS_STRING: case IS_LONG: case IS_NULL: case IS_BOOL: case IS_DOUBLE: /* Serialize a reference if zval already added */ if (igbinary_serialize_array_ref(igsd, z, false TSRMLS_CC) == 0) { return 0; } /* otherwise fall through */ } } switch (Z_TYPE_P(z)) { case IS_RESOURCE: return igbinary_serialize_null(igsd TSRMLS_CC); case IS_OBJECT: return igbinary_serialize_object(igsd, z TSRMLS_CC); case IS_ARRAY: return igbinary_serialize_array(igsd, z, false, false TSRMLS_CC); case IS_STRING: return igbinary_serialize_string(igsd, Z_STRVAL_P(z), Z_STRLEN_P(z) TSRMLS_CC); case IS_LONG: return igbinary_serialize_long(igsd, Z_LVAL_P(z) TSRMLS_CC); case IS_NULL: return igbinary_serialize_null(igsd TSRMLS_CC); case IS_BOOL: return igbinary_serialize_bool(igsd, Z_LVAL_P(z) ? 1 : 0 TSRMLS_CC); case IS_DOUBLE: return igbinary_serialize_double(igsd, Z_DVAL_P(z) TSRMLS_CC); default: zend_error(E_ERROR, "igbinary_serialize_zval: zval has unknown type %d", (int)Z_TYPE_P(z)); /* not reached */ return 1; } return 0; } /* }}} */ /* {{{ igbinary_unserialize_data_init */ /** Inits igbinary_unserialize_data_init. */ inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd TSRMLS_DC) { smart_str empty_str = { 0 }; igsd->buffer = NULL; igsd->buffer_size = 0; igsd->buffer_offset = 0; igsd->strings = NULL; igsd->strings_count = 0; igsd->strings_capacity = 4; igsd->string0_buf = empty_str; igsd->error = 0; igsd->references = NULL; igsd->references_count = 0; igsd->references_capacity = 4; igsd->references = (void **) emalloc(sizeof(void *) * igsd->references_capacity); if (igsd->references == NULL) { return 1; } igsd->strings = (struct igbinary_unserialize_string_pair *) emalloc(sizeof(struct igbinary_unserialize_string_pair) * igsd->strings_capacity); if (igsd->strings == NULL) { efree(igsd->references); return 1; } return 0; } /* }}} */ /* {{{ igbinary_unserialize_data_deinit */ /** Deinits igbinary_unserialize_data_init. */ inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd TSRMLS_DC) { if (igsd->strings) { efree(igsd->strings); } if (igsd->references) { efree(igsd->references); } smart_str_free(&igsd->string0_buf); return; } /* }}} */ /* {{{ igbinary_unserialize_header_emit_warning */ /* Precondition: igsd->buffer_size >= 4 */ inline static void igbinary_unserialize_header_emit_warning(struct igbinary_unserialize_data *igsd, int version) { int i; char buf[9], *it; for (i = 0; i < 4; i++) { if (!isprint((int)igsd->buffer[i])) { if (version != 0 && (version & 0xff000000) == version) { // Check if high order byte was somehow set instead of low order byte zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u (wrong endianness?)", (unsigned int) version, 0x00000001, (unsigned int) IGBINARY_FORMAT_VERSION); return; } // Binary data, or a version number from a future release. zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u", (unsigned int) version, 0x00000001, (unsigned int) IGBINARY_FORMAT_VERSION); return; } } for (it = buf, i = 0; i < 4; i++) { char c = igsd->buffer[i]; if (c == '"' || c == '\\') { *it++ = '\\'; } *it++ = c; } *it = '\0'; zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: \"%s\"..., should begin with a binary version header of \"\\x00\\x00\\x00\\x01\" or \"\\x00\\x00\\x00\\x%02x\"", buf, (int)IGBINARY_FORMAT_VERSION); } /* }}} */ /* {{{ igbinary_unserialize_header */ /** Unserialize header. Check for version. */ inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd TSRMLS_DC) { uint32_t version; if (igsd->buffer_offset + 4 >= igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_header: expected at least 5 bytes of data, got %u byte(s)", (unsigned int) igsd->buffer_size); return 1; } version = igbinary_unserialize32(igsd TSRMLS_CC); /* Support older version 1 and the current format 2 */ if (version == IGBINARY_FORMAT_VERSION || version == 0x00000001) { return 0; } else { igbinary_unserialize_header_emit_warning(igsd, version); return 1; } } /* }}} */ /* {{{ igbinary_unserialize8 */ /** Unserialize 8bit value. */ inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd TSRMLS_DC) { uint8_t ret = 0; ret = igsd->buffer[igsd->buffer_offset++]; return ret; } /* }}} */ /* {{{ igbinary_unserialize16 */ /** Unserialize 16bit value. */ inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd TSRMLS_DC) { const uint8_t* buffer_ptr = &igsd->buffer[igsd->buffer_offset]; uint16_t ret = ((uint16_t) (buffer_ptr[0]) << 8) | ((uint16_t) (buffer_ptr[1])); igsd->buffer_offset += 2; return ret; } /* }}} */ /* {{{ igbinary_unserialize32 */ /** Unserialize 32bit value. */ inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd TSRMLS_DC) { const uint8_t* buffer_ptr = &igsd->buffer[igsd->buffer_offset]; uint32_t ret = ((uint32_t) (buffer_ptr[0]) << 24) | ((uint32_t) (buffer_ptr[1]) << 16) | ((uint32_t) (buffer_ptr[2]) << 8) | ((uint32_t) (buffer_ptr[3])); igsd->buffer_offset += 4; return ret; } /* }}} */ /* {{{ igbinary_unserialize64 */ /** Unserialize 64bit value. */ inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd TSRMLS_DC) { const uint8_t* buffer_ptr = &igsd->buffer[igsd->buffer_offset]; uint64_t ret = ((uint64_t) (buffer_ptr[0]) << 56) | ((uint64_t) (buffer_ptr[1]) << 48) | ((uint64_t) (buffer_ptr[2]) << 40) | ((uint64_t) (buffer_ptr[3]) << 32) | ((uint64_t) (buffer_ptr[4]) << 24) | ((uint64_t) (buffer_ptr[5]) << 16) | ((uint64_t) (buffer_ptr[6]) << 8) | ((uint64_t) (buffer_ptr[7]) << 0); igsd->buffer_offset += 8; return ret; } /* }}} */ /* {{{ igbinary_unserialize_long */ /** Unserializes long */ inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, long *ret TSRMLS_DC) { uint32_t tmp32; #if SIZEOF_LONG == 8 uint64_t tmp64; #endif if (t == igbinary_type_long8p || t == igbinary_type_long8n) { if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } *ret = (long) (t == igbinary_type_long8n ? -1 : 1) * igbinary_unserialize8(igsd TSRMLS_CC); } else if (t == igbinary_type_long16p || t == igbinary_type_long16n) { if (igsd->buffer_offset + 2 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } *ret = (long) (t == igbinary_type_long16n ? -1 : 1) * igbinary_unserialize16(igsd TSRMLS_CC); } else if (t == igbinary_type_long32p || t == igbinary_type_long32n) { if (igsd->buffer_offset + 4 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } /* check for boundaries */ tmp32 = igbinary_unserialize32(igsd TSRMLS_CC); #if SIZEOF_LONG == 4 if (tmp32 > 0x80000000 || (tmp32 == 0x80000000 && t == igbinary_type_long32p)) { zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform?"); tmp32 = 0; /* t == igbinary_type_long32p ? LONG_MAX : LONG_MIN; */ } #endif *ret = (long) (t == igbinary_type_long32n ? -1 : 1) * tmp32; } else if (t == igbinary_type_long64p || t == igbinary_type_long64n) { #if SIZEOF_LONG == 8 if (igsd->buffer_offset + 8 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } /* check for boundaries */ tmp64 = igbinary_unserialize64(igsd TSRMLS_CC); if (tmp64 > 0x8000000000000000 || (tmp64 == 0x8000000000000000 && t == igbinary_type_long64p)) { zend_error(E_WARNING, "igbinary_unserialize_long: too big 64bit long."); tmp64 = 0; /* t == igbinary_type_long64p ? LONG_MAX : LONG_MIN */; } *ret = (long) (t == igbinary_type_long64n ? -1 : 1) * tmp64; #elif SIZEOF_LONG == 4 /* can't put 64bit long into 32bit one, placeholder zero */ *ret = 0; igbinary_unserialize64(igsd TSRMLS_CC); zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform"); #else #error "Strange sizeof(long)." #endif } else { *ret = 0; zend_error(E_WARNING, "igbinary_unserialize_long: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } return 0; } /* }}} */ /* {{{ igbinary_unserialize_double */ /** Unserializes double. */ inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret TSRMLS_DC) { union { double d; uint64_t u; } u; (void) t; if (igsd->buffer_offset + 8 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_double: end-of-data"); return 1; } u.u = igbinary_unserialize64(igsd TSRMLS_CC); *ret = u.d; return 0; } /* }}} */ /* {{{ igbinary_unserialize_string */ /** Unserializes string. Unserializes both actual string or by string id. */ inline static int igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC) { size_t i; if (t == igbinary_type_string_id8 || t == igbinary_type_object_id8) { if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return 1; } i = igbinary_unserialize8(igsd TSRMLS_CC); } else if (t == igbinary_type_string_id16 || t == igbinary_type_object_id16) { if (igsd->buffer_offset + 2 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return 1; } i = igbinary_unserialize16(igsd TSRMLS_CC); } else if (t == igbinary_type_string_id32 || t == igbinary_type_object_id32) { if (igsd->buffer_offset + 4 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return 1; } i = igbinary_unserialize32(igsd TSRMLS_CC); } else { zend_error(E_WARNING, "igbinary_unserialize_string: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } if (i >= igsd->strings_count) { zend_error(E_WARNING, "igbinary_unserialize_string: string index is out-of-bounds"); return 1; } *s = igsd->strings[i].data; *len = igsd->strings[i].len; return 0; } /* }}} */ /* {{{ igbinary_unserialize_chararray */ /** Unserializes chararray of string. */ inline static int igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC) { size_t l; if (t == igbinary_type_string8 || t == igbinary_type_object8) { if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return 1; } l = igbinary_unserialize8(igsd TSRMLS_CC); if (igsd->buffer_offset + l > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return 1; } } else if (t == igbinary_type_string16 || t == igbinary_type_object16) { if (igsd->buffer_offset + 2 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return 1; } l = igbinary_unserialize16(igsd TSRMLS_CC); if (igsd->buffer_offset + l > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return 1; } } else if (t == igbinary_type_string32 || t == igbinary_type_object32) { if (igsd->buffer_offset + 4 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return 1; } l = igbinary_unserialize32(igsd TSRMLS_CC); if (igsd->buffer_offset + l > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return 1; } } else { zend_error(E_WARNING, "igbinary_unserialize_chararray: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } if (igsd->strings_count + 1 > igsd->strings_capacity) { while (igsd->strings_count + 1 > igsd->strings_capacity) { igsd->strings_capacity *= 2; } igsd->strings = (struct igbinary_unserialize_string_pair *) erealloc(igsd->strings, sizeof(struct igbinary_unserialize_string_pair) * igsd->strings_capacity); if (igsd->strings == NULL) { return 1; } } igsd->strings[igsd->strings_count].data = (char *) (igsd->buffer + igsd->buffer_offset); igsd->strings[igsd->strings_count].len = l; igsd->buffer_offset += l; if (igsd->strings[igsd->strings_count].data == NULL) { return 1; } *len = igsd->strings[igsd->strings_count].len; *s = igsd->strings[igsd->strings_count].data; igsd->strings_count += 1; return 0; } /* }}} */ /* {{{ igbinary_unserialize_array */ /** Unserializes array. */ inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, int object TSRMLS_DC) { size_t n; size_t i; zval *v = NULL; /* zval *old_v; */ char *key; size_t key_len = 0; long key_index = 0; enum igbinary_type key_type; HashTable *h; if (t == igbinary_type_array8) { if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize8(igsd TSRMLS_CC); } else if (t == igbinary_type_array16) { if (igsd->buffer_offset + 2 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize16(igsd TSRMLS_CC); } else if (t == igbinary_type_array32) { if (igsd->buffer_offset + 4 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize32(igsd TSRMLS_CC); } else { zend_error(E_WARNING, "igbinary_unserialize_array: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } // n cannot be larger than the number of minimum "objects" in the array if (n > igsd->buffer_size - igsd->buffer_offset) { zend_error(E_WARNING, "%s: data size %zu smaller that requested array length %zu.", "igbinary_unserialize_array", igsd->buffer_size - igsd->buffer_offset, n); return 1; } if (!object) { Z_TYPE_PP(z) = IS_ARRAY; ALLOC_HASHTABLE(Z_ARRVAL_PP(z)); zend_hash_init(Z_ARRVAL_PP(z), n + 1, NULL, ZVAL_PTR_DTOR, 0); /* references */ if (igsd->references_count + 1 >= igsd->references_capacity) { while (igsd->references_count + 1 >= igsd->references_capacity) { igsd->references_capacity *= 2; } igsd->references = (void **) erealloc(igsd->references, sizeof(void *) * igsd->references_capacity); if (igsd->references == NULL) return 1; } igsd->references[igsd->references_count++] = (void *) *z; } /* empty array */ if (n == 0) { return 0; } h = HASH_OF(*z); for (i = 0; i < n; i++) { key = NULL; if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); zval_dtor(*z); ZVAL_NULL(*z); return 1; } key_type = (enum igbinary_type) igbinary_unserialize8(igsd TSRMLS_CC); switch (key_type) { case igbinary_type_long8p: case igbinary_type_long8n: case igbinary_type_long16p: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: if (igbinary_unserialize_long(igsd, key_type, &key_index TSRMLS_CC)) { zval_dtor(*z); ZVAL_NULL(*z); return 1; } break; case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: if (igbinary_unserialize_string(igsd, key_type, &key, &key_len TSRMLS_CC)) { zval_dtor(*z); ZVAL_NULL(*z); return 1; } break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: if (igbinary_unserialize_chararray(igsd, key_type, &key, &key_len TSRMLS_CC)) { zval_dtor(*z); ZVAL_NULL(*z); return 1; } break; case igbinary_type_string_empty: key = ""; key_len = 0; break; case igbinary_type_null: continue; default: zend_error(E_WARNING, "igbinary_unserialize_array: unknown key type '%02x', position %zu", key_type, igsd->buffer_offset); zval_dtor(*z); ZVAL_NULL(*z); return 1; } ALLOC_INIT_ZVAL(v); if (igbinary_unserialize_zval(igsd, &v TSRMLS_CC)) { zval_dtor(*z); ZVAL_NULL(*z); zval_ptr_dtor(&v); return 1; } if (key) { /* Keys must include a terminating null. */ /* Ensure buffer starts at the beginning. */ igsd->string0_buf.len = 0; smart_str_appendl(&igsd->string0_buf, key, key_len); smart_str_0(&igsd->string0_buf); /* if (zend_symtable_find(h, key, key_len + 1, (void **)&old_v) == SUCCESS) { var_push_dtor(var_hash, old_v); } */ zend_symtable_update(h, igsd->string0_buf.c, igsd->string0_buf.len + 1, &v, sizeof(v), NULL); } else { /* if (zend_hash_index_find(h, key_index, (void **)&old_v) == SUCCESS) { var_push_dtor(var_hash, old_v); } */ /* PHP 5 can access object number properties only by string. */ if (UNEXPECTED(object)) { char id[32], *p; int len; p = smart_str_print_long(id + sizeof(id) - 1, (long) key_index); len = id + sizeof(id) - 1 - p; /*if (zend_symtable_find(h, key, key_len + 1, (void **)&old_v) == SUCCESS) { var_push_dtor(var_hash, old_v); } */ zend_hash_update(h, p, len + 1, &v, sizeof(v), NULL); /* See process_nested_data from ext/standard/var_unserializer.re */ } else { zend_hash_index_update(h, key_index, &v, sizeof(v), NULL); } } } return 0; } /* }}} */ /* {{{ igbinary_unserialize_object_ser */ /** Unserializes object's property array of objects implementing Serializable -interface. */ inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, zend_class_entry *ce TSRMLS_DC) { size_t n; int ret; php_unserialize_data_t var_hash; if (ce->unserialize == NULL) { zend_error(E_WARNING, "Class %s has no unserializer", ce->name); return 1; } if (t == igbinary_type_object_ser8) { if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize8(igsd TSRMLS_CC); } else if (t == igbinary_type_object_ser16) { if (igsd->buffer_offset + 2 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize16(igsd TSRMLS_CC); } else if (t == igbinary_type_object_ser32) { if (igsd->buffer_offset + 4 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize32(igsd TSRMLS_CC); } else { zend_error(E_WARNING, "igbinary_unserialize_object_ser: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } if (igsd->buffer_offset + n > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } PHP_VAR_UNSERIALIZE_INIT(var_hash); ret = ce->unserialize(z, ce, (const unsigned char*)(igsd->buffer + igsd->buffer_offset), n, (zend_unserialize_data *)&var_hash TSRMLS_CC); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (ret != SUCCESS || EG(exception)) { return 1; } igsd->buffer_offset += n; return 0; } /* }}} */ /* {{{ igbinary_unserialize_object */ /** Unserialize object. * @see ext/standard/var_unserializer.c */ inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC) { zend_class_entry *ce; zend_class_entry **pce; zval *h = NULL; zval f; char *name = NULL; size_t name_len = 0; int r; bool incomplete_class = false; bool is_from_serialized_data = false; zval *user_func; zval *retval_ptr; zval **args[1]; zval *arg_func_name; if (t == igbinary_type_object8 || t == igbinary_type_object16 || t == igbinary_type_object32) { if (igbinary_unserialize_chararray(igsd, t, &name, &name_len TSRMLS_CC)) { return 1; } } else if (t == igbinary_type_object_id8 || t == igbinary_type_object_id16 || t == igbinary_type_object_id32) { if (igbinary_unserialize_string(igsd, t, &name, &name_len TSRMLS_CC)) { return 1; } } else { zend_error(E_WARNING, "igbinary_unserialize_object: unknown object type '%02x', position %zu", t, igsd->buffer_offset); return 1; } do { /* Try to find class directly */ if (zend_lookup_class(name, name_len, &pce TSRMLS_CC) == SUCCESS) { ce = *pce; break; } /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } /* Call unserialize callback */ MAKE_STD_ZVAL(user_func); ZVAL_STRING(user_func, PG(unserialize_callback_func), 1); args[0] = &arg_func_name; MAKE_STD_ZVAL(arg_func_name); ZVAL_STRING(arg_func_name, name, 1); if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", name); incomplete_class = 1; ce = PHP_IC_ENTRY; zval_ptr_dtor(&user_func); zval_ptr_dtor(&arg_func_name); break; } if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } /* The callback function may have defined the class */ if (zend_lookup_class(name, name_len, &pce TSRMLS_CC) == SUCCESS) { ce = *pce; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function %s() hasn't defined the class it was called for", name); incomplete_class = true; ce = PHP_IC_ENTRY; } zval_ptr_dtor(&user_func); zval_ptr_dtor(&arg_func_name); } while (0); /* previous user function call may have raised an exception */ if (EG(exception)) { return 1; } object_init_ex(*z, ce); /* reference */ if (igsd->references_count + 1 >= igsd->references_capacity) { while (igsd->references_count + 1 >= igsd->references_capacity) { igsd->references_capacity *= 2; } igsd->references = (void **) erealloc(igsd->references, sizeof(void *) * igsd->references_capacity); if (igsd->references == NULL) return 1; } igsd->references[igsd->references_count++] = (void *) *z; /* store incomplete class name */ if (incomplete_class) { php_store_class_name(*z, name, name_len); } if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } t = (enum igbinary_type) igbinary_unserialize8(igsd TSRMLS_CC); switch (t) { case igbinary_type_array8: case igbinary_type_array16: case igbinary_type_array32: r = igbinary_unserialize_array(igsd, t, z, 1 TSRMLS_CC); break; case igbinary_type_object_ser8: case igbinary_type_object_ser16: case igbinary_type_object_ser32: is_from_serialized_data = true; r = igbinary_unserialize_object_ser(igsd, t, z, ce TSRMLS_CC); break; default: zend_error(E_WARNING, "igbinary_unserialize_object: unknown object inner type '%02x', position %zu", t, igsd->buffer_offset); return 1; } if (r) { return r; } if (!is_from_serialized_data && Z_OBJCE_PP(z) != PHP_IC_ENTRY && zend_hash_exists(&Z_OBJCE_PP(z)->function_table, "__wakeup", sizeof("__wakeup"))) { INIT_PZVAL(&f); ZVAL_STRINGL(&f, "__wakeup", sizeof("__wakeup") - 1, 0); call_user_function_ex(CG(function_table), z, &f, &h, 0, 0, 1, NULL TSRMLS_CC); if (h) { zval_ptr_dtor(&h); } if (EG(exception)) { r = 1; } } return r; } /* }}} */ /* {{{ igbinary_unserialize_ref */ /** Unserializes array or object by reference. */ inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC) { size_t n; if (t == igbinary_type_ref8 || t == igbinary_type_objref8) { if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize8(igsd TSRMLS_CC); } else if (t == igbinary_type_ref16 || t == igbinary_type_objref16) { if (igsd->buffer_offset + 2 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize16(igsd TSRMLS_CC); } else if (t == igbinary_type_ref32 || t == igbinary_type_objref32) { if (igsd->buffer_offset + 4 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize32(igsd TSRMLS_CC); } else { zend_error(E_WARNING, "igbinary_unserialize_ref: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } if (n >= igsd->references_count) { zend_error(E_WARNING, "igbinary_unserialize_ref: invalid reference"); return 1; } if (*z != NULL) { zval_ptr_dtor(z); } *z = igsd->references[n]; Z_ADDREF_PP(z); if (t == igbinary_type_objref8 || t == igbinary_type_objref16 || t == igbinary_type_objref32) { Z_SET_ISREF_TO_PP(z, false); } return 0; } /* }}} */ /* {{{ igbinary_unserialize_zval */ /** Unserialize zval. */ static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval **z TSRMLS_DC) { enum igbinary_type t; long tmp_long; double tmp_double; char *tmp_chararray; size_t tmp_size_t; if (igsd->buffer_offset + 1 > igsd->buffer_size) { zend_error(E_WARNING, "igbinary_unserialize_zval: end-of-data"); return 1; } t = (enum igbinary_type) igbinary_unserialize8(igsd TSRMLS_CC); switch (t) { case igbinary_type_ref: if (igbinary_unserialize_zval(igsd, z TSRMLS_CC)) { return 1; } /* Scalar types should be added to the references hash */ /* unless they're already added */ /* in references list: marked as ref */ if (!Z_ISREF_PP(z)) switch (Z_TYPE_PP(z)) { case IS_STRING: case IS_LONG: case IS_NULL: case IS_DOUBLE: case IS_BOOL: /* reference */ if (igsd->references_count + 1 >= igsd->references_capacity) { while (igsd->references_count + 1 >= igsd->references_capacity) { igsd->references_capacity *= 2; } igsd->references = (void **) erealloc(igsd->references, sizeof(void *) * igsd->references_capacity); if (igsd->references == NULL) return 1; } igsd->references[igsd->references_count++] = (void *) *z; } Z_SET_ISREF_TO_PP(z, true); break; case igbinary_type_objref8: case igbinary_type_objref16: case igbinary_type_objref32: case igbinary_type_ref8: case igbinary_type_ref16: case igbinary_type_ref32: if (igbinary_unserialize_ref(igsd, t, z TSRMLS_CC)) { return 1; } break; case igbinary_type_object8: case igbinary_type_object16: case igbinary_type_object32: case igbinary_type_object_id8: case igbinary_type_object_id16: case igbinary_type_object_id32: if (igbinary_unserialize_object(igsd, t, z TSRMLS_CC)) { return 1; } break; case igbinary_type_array8: case igbinary_type_array16: case igbinary_type_array32: if (igbinary_unserialize_array(igsd, t, z, 0 TSRMLS_CC)) { return 1; } break; case igbinary_type_string_empty: ZVAL_EMPTY_STRING(*z); break; case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: if (igbinary_unserialize_string(igsd, t, &tmp_chararray, &tmp_size_t TSRMLS_CC)) { return 1; } ZVAL_STRINGL(*z, tmp_chararray, tmp_size_t, 1); break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: if (igbinary_unserialize_chararray(igsd, t, &tmp_chararray, &tmp_size_t TSRMLS_CC)) { return 1; } ZVAL_STRINGL(*z, tmp_chararray, tmp_size_t, 1); break; case igbinary_type_long8p: case igbinary_type_long8n: case igbinary_type_long16p: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: if (igbinary_unserialize_long(igsd, t, &tmp_long TSRMLS_CC)) { return 1; } ZVAL_LONG(*z, tmp_long); break; case igbinary_type_null: ZVAL_NULL(*z); break; case igbinary_type_bool_false: ZVAL_BOOL(*z, 0); break; case igbinary_type_bool_true: ZVAL_BOOL(*z, 1); break; case igbinary_type_double: if (igbinary_unserialize_double(igsd, t, &tmp_double TSRMLS_CC)) { return 1; } ZVAL_DOUBLE(*z, tmp_double); break; default: zend_error(E_WARNING, "igbinary_unserialize_zval: unknown type '%02x', position %zu", t, igsd->buffer_offset); return 1; } return 0; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-2.0.5/src/php5/igbinary.h0000644000175000017500000000514413177372464016076 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef IGBINARY_H #define IGBINARY_H #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else # include #endif /* Forward declarations */ struct zval; /* Constants / macro constants */ /** Binary protocol version of igbinary. */ #define IGBINARY_FORMAT_VERSION 0x00000002 #define PHP_IGBINARY_VERSION "2.0.5" /* Macros */ #ifdef PHP_WIN32 # if defined(IGBINARY_EXPORTS) || (!defined(COMPILE_DL_IGBINARY)) # define IGBINARY_API __declspec(dllexport) # elif defined(COMPILE_DL_IGBINARY) # define IGBINARY_API __declspec(dllimport) # else # define IGBINARY_API /* nothing special */ # endif #elif defined(__GNUC__) && __GNUC__ >= 4 # define IGBINARY_API __attribute__ ((visibility("default"))) #else # define IGBINARY_API /* nothing special */ #endif /** Struct that contains pointers to memory allocation and deallocation functions. * @see igbinary_serialize_data */ struct igbinary_memory_manager { void *(*alloc)(size_t size, void *context); void *(*realloc)(void *ptr, size_t new_size, void *context); void (*free)(void *ptr, void *context); void *context; }; /** Serialize zval. * Return buffer is allocated by this function with emalloc. * @param[out] ret Return buffer * @param[out] ret_len Size of return buffer * @param[in] z Variable to be serialized * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z TSRMLS_DC); /** Serialize zval. * Return buffer is allocated by this function with emalloc. * @param[out] ret Return buffer * @param[out] ret_len Size of return buffer * @param[in] z Variable to be serialized * @param[in] memory_manager Pointer to the structure that contains memory allocation functions. * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager TSRMLS_DC); /** Unserialize to zval. * @param[in] buf Buffer with serialized data. * @param[in] buf_len Buffer length. * @param[out] z Unserialized zval * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval **z TSRMLS_DC); #endif /* IGBINARY_H */ igbinary-2.0.5/src/php5/igbinary_macros.h0000644000175000017500000000137713177372464017446 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef PHP_IGBINARY_MACROS_H #define PHP_IGBINARY_MACROS_H /* Require zend.h first, so that we are absolutely importing this header doesn't override EXPECTED or UNEXPECTED. */ #include "zend.h" /* PHP 5.2 doesn't define EXPECTED or UNEXPECTED in Zend/zend.h. */ #ifndef EXPECTED # define EXPECTED(expr) (expr) #endif #ifndef UNEXPECTED # define UNEXPECTED(expr) (expr) #endif #endif igbinary-2.0.5/src/php5/php_igbinary.h0000644000175000017500000000465213177372464016750 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef PHP_IGBINARY_H #define PHP_IGBINARY_H #include "php.h" /** Module entry of igbinary. */ extern zend_module_entry igbinary_module_entry; #define phpext_igbinary_ptr &igbinary_module_entry #ifdef PHP_WIN32 #define PHP_IGBINARY_API __declspec(dllexport) #else #define PHP_IGBINARY_API #endif ZEND_BEGIN_MODULE_GLOBALS(igbinary) zend_bool compact_strings; ZEND_END_MODULE_GLOBALS(igbinary) #ifdef ZTS #include "TSRM.h" #endif #include "ext/standard/php_smart_str.h" /** Module init function. */ PHP_MINIT_FUNCTION(igbinary); /** Module shutdown function. */ PHP_MSHUTDOWN_FUNCTION(igbinary); /** Request init function. */ PHP_RINIT_FUNCTION(igbinary); /** Request shutdown function. */ PHP_RSHUTDOWN_FUNCTION(igbinary); /** Module info function for phpinfo(). */ PHP_MINFO_FUNCTION(igbinary); /** string igbinary_serialize(mixed value). * Returns the binary serialized value. */ PHP_FUNCTION(igbinary_serialize); /** mixed igbinary_unserialize(string data). * Unserializes the given inputstring (value). */ PHP_FUNCTION(igbinary_unserialize); #ifdef ZTS #define IGBINARY_G(v) TSRMG(igbinary_globals_id, zend_igbinary_globals *, v) #else #define IGBINARY_G(v) (igbinary_globals.v) #endif /** Backport macros from php 5.3 */ #ifndef Z_ISREF_P #define Z_ISREF_P(pz) PZVAL_IS_REF(pz) #endif #ifndef Z_ISREF_PP #define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) #endif #ifndef Z_SET_ISREF_TO_P #define Z_SET_ISREF_TO_P(pz, isref) (Z_ISREF_P(pz) = (isref)) #endif #ifndef Z_SET_ISREF_TO_PP #define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) #endif #ifndef Z_ADDREF_P #define Z_ADDREF_P(pz) ZVAL_ADDREF(pz) #endif #ifndef Z_ADDREF_PP #define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz)) #endif /** Add macros missing from php 5.2 */ #include "igbinary_macros.h" #endif /* PHP_IGBINARY_H */ /* * Local variables: * tab-width: 2 * c-basic-offset: 0 * End: * vim600: noet sw=2 ts=2 fdm=marker * vim<600: noet sw=2 ts=2 */ igbinary-2.0.5/src/php5/ig_win32.h0000644000175000017500000000105613177372464015711 0ustar tysontyson#ifndef _IG_WIN32_H #define _IG_WIN32_H #if PHP_WIN32 # include "win32/php_stdint.h" # if defined(_MSC_VER) && _MSC_VER >= 1800 # include # else # ifndef inline # define inline __inline # endif # ifndef __cplusplus # if !0 typedef enum { false = 0, true = 1 } _Bool; # define bool _Bool # endif # else typedef bool _Bool; # define bool _Bool # endif # define false 0 # define true 1 # define __bool_true_false_are_defined 1 # endif /* __MSC_VER */ #endif /* PHP_WIN32 */ #endif /* _IG_WIN32_H */ igbinary-2.0.5/src/php5/apc_serializer.h0000644000175000017500000000647513177372464017276 0ustar tysontyson/* +----------------------------------------------------------------------+ | APC | +----------------------------------------------------------------------+ | Copyright (c) 2006-2011 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. | +----------------------------------------------------------------------+ | Authors: Gopal Vijayaraghavan | +----------------------------------------------------------------------+ */ /* $Id: $ */ #ifndef APC_SERIALIZER_H #define APC_SERIALIZER_H /* this is a shipped .h file, do not include any other header in this file */ #define APC_SERIALIZER_NAME(module) module##_apc_serializer #define APC_UNSERIALIZER_NAME(module) module##_apc_unserializer #define APC_SERIALIZER_ARGS unsigned char **buf, size_t *buf_len, const zval *value, void *config TSRMLS_DC #define APC_UNSERIALIZER_ARGS zval **value, unsigned char *buf, size_t buf_len, void *config TSRMLS_DC typedef int (*apc_serialize_t)(APC_SERIALIZER_ARGS); typedef int (*apc_unserialize_t)(APC_UNSERIALIZER_ARGS); typedef int (*apc_register_serializer_t)(const char* name, apc_serialize_t serialize, apc_unserialize_t unserialize, void *config TSRMLS_DC); /* * ABI version for constant hooks. Increment this any time you make any changes * to any function in this file. */ #define APC_SERIALIZER_ABI "0" #define APC_SERIALIZER_CONSTANT "\000apc_register_serializer-" APC_SERIALIZER_ABI #if !defined(APC_UNUSED) # if defined(__GNUC__) # define APC_UNUSED __attribute__((unused)) # else # define APC_UNUSED # endif #endif static APC_UNUSED int apc_register_serializer(const char* name, apc_serialize_t serialize, apc_unserialize_t unserialize, void *config TSRMLS_DC) { zval *apc_magic_constant = NULL; (void)config; ALLOC_INIT_ZVAL(apc_magic_constant); if (zend_get_constant(APC_SERIALIZER_CONSTANT, sizeof(APC_SERIALIZER_CONSTANT)-1, apc_magic_constant TSRMLS_CC)) { if(apc_magic_constant) { apc_register_serializer_t register_func = (apc_register_serializer_t)(Z_LVAL_P(apc_magic_constant)); if(register_func) { zval_dtor(apc_magic_constant); return register_func(name, serialize, unserialize, NULL TSRMLS_CC); } } } zval_dtor(apc_magic_constant); return 0; } #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker * vim<600: expandtab sw=4 ts=4 sts=4 */ igbinary-2.0.5/src/php7/hash.h0000644000175000017500000000743513177372464015224 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef HASH_H #define HASH_H #include #ifdef PHP_WIN32 # include "ig_win32.h" #else # include /* defines uint32_t etc */ #endif #include #include "zend_types.h" /** Key/value pair of hash_si. * @author Oleg Grenrus * @see hash_si */ struct hash_si_pair { zend_string* key_zstr; /* Contains key, key length, and key hash */ uint32_t key_hash; /**< Copy of ZSTR_H(key_zstr). Avoid dereferencing key_zstr if hashes are different. */ uint32_t value; /**< Value. */ }; enum hash_si_code { hash_si_code_inserted, hash_si_code_exists, hash_si_code_exception }; struct hash_si_result { enum hash_si_code code; uint32_t value; }; /** Hash-array. * Like c++ unordered_map. * Current implementation uses linear probing (with interval 1, 3, 5, or 7). * @author Oleg Grenrus */ struct hash_si { size_t mask; /**< Bitmask for the array. size == mask+1 */ size_t used; /**< Used size of array. */ struct hash_si_pair *data; /**< Pointer to array or pairs of data. */ }; /** Inits hash_si structure. * @param h pointer to hash_si struct. * @param size initial size of the hash array. * @return 0 on success, 1 else. */ int hash_si_init (struct hash_si *h, size_t size); /** Frees hash_si structure. * Doesn't call free(h). * @param h pointer to hash_si struct. */ void hash_si_deinit (struct hash_si *h); /** Inserts value into hash_si. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param value Value. * @return 0 on success, 1 or 2 else. */ /* int hash_si_insert (struct hash_si *h, const char *key, size_t key_len, uint32_t value); */ /** Finds value from hash_si. * Value returned thru value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ /* int hash_si_find (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); */ /** Finds value from hash_si. * Value returned thru value param. * @param h Pointer to hash_si struct. * @param key zend_string with key * @param[out] value Found value. * @return 0 if found, 1 if not. */ struct hash_si_result hash_si_find_or_insert(struct hash_si *h, zend_string *key, uint32_t value); /** Remove value from hash_si. * Removed value is available thru value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Removed value. * @return 0 ivalue removed, 1 if not existed. */ /* int hash_si_remove (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); */ /** Travarses hash_si. * Calls traverse_function on every item. Traverse function should not modify hash * @param h Pointer to hash_si struct. * @param traverse_function Function to call on every item of hash_si. */ /* void hash_si_traverse (struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)); */ /** Returns size of hash_si. * @param h Pointer to hash_si struct. * @return Size of hash_si. */ size_t hash_si_size (struct hash_si *h); /** Returns capacity of hash_si. * @param h Pointer to hash_si struct. * @return Capacity of hash_si. */ size_t hash_si_capacity (struct hash_si *h); #endif /* HASH_H */ igbinary-2.0.5/src/php7/hash_ptr.h0000644000175000017500000000653113177372464016105 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | | This defines hash_si_ptr. | | It is like hash_si, but the key is always a non-zero zend_uintptr_t | +----------------------------------------------------------------------+ */ #ifndef HASH_PTR_H #define HASH_PTR_H #include #ifdef PHP_WIN32 # include "ig_win32.h" #else # include /* defines uint32_t etc */ #endif #include #include "zend_types.h" // NULL converted to an integer, on sane platforms. #define HASH_PTR_KEY_INVALID 0 /** Key/value pair of hash_si_ptr. * @author Oleg Grenrus * @see hash_si_ptr */ struct hash_si_ptr_pair { zend_uintptr_t key; /**< The key: The address of a pointer, casted to an int (won't be dereferenced). */ uint32_t value; /**< Value. */ }; /** Hash-array. * Like c++ std::unordered_map, but does not allow HASH_PTR_KEY_INVALID as a key. * Current implementation uses linear probing. * @author Oleg Grenrus */ struct hash_si_ptr { size_t size; /**< Allocated size of array. */ size_t used; /**< Used size of array. */ struct hash_si_ptr_pair *data; /**< Pointer to array or pairs of data. */ }; /** Inits hash_si_ptr structure. * @param h pointer to hash_si_ptr struct. * @param size initial size of the hash array. * @return 0 on success, 1 else. */ int hash_si_ptr_init(struct hash_si_ptr *h, size_t size); /** Frees hash_si_ptr structure. * Doesn't call free(h). * @param h pointer to hash_si_ptr struct. */ void hash_si_ptr_deinit(struct hash_si_ptr *h); /** Inserts value into hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @param key_len Key length. * @param value Value. * @return 0 on success, 1 or 2 else. */ /* int hash_si_ptr_insert (struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value); */ /** Finds value from hash_si_ptr. * Value returned thru value param. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ /* int hash_si_ptr_find (struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t * value); */ /** Returns size of hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @return Size of hash_si_ptr. */ size_t hash_si_ptr_size (struct hash_si_ptr *h); /** * If the key does not exist, add a mapping from key to value and returns SIZE_MAX * If the key does exist, return the corresponding value * @param h Pointer to hash_si_ptr struct. * @param key key to look up or add * @param value value to insert if it doesn't already exist * * @return SIZE_MAX or old, unmodified key */ size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value); /** Returns capacity of hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @return Capacity of hash_si_ptr. */ size_t hash_si_ptr_capacity (struct hash_si_ptr *h); #endif /* HASH_PTR_H */ igbinary-2.0.5/src/php7/hash_si.c0000644000175000017500000001503213177372464015702 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef PHP_WIN32 # include "ig_win32.h" #endif #include #include #include #include #include "hash.h" #include "zend.h" /* {{{ nextpow2 */ /** Next power of 2. * @param n Integer. * @return next to n power of 2 . */ inline static uint32_t nextpow2(uint32_t n) { uint32_t m = 1; while (m < n) { m = m << 1; } return m; } /* }}} */ /* {{{ hash_si_init */ int hash_si_init(struct hash_si *h, size_t size) { size = nextpow2(size); h->mask = size - 1; h->used = 0; h->data = (struct hash_si_pair *) emalloc(sizeof(struct hash_si_pair) * size); if (h->data == NULL) { return 1; } memset(h->data, 0, sizeof(struct hash_si_pair) * size); return 0; } /* }}} */ /* {{{ hash_si_deinit */ void hash_si_deinit(struct hash_si *h) { size_t i; for (i = 0; i <= h->mask; i++) { if (h->data[i].key_zstr != NULL) { zend_string_release(h->data[i].key_zstr); } } efree(h->data); h->mask = 0; h->used = 0; } /* }}} */ /* {{{ _hash_si_find */ /** Returns index of key, or where it should be. * @param h Pointer to hash_si struct. * @param key_zstr Pointer to key. (Will initialize ZSTR_H(key_zstr) if not already initialized) * @return index. */ inline static struct hash_si_pair *_hash_si_find(const struct hash_si *h, const zend_string *key_zstr, const uint32_t key_hash) { size_t mask; struct hash_si_pair *it; const struct hash_si_pair *last_element; uint32_t increment; uint32_t elem_key_hash; assert(h != NULL); mask = h->mask; it = &(h->data[key_hash & mask]); elem_key_hash = it->key_hash; if (elem_key_hash == 0) { /* This slot is empty - PHP guarantees hashes are non-zero */ return it; } if (key_hash == elem_key_hash) { const zend_string *elem_key_zstr = it->key_zstr; if (key_zstr == elem_key_zstr || ( ZSTR_LEN(key_zstr) == ZSTR_LEN(elem_key_zstr) && EXPECTED(!memcmp(ZSTR_VAL(key_zstr), ZSTR_VAL(elem_key_zstr), ZSTR_LEN(elem_key_zstr))))) { /* We found an identical key. */ return it; } } increment = ((key_hash >> 16) & 6) | 1; /* An odd number < size to increment by (1, 3, 5, or 7). Avoid clumping. */ last_element = h->data + mask; /* Continue scanning, we'll reach an empty element eventually (odd number increment over a hash table of size (2^n) will traverse the entire map). */ while (1) { it += increment; if (it > last_element) { it -= (mask + 1); } elem_key_hash = it->key_hash; if (elem_key_hash == 0) { /* This slot is empty - PHP guarantees hashes are non-zero */ return it; } if (key_hash == elem_key_hash) { const zend_string *elem_key_zstr = it->key_zstr; if (EXPECTED(key_zstr == elem_key_zstr || ( ZSTR_LEN(key_zstr) == ZSTR_LEN(elem_key_zstr) && EXPECTED(!memcmp(ZSTR_VAL(key_zstr), ZSTR_VAL(elem_key_zstr), ZSTR_LEN(elem_key_zstr)))))) { /* We found an identical key. */ return it; } } /* linear probing by increment if we found a different key */ } } /* }}} */ /* {{{ hash_si_rehash */ /** Rehash/resize hash_si. * @param h Pointer to hash_si struct. */ inline static void hash_si_rehash(struct hash_si *h) { size_t i; size_t size; struct hash_si newh; struct hash_si_pair *data; struct hash_si_pair *new_data; assert(h != NULL); size = h->mask + 1; hash_si_init(&newh, size * 2); data = h->data; new_data = newh.data; for (i = 0; i < size; i++) { const struct hash_si_pair *old_pair = &data[i]; const zend_string *key_zstr = old_pair->key_zstr; if (key_zstr != NULL) { /* We already computed the hash, avoid recomputing it. */ struct hash_si_pair *new_data_entry = _hash_si_find((const struct hash_si*) &newh, key_zstr, old_pair->key_hash); *new_data_entry = *old_pair; } } efree(h->data); h->data = new_data; h->mask = size * 2 - 1; } /* }}} */ /* {{{ hash_si_insert */ /* int hash_si_insert(struct hash_si *h, const char *key, size_t key_len, uint32_t value) { uint32_t hv; if (h->size / 4 * 3 < h->used + 1) { hash_si_rehash(h); } hv = _hash_si_find(h, key, key_len); if (h->data[hv].key == NULL) { h->data[hv].key = (char *) emalloc(key_len + 1); if (h->data[hv].key == NULL) { return 1; } memcpy(h->data[hv].key, key, key_len); h->data[hv].key[key_len] = '\0'; h->data[hv].key_len = key_len; h->used++; } else { return 2; } h->data[hv].value = value; return 0; } */ /* }}} */ /* {{{ hash_si_find */ /* int hash_si_find(struct hash_si *h, const char *key, size_t key_len, uint32_t *value) { uint32_t hv; assert(h != NULL); hv = _hash_si_find(h, key, key_len); if (h->data[hv].key == NULL) { return 1; } else { *value = h->data[hv].value; return 0; } } */ /* }}} */ /* {{{ hash_si_find_or_insert */ struct hash_si_result hash_si_find_or_insert(struct hash_si *h, zend_string *key_zstr, uint32_t value) { struct hash_si_result result; struct hash_si_pair *pair; uint32_t key_hash = ZSTR_HASH(key_zstr); pair = _hash_si_find(h, key_zstr, key_hash); if (pair->key_zstr == NULL) { zend_string_addref(key_zstr); /* Having already computed the hash in the zend_string, insert that into the hash table */ pair->key_zstr = key_zstr; pair->key_hash = key_hash; pair->value = value; h->used++; if (h->mask * 3 / 4 < h->used) { hash_si_rehash(h); } result.code = hash_si_code_inserted; return result; } else { result.code = hash_si_code_exists; result.value = pair->value; return result; } } /* }}} */ /* {{{ hash_si_traverse */ /* void hash_si_traverse(struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)) { size_t i; assert(h != NULL && traverse_function != NULL); for (i = 0; i < h->size; i++) { if (h->data[i].key != NULL && traverse_function(h->data[i].key, h->data[i].key_len, h->data[i].value) != 1) { return; } } } */ /* }}} */ /* {{{ hash_si_size */ size_t hash_si_size(struct hash_si *h) { assert(h != NULL); return h->used; } /* }}} */ /* {{{ hash_si_capacity */ size_t hash_si_capacity(struct hash_si *h) { assert(h != NULL); return h->mask + 1; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-2.0.5/src/php7/hash_si_ptr.c0000644000175000017500000001201113177372464016561 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | | This is a specialized hash map mapping uintprt_t to int32_t | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | Modified by Tyson Andre for fixed size, removing unused functions | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else # include #endif #include #include #include #include #include "hash_ptr.h" #include "zend.h" /* Function similar to zend_inline_hash_func. This is not identical. */ inline static uint32_t inline_hash_of_address(zend_uintptr_t ptr) { register uint32_t hash = Z_UL(5381); /* Note: Hash the least significant bytes first - Those need to influence the final result as much as possible. */ hash = ((hash << 5) + hash) + (ptr & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 8) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 16) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 24) & 0xff); #if UINTPTR_MAX > UINT32_MAX hash = ((hash << 5) + hash) + ((ptr >> 32) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 40) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 48) & 0xff); hash = ((hash << 5) + hash) + ((ptr >> 56) & 0xff); #endif return hash; } /* {{{ nextpow2 */ /** Next power of 2. * @param n Integer. * @return next to n power of 2 . */ inline static uint32_t nextpow2(uint32_t n) { uint32_t m = 1; while (m < n) { m = m << 1; } return m; } /* }}} */ /* {{{ hash_si_ptr_init */ int hash_si_ptr_init(struct hash_si_ptr *h, size_t size) { size = nextpow2(size); h->size = size; h->used = 0; h->data = (struct hash_si_ptr_pair*) malloc(sizeof(struct hash_si_ptr_pair) * size); if (h->data == NULL) { return 1; } memset(h->data, 0, sizeof(struct hash_si_ptr_pair) * size); /* Set everything to 0. sets keys to HASH_PTR_KEY_INVALID. */ return 0; } /* }}} */ /* {{{ hash_si_ptr_deinit */ void hash_si_ptr_deinit(struct hash_si_ptr *h) { free(h->data); h->data = NULL; h->size = 0; h->used = 0; } /* }}} */ /* {{{ _hash_si_ptr_find */ /** Returns index of key, or where it should be. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @return index. */ inline static size_t _hash_si_ptr_find(struct hash_si_ptr *h, const zend_uintptr_t key) { uint32_t hv; size_t size; assert(h != NULL); size = h->size; hv = inline_hash_of_address(key) & (h->size-1); while (size > 0 && h->data[hv].key != HASH_PTR_KEY_INVALID && h->data[hv].key != key) { /* linear prob */ hv = (hv + 1) & (h->size-1); size--; } return hv; } /* }}} */ /* }}} */ /* {{{ hash_si_ptr_rehash */ /** Rehash/resize hash_si_ptr. * @param h Pointer to hash_si_ptr struct. */ inline static void hash_si_ptr_rehash(struct hash_si_ptr *h) { uint32_t hv; size_t i; struct hash_si_ptr newh; assert(h != NULL); hash_si_ptr_init(&newh, h->size * 2); for (i = 0; i < h->size; i++) { if (h->data[i].key != HASH_PTR_KEY_INVALID) { hv = _hash_si_ptr_find(&newh, h->data[i].key); newh.data[hv].key = h->data[i].key; newh.data[hv].value = h->data[i].value; } } free(h->data); h->data = newh.data; h->size *= 2; } /* }}} */ /* {{{ hash_si_ptr_insert */ /* int hash_si_ptr_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value) { uint32_t hv; if (h->size / 4 * 3 < h->used + 1) { hash_si_ptr_rehash(h); } hv = _hash_si_ptr_find(h, key); if (h->data[hv].key == HASH_PTR_KEY_INVALID) { h->data[hv].key = key; h->used++; } else { return 2; } h->data[hv].value = value; return 0; } */ /* }}} */ /* {{{ hash_si_ptr_find */ /* int hash_si_ptr_find(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t *value) { uint32_t hv; assert(h != NULL); hv = _hash_si_ptr_find(h, key); if (h->data[hv].key == HASH_PTR_KEY_INVALID) { return 1; } else { *value = h->data[hv].value; return 0; } } */ /* }}} */ /* {{{ hash_si_ptr_find_or_insert */ size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value) { uint32_t hv; hv = _hash_si_ptr_find(h, key); if (h->data[hv].key == HASH_PTR_KEY_INVALID) { h->data[hv].key = key; h->data[hv].value = value; h->used++; /* The size increased, so check if we need to expand the map */ if (h->size / 4 * 3 < h->used) { hash_si_ptr_rehash(h); } return SIZE_MAX; } else { return h->data[hv].value; } } /* }}} */ /* {{{ hash_si_ptr_size */ size_t hash_si_ptr_size(struct hash_si_ptr *h) { assert(h != NULL); return h->used; } /* }}} */ /* {{{ hash_si_ptr_capacity */ size_t hash_si_ptr_capacity(struct hash_si_ptr *h) { assert(h != NULL); return h->size; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-2.0.5/src/php7/igbinary.c0000644000175000017500000024750313177372464016102 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PHP_WIN32 # include "ig_win32.h" #endif #include "php.h" #include "php_ini.h" #include "zend_alloc.h" #include "ext/standard/info.h" #include "ext/standard/php_var.h" #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) # include "ext/session/php_session.h" #endif #include "ext/standard/php_incomplete_class.h" /* Note: there are no checks for APC (project from which APCU was forked) */ #if defined(HAVE_APCU_SUPPORT) # include "ext/apcu/apc_serializer.h" #endif /* HAVE_APCU_SUPPORT */ #include "php_igbinary.h" #include "igbinary.h" #include #include #ifndef PHP_WIN32 # include # include # include #endif #include #include "hash.h" #include "hash_ptr.h" #include "zend_alloc.h" #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) /** Session serializer function prototypes. */ PS_SERIALIZER_FUNCS(igbinary); #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ #if defined(HAVE_APCU_SUPPORT) /** Apc serializer function prototypes */ static int APC_SERIALIZER_NAME(igbinary) (APC_SERIALIZER_ARGS); static int APC_UNSERIALIZER_NAME(igbinary) (APC_UNSERIALIZER_ARGS); #endif /* {{{ Types */ enum igbinary_type { /* 00 */ igbinary_type_null, /**< Null. */ /* 01 */ igbinary_type_ref8, /**< Array reference. */ /* 02 */ igbinary_type_ref16, /**< Array reference. */ /* 03 */ igbinary_type_ref32, /**< Array reference. */ /* 04 */ igbinary_type_bool_false, /**< Boolean true. */ /* 05 */ igbinary_type_bool_true, /**< Boolean false. */ /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */ /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */ /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */ /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */ /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */ /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */ /* 0c */ igbinary_type_double, /**< Double. */ /* 0d */ igbinary_type_string_empty, /**< Empty string. */ /* 0e */ igbinary_type_string_id8, /**< String id. */ /* 0f */ igbinary_type_string_id16, /**< String id. */ /* 10 */ igbinary_type_string_id32, /**< String id. */ /* 11 */ igbinary_type_string8, /**< String. */ /* 12 */ igbinary_type_string16, /**< String. */ /* 13 */ igbinary_type_string32, /**< String. */ /* 14 */ igbinary_type_array8, /**< Array. */ /* 15 */ igbinary_type_array16, /**< Array. */ /* 16 */ igbinary_type_array32, /**< Array. */ /* 17 */ igbinary_type_object8, /**< Object. */ /* 18 */ igbinary_type_object16, /**< Object. */ /* 19 */ igbinary_type_object32, /**< Object. */ /* 1a */ igbinary_type_object_id8, /**< Object string id. */ /* 1b */ igbinary_type_object_id16, /**< Object string id. */ /* 1c */ igbinary_type_object_id32, /**< Object string id. */ /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. */ /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */ /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */ /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */ /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */ /* 22 */ igbinary_type_objref8, /**< Object reference. */ /* 23 */ igbinary_type_objref16, /**< Object reference. */ /* 24 */ igbinary_type_objref32, /**< Object reference. */ /* 25 */ igbinary_type_ref, /**< Simple reference */ }; /** Serializer data. * @author Oleg Grenrus */ struct igbinary_serialize_data { uint8_t *buffer; /**< Buffer. */ size_t buffer_size; /**< Buffer size. */ size_t buffer_capacity; /**< Buffer capacity. */ bool scalar; /**< Serializing scalar. */ bool compact_strings; /**< Check for duplicate strings. */ struct hash_si strings; /**< Hash of already serialized strings. */ struct hash_si_ptr references; /**< Hash of already serialized potential references. (non-NULL uintptr_t => int32_t) */ int references_id; /**< Number of things that the unserializer might think are references. >= length of references */ int string_count; /**< Serialized string count, used for back referencing */ int error; /**< Error number. Not used. */ struct igbinary_memory_manager mm; /**< Memory management functions. */ }; /* Object { reference {scalar, object, array, null} (convert to reference, share reference in zval_ref) object {} (convert to zend_object, share zend_object* in reference) array {} (convert to zend_array, share zend_array* in reference) } */ enum zval_ref_type { IG_REF_IS_REFERENCE, // Points to zend_reference IG_REF_IS_OBJECT, // Points to zend_object IG_REF_IS_ARRAY, // Points to zend_object }; struct igbinary_value_ref { // We reuse temporary values for object properties that are references or arrays. union { zend_reference *reference; zend_object *object; zend_array *array; } reference; enum zval_ref_type type; }; /** Unserializer data. * @author Oleg Grenrus */ struct igbinary_unserialize_data { const uint8_t *buffer; /**< Buffer. */ const uint8_t *buffer_end; /**< Buffer size. */ const uint8_t *buffer_ptr; /**< Current read offset. */ zend_string **strings; /**< Unserialized strings. */ size_t strings_count; /**< Unserialized string count. */ size_t strings_capacity; /**< Unserialized string array capacity. */ struct igbinary_value_ref *references; /**< Unserialized Arrays/Objects/References */ size_t references_count; /**< Unserialized array/objects count. */ size_t references_capacity; /**< Unserialized array/object array capacity. */ zend_object **wakeup; /**< objects for calls to __wakeup. */ size_t wakeup_count; /**< count of objects in array for calls to __wakeup */ size_t wakeup_capacity; /**< capacity of objects in array for calls to __wakeup */ int error; /**< Error number. Not used. */ smart_string string0_buf; /**< Temporary buffer for strings */ }; #define IGB_REF_VAL_2(igsd, n) ((igsd)->references[(n)]) #define IGB_NEEDS_MORE_DATA(igsd, n) UNEXPECTED((size_t)((igsd)->buffer_end - (igsd)->buffer_ptr) < (n)) #define IGB_REMAINING_BYTES(igsd) ((unsigned int)((igsd)->buffer_end - (igsd)->buffer_ptr)) #define IGB_BUFFER_OFFSET(igsd) ((unsigned int)((igsd)->buffer_ptr - (igsd)->buffer)) #define WANT_CLEAR (0) #define WANT_REF (1<<1) /* }}} */ /* {{{ Memory allocator wrapper prototypes */ static inline void *igbinary_mm_wrapper_malloc(size_t size, void *context); static inline void *igbinary_mm_wrapper_realloc(void *ptr, size_t size, void *context); static inline void igbinary_mm_wrapper_free(void *ptr, void *context); /* }}} */ /* {{{ Serializing functions prototypes */ inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar, struct igbinary_memory_manager *memory_manager); inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd, int free_buffer); inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd); inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i); inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i); inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i); inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i); inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd); inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b); inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, zend_long l); inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d); inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, zend_string *s); inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len); inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class); inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object); inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *ht, zend_class_entry *ce, bool incomplete_class); inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, zend_string *name); inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z); static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z); /* }}} */ /* {{{ Unserializing functions prototypes */ inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd); inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd); inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd); inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd); inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd); inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd); inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd); inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_long *ret); inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret); inline static zend_string * igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t); inline static zend_string * igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t); inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags); inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags); inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, zend_class_entry *ce); inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags); inline static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval *const z, int flags); /* }}} */ /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_serialize, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_unserialize, 0, 0, 1) ZEND_ARG_INFO(0, str) ZEND_END_ARG_INFO() /* }}} */ /* {{{ igbinary_functions[] */ /** Exported php functions. */ zend_function_entry igbinary_functions[] = { PHP_FE(igbinary_serialize, arginfo_igbinary_serialize) PHP_FE(igbinary_unserialize, arginfo_igbinary_unserialize) PHP_FE_END }; /* }}} */ /* {{{ igbinary dependencies */ static const zend_module_dep igbinary_module_deps[] = { ZEND_MOD_REQUIRED("standard") #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) ZEND_MOD_REQUIRED("session") #endif #if defined(HAVE_APCU_SUPPORT) ZEND_MOD_OPTIONAL("apcu") #endif ZEND_MOD_END }; /* }}} */ /* {{{ igbinary_module_entry */ zend_module_entry igbinary_module_entry = { #if ZEND_MODULE_API_NO >= 20050922 STANDARD_MODULE_HEADER_EX, NULL, igbinary_module_deps, #elif ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "igbinary", igbinary_functions, PHP_MINIT(igbinary), PHP_MSHUTDOWN(igbinary), NULL, NULL, PHP_MINFO(igbinary), #if ZEND_MODULE_API_NO >= 20010901 PHP_IGBINARY_VERSION, /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ ZEND_DECLARE_MODULE_GLOBALS(igbinary) /* {{{ ZEND_GET_MODULE */ #ifdef COMPILE_DL_IGBINARY ZEND_GET_MODULE(igbinary) #endif /* }}} */ /* {{{ INI entries */ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("igbinary.compact_strings", "1", PHP_INI_ALL, OnUpdateBool, compact_strings, zend_igbinary_globals, igbinary_globals) PHP_INI_END() /* }}} */ /* {{{ php_igbinary_init_globals */ static void php_igbinary_init_globals(zend_igbinary_globals *igbinary_globals) { igbinary_globals->compact_strings = 1; } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(igbinary) { (void) type; (void) module_number; ZEND_INIT_MODULE_GLOBALS(igbinary, php_igbinary_init_globals, NULL); #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) php_session_register_serializer("igbinary", PS_SERIALIZER_ENCODE_NAME(igbinary), PS_SERIALIZER_DECODE_NAME(igbinary)); #endif #if defined(HAVE_APCU_SUPPORT) apc_register_serializer("igbinary", APC_SERIALIZER_NAME(igbinary), APC_UNSERIALIZER_NAME(igbinary), NULL); #endif REGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(igbinary) { (void) type; (void) module_number; #ifdef ZTS ts_free_id(igbinary_globals_id); #endif /* * Clean up ini entries. * Aside: It seems like the php_session_register_serializer unserializes itself, since MSHUTDOWN in ext/wddx/wddx.c doesn't exist? */ UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(igbinary) { (void) zend_module; php_info_print_table_start(); php_info_print_table_row(2, "igbinary support", "enabled"); php_info_print_table_row(2, "igbinary version", PHP_IGBINARY_VERSION); #if defined(HAVE_APCU_SUPPORT) php_info_print_table_row(2, "igbinary APCU serializer ABI", APC_SERIALIZER_ABI); #else php_info_print_table_row(2, "igbinary APC serializer ABI", "no"); #endif #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) php_info_print_table_row(2, "igbinary session support", "yes"); #else php_info_print_table_row(2, "igbinary session support", "no"); #endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* {{{ igsd management */ /* Append to list of references to take out later. Returns SIZE_MAX on allocation error. */ static inline size_t igsd_append_ref(struct igbinary_unserialize_data *igsd, struct igbinary_value_ref v) { size_t ref_n; if (igsd->references_count + 1 >= igsd->references_capacity) { while (igsd->references_count + 1 >= igsd->references_capacity) { igsd->references_capacity *= 2; } struct igbinary_value_ref *new_references = erealloc(igsd->references, sizeof(igsd->references[0]) * igsd->references_capacity); if (new_references == NULL) { return SIZE_MAX; } igsd->references = new_references; } ref_n = igsd->references_count++; IGB_REF_VAL_2(igsd, ref_n) = v; return ref_n; } static inline int igsd_defer_wakeup(struct igbinary_unserialize_data *igsd, zend_object* object) { if (igsd->wakeup_count >= igsd->wakeup_capacity) { if (igsd->wakeup_capacity == 0) { igsd->wakeup_capacity = 2; igsd->wakeup = emalloc(sizeof(igsd->wakeup[0]) * igsd->wakeup_capacity); } else { igsd->wakeup_capacity *= 2; igsd->wakeup = erealloc(igsd->wakeup, sizeof(igsd->wakeup[0]) * igsd->wakeup_capacity); if (igsd->wakeup == NULL) { return 1; } } } igsd->wakeup[igsd->wakeup_count++] = object; return 0; } /* }}} */ /* {{{ igbinary_finish_wakeup }}} */ static int igbinary_finish_wakeup(struct igbinary_unserialize_data* igsd) { if (igsd->wakeup_count == 0) { /* nothing to do */ return 0; } zval fname; size_t i; ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1); for (i = 0; i < igsd->wakeup_count; i++) { zval retval; /* return value of __wakeup */ zval rval; zend_object *obj = igsd->wakeup[i]; ZVAL_OBJ(&rval, obj); if (call_user_function_ex(CG(function_table), &rval, &fname, &retval, 0, 0, 1, NULL) == FAILURE || Z_ISUNDEF(retval)) { GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED; } zval_ptr_dtor(&retval); if (EG(exception)) { size_t j; zval_dtor(&fname); /* Don't call __destruct for any of the objects which __wakeup wasn't called on yet, either */ for (j = i + 1; j < igsd->wakeup_count; j++) { GC_FLAGS(igsd->wakeup[j]) |= IS_OBJ_DESTRUCTOR_CALLED; } return 1; } } zval_dtor(&fname); return 0; } /* {{{ Memory allocator wrappers */ static inline void *igbinary_mm_wrapper_malloc(size_t size, void *context) { (void)context; return emalloc(size); } static inline void *igbinary_mm_wrapper_realloc(void *ptr, size_t size, void *context) { (void)context; return erealloc(ptr, size); } static inline void igbinary_mm_wrapper_free(void *ptr, void *context) { (void)context; efree(ptr); } /* }}} */ /* {{{ int igbinary_serialize(uint8_t**, size_t*, zval*) */ IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z) { return igbinary_serialize_ex(ret, ret_len, z, NULL); } /* }}} */ /* {{{ int igbinary_serialize_ex(uint8_t**, size_t*, zval*, igbinary_memory_manager*) */ IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager) { struct igbinary_serialize_data igsd; uint8_t *tmpbuf; // While we can't get passed references through the PHP_FUNCTIONs igbinary declares, third party code can call us igbinary's methods with references. // See https://github.com/php-memcached-dev/php-memcached/issues/326 if (Z_TYPE_P(z) == IS_INDIRECT) { z = Z_INDIRECT_P(z); } ZVAL_DEREF(z); if (igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY, memory_manager)) { zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); return 1; } if (igbinary_serialize_header(&igsd) != 0) { zend_error(E_WARNING, "igbinary_serialize: cannot write header"); igbinary_serialize_data_deinit(&igsd, 1); return 1; } if (igbinary_serialize_zval(&igsd, z) != 0) { igbinary_serialize_data_deinit(&igsd, 1); return 1; } /* Explicit nul termination */ if (igbinary_serialize8(&igsd, 0) != 0) { igbinary_serialize_data_deinit(&igsd, 1); return 1; } /* shrink buffer to the real length, ignore errors */ tmpbuf = (uint8_t *) igsd.mm.realloc(igsd.buffer, igsd.buffer_size, igsd.mm.context); if (tmpbuf != NULL) { igsd.buffer = tmpbuf; } /* Set return values */ *ret_len = igsd.buffer_size - 1; *ret = igsd.buffer; igbinary_serialize_data_deinit(&igsd, 0); return 0; } /* }}} */ /* {{{ int igbinary_unserialize(const uint8_t *, size_t, zval **) */ IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval *z) { struct igbinary_unserialize_data igsd; igbinary_unserialize_data_init(&igsd); igsd.buffer = buf; igsd.buffer_ptr = buf; igsd.buffer_end = buf + buf_len; if (igbinary_unserialize_header(&igsd)) { igbinary_unserialize_data_deinit(&igsd); return 1; } if (igbinary_unserialize_zval(&igsd, z, WANT_CLEAR)) { igbinary_unserialize_data_deinit(&igsd); return 1; } if (igbinary_finish_wakeup(&igsd)) { igbinary_unserialize_data_deinit(&igsd); return 1; } igbinary_unserialize_data_deinit(&igsd); return 0; } /* }}} */ /* {{{ proto string igbinary_unserialize(mixed value) */ PHP_FUNCTION(igbinary_unserialize) { char *string = NULL; size_t string_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &string, &string_len) == FAILURE) { RETURN_NULL(); } if (string_len <= 0) { RETURN_FALSE; } if (igbinary_unserialize((uint8_t *) string, string_len, return_value) != 0) { /* FIXME: is this a good place? a catch all */ zval_ptr_dtor(return_value); RETURN_NULL(); } } /* }}} */ /* {{{ proto mixed igbinary_serialize(string value) */ PHP_FUNCTION(igbinary_serialize) { zval *z; uint8_t *string; size_t string_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z) == FAILURE) { RETURN_NULL(); } if (igbinary_serialize(&string, &string_len, z) != 0) { RETURN_NULL(); } RETVAL_STRINGL((char *)string, string_len); efree(string); } /* }}} */ #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) /* {{{ Serializer encode function */ PS_SERIALIZER_ENCODE_FUNC(igbinary) { zval* session_vars; zend_string *result; struct igbinary_serialize_data igsd; session_vars = &(PS(http_session_vars)); if (Z_ISREF_P(session_vars)) { session_vars = Z_REFVAL_P(session_vars); } if (Z_TYPE_P(session_vars) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(session_vars)) == 0) { return ZSTR_EMPTY_ALLOC(); } if (igbinary_serialize_data_init(&igsd, false, NULL)) { zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); return ZSTR_EMPTY_ALLOC(); } if (igbinary_serialize_header(&igsd) != 0) { zend_error(E_WARNING, "igbinary_serialize: cannot write header"); igbinary_serialize_data_deinit(&igsd, 1); return ZSTR_EMPTY_ALLOC(); } if (igbinary_serialize_array(&igsd, session_vars, false, false) != 0) { igbinary_serialize_data_deinit(&igsd, 1); zend_error(E_WARNING, "igbinary_serialize: cannot serialize session variables"); return ZSTR_EMPTY_ALLOC(); } /* Copy the buffer to a new zend_string */ /* TODO: Clean up igsd->mm, and make this a pointer swap instead? It's only used for building up the serialization data buffer. */ result = zend_string_init((const char*)igsd.buffer, igsd.buffer_size, 0); igbinary_serialize_data_deinit(&igsd, 1); return result; } /* }}} */ /* {{{ Serializer decode function */ /* This is similar to PS_SERIALIZER_DECODE_FUNC(php) from ext/session/session.c */ PS_SERIALIZER_DECODE_FUNC(igbinary) { HashTable *tmp_hash; zval z; zval *d; zend_string *key; struct igbinary_unserialize_data igsd; if (!val || vallen==0) { return SUCCESS; } if (igbinary_unserialize_data_init(&igsd) != 0) { return FAILURE; } igsd.buffer = (const uint8_t*) val; igsd.buffer_ptr = igsd.buffer; igsd.buffer_end = igsd.buffer + vallen; if (igbinary_unserialize_header(&igsd)) { igbinary_unserialize_data_deinit(&igsd); return FAILURE; } if (igbinary_unserialize_zval(&igsd, &z, WANT_CLEAR)) { igbinary_unserialize_data_deinit(&igsd); return FAILURE; } if (igbinary_finish_wakeup(&igsd)) { igbinary_unserialize_data_deinit(&igsd); return FAILURE; } igbinary_unserialize_data_deinit(&igsd); tmp_hash = HASH_OF(&z); if (tmp_hash == NULL) { zval_ptr_dtor(&z); return FAILURE; } ZEND_HASH_FOREACH_STR_KEY_VAL(tmp_hash, key, d) { if (key == NULL) { /* array key is a number, how? Skip it. */ /* ??? */ continue; } if (php_set_session_var(key, d, NULL)) { /* Added to session successfully */ /* Refcounted types such as arrays, objects, references need to have references incremented manually, so that zval_ptr_dtor doesn't clean up pointers they include. */ /* Non-refcounted types have the data copied. */ Z_TRY_ADDREF_P(d); } } ZEND_HASH_FOREACH_END(); zval_ptr_dtor(&z); return SUCCESS; } /* }}} */ #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ #if defined(HAVE_APCU_SUPPORT) /* {{{ apc_serialize function */ static int APC_SERIALIZER_NAME(igbinary) ( APC_SERIALIZER_ARGS ) { (void)config; if (igbinary_serialize(buf, buf_len, (zval *)value) == 0) { /* flipped semantics */ return 1; } return 0; } /* }}} */ /* {{{ apc_unserialize function */ static int APC_UNSERIALIZER_NAME(igbinary) ( APC_UNSERIALIZER_ARGS ) { (void)config; if (igbinary_unserialize(buf, buf_len, value) == 0) { /* flipped semantics - Succeeded. */ return 1; } /* Failed. free return value */ zval_ptr_dtor(value); ZVAL_NULL(value); /* and replace the incomplete value with null just in case APCU uses it in the future */ return 0; } /* }}} */ #endif /* {{{ igbinary_serialize_data_init */ /** Inits igbinary_serialize_data. */ inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar, struct igbinary_memory_manager *memory_manager) { int r = 0; if (memory_manager == NULL) { igsd->mm.alloc = igbinary_mm_wrapper_malloc; igsd->mm.realloc = igbinary_mm_wrapper_realloc; igsd->mm.free = igbinary_mm_wrapper_free; igsd->mm.context = NULL; } else { igsd->mm = *memory_manager; } igsd->buffer = NULL; igsd->buffer_size = 0; igsd->buffer_capacity = 32; igsd->string_count = 0; igsd->error = 0; igsd->buffer = (uint8_t *) igsd->mm.alloc(igsd->buffer_capacity, igsd->mm.context); if (igsd->buffer == NULL) { return 1; } igsd->scalar = scalar; if (!igsd->scalar) { hash_si_init(&igsd->strings, 16); hash_si_ptr_init(&igsd->references, 16); igsd->references_id = 0; } igsd->compact_strings = (bool)IGBINARY_G(compact_strings); return r; } /* }}} */ /* {{{ igbinary_serialize_data_deinit */ /** Deinits igbinary_serialize_data. */ inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd, int free_buffer) { if (free_buffer && igsd->buffer) { igsd->mm.free(igsd->buffer, igsd->mm.context); } if (!igsd->scalar) { hash_si_deinit(&igsd->strings); hash_si_ptr_deinit(&igsd->references); } } /* }}} */ /* {{{ igbinary_serialize_header */ /** Serializes header. */ inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd) { return igbinary_serialize32(igsd, IGBINARY_FORMAT_VERSION); /* version */ } /* }}} */ /* {{{ igbinary_serialize_resize */ /** Expands igbinary_serialize_data. */ inline static int igbinary_serialize_resize(struct igbinary_serialize_data *igsd, size_t size) { if (igsd->buffer_size + size < igsd->buffer_capacity) { return 0; } while (igsd->buffer_size + size >= igsd->buffer_capacity) { igsd->buffer_capacity *= 2; } igsd->buffer = (uint8_t *) igsd->mm.realloc(igsd->buffer, igsd->buffer_capacity, igsd->mm.context); if (igsd->buffer == NULL) return 1; return 0; } /* }}} */ /* {{{ igbinary_serialize8 */ /** Serialize 8bit value. */ inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i) { if (UNEXPECTED(igbinary_serialize_resize(igsd, 1))) { return 1; } igsd->buffer[igsd->buffer_size++] = i; return 0; } /* }}} */ /* {{{ igbinary_serialize16 */ /** Serialize 16bit value. */ inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i) { uint8_t* append_buffer; if (UNEXPECTED(igbinary_serialize_resize(igsd, 2))) { return 1; } append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t) (i >> 8 & 0xff); append_buffer[1] = (uint8_t) (i & 0xff); igsd->buffer_size += 2; return 0; } /* }}} */ /* {{{ igbinary_serialize32 */ /** Serialize 32bit value. */ inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i) { uint8_t* append_buffer; if (UNEXPECTED(igbinary_serialize_resize(igsd, 4))) { return 1; } append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t) (i >> 24 & 0xff); append_buffer[1] = (uint8_t) (i >> 16 & 0xff); append_buffer[2] = (uint8_t) (i >> 8 & 0xff); append_buffer[3] = (uint8_t) (i & 0xff); igsd->buffer_size += 4; return 0; } /* }}} */ /* {{{ igbinary_serialize64 */ /** Serialize 64bit value. */ inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i) { uint8_t* append_buffer; if (UNEXPECTED(igbinary_serialize_resize(igsd, 8))) { return 1; } append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t) (i >> 56 & 0xff); append_buffer[1] = (uint8_t) (i >> 48 & 0xff); append_buffer[2] = (uint8_t) (i >> 40 & 0xff); append_buffer[3] = (uint8_t) (i >> 32 & 0xff); append_buffer[4] = (uint8_t) (i >> 24 & 0xff); append_buffer[5] = (uint8_t) (i >> 16 & 0xff); append_buffer[6] = (uint8_t) (i >> 8 & 0xff); append_buffer[7] = (uint8_t) (i & 0xff); igsd->buffer_size += 8; return 0; } /* }}} */ /* {{{ igbinary_serialize_null */ /** Serializes null. */ inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd) { return igbinary_serialize8(igsd, igbinary_type_null); } /* }}} */ /* {{{ igbinary_serialize_bool */ /** Serializes bool. */ inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b) { return igbinary_serialize8(igsd, (uint8_t) (b ? igbinary_type_bool_true : igbinary_type_bool_false)); } /* }}} */ /* {{{ igbinary_serialize_long */ /** Serializes zend_long. */ inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, zend_long l) { zend_long k = l >= 0 ? l : -l; bool p = l >= 0; /* -ZEND_LONG_MIN is 0 otherwise. */ if (l == ZEND_LONG_MIN) { #if SIZEOF_ZEND_LONG == 8 if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_long64n) != 0) { return 1; } if (igbinary_serialize64(igsd, (uint64_t) 0x8000000000000000) != 0) { return 1; } #elif SIZEOF_ZEND_LONG == 4 if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_long32n) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) 0x80000000) != 0) { return 1; } #else #error "Strange sizeof(zend_long)." #endif return 0; } if (k <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long8p : igbinary_type_long8n)) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) k) != 0) { return 1; } } else if (k <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long16p : igbinary_type_long16n)) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) k) != 0) { return 1; } #if SIZEOF_ZEND_LONG == 8 } else if (k <= 0xffffffff) { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n)) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) k) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long64p : igbinary_type_long64n)) != 0) { return 1; } if (igbinary_serialize64(igsd, (uint64_t) k) != 0) { return 1; } } #elif SIZEOF_ZEND_LONG == 4 } else { if (igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n)) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) k) != 0) { return 1; } } #else #error "Strange sizeof(zend_long)." #endif return 0; } /* }}} */ /* {{{ igbinary_serialize_double */ /** Serializes double. */ inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d) { union { double d; uint64_t u; } u; if (igbinary_serialize8(igsd, igbinary_type_double) != 0) { return 1; } u.d = d; return igbinary_serialize64(igsd, u.u); } /* }}} */ /* {{{ igbinary_serialize_string */ /** Serializes string. * Serializes each string once, after first time uses pointers. */ inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, zend_string *s) { const size_t len = ZSTR_LEN(s); if (len == 0) { if (igbinary_serialize8(igsd, igbinary_type_string_empty) != 0) { return 1; } return 0; } if (!igsd->scalar && igsd->compact_strings) { struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, s, igsd->string_count); if (result.code == hash_si_code_exists) { uint32_t value = result.value; if (value <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id8) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) value) != 0) { return 1; } } else if (value <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id16) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) value) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id32) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) value) != 0) { return 1; } } return 0; } else if (EXPECTED(result.code == hash_si_code_inserted)) { /* Fall through to igbinary_serialize_chararray */ } else { return 1; /* Failed to allocate copy of string */ } } igsd->string_count++; /* A new string is being serialized - update count so that duplicate class names can be used. */ if (igbinary_serialize_chararray(igsd, ZSTR_VAL(s), len) != 0) { return 1; } return 0; } /* }}} */ /* {{{ igbinary_serialize_chararray */ /** Serializes string data. */ inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len) { if (len <= 0xff) { if (igbinary_serialize8(igsd, igbinary_type_string8) != 0) { return 1; } if (igbinary_serialize8(igsd, len) != 0) { return 1; } } else if (len <= 0xffff) { if (igbinary_serialize8(igsd, igbinary_type_string16) != 0) { return 1; } if (igbinary_serialize16(igsd, len) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, igbinary_type_string32) != 0) { return 1; } if (igbinary_serialize32(igsd, len) != 0) { return 1; } } if (igbinary_serialize_resize(igsd, len)) { return 1; } memcpy(igsd->buffer+igsd->buffer_size, s, len); igsd->buffer_size += len; return 0; } /* }}} */ /* {{{ igbinay_serialize_array */ /** Serializes array or objects inner properties. */ inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class) { /* If object=true: z is IS_OBJECT */ /* If object=false: z is either IS_ARRAY, or IS_REFERENCE pointing to an IS_ARRAY. */ HashTable *h; size_t n; zval *d; zval *z_original; zend_string *key; zend_long key_index; z_original = z; ZVAL_DEREF(z); /* hash */ h = object ? Z_OBJPROP_P(z) : HASH_OF(z); /* hash size */ n = h ? zend_hash_num_elements(h) : 0; /* incomplete class magic member */ if (n > 0 && incomplete_class) { --n; } /* if it is an array or a reference to an array, then add a reference unique to that **reference** to that array */ if (!object && igbinary_serialize_array_ref(igsd, z_original, false) == 0) { return 0; } if (n <= 0xff) { if (igbinary_serialize8(igsd, igbinary_type_array8) != 0) { return 1; } if (igbinary_serialize8(igsd, n) != 0) { return 1; } } else if (n <= 0xffff) { if (igbinary_serialize8(igsd, igbinary_type_array16) != 0) { return 1; } if (igbinary_serialize16(igsd, n) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, igbinary_type_array32) != 0) { return 1; } if (igbinary_serialize32(igsd, n) != 0) { return 1; } } if (n == 0) { return 0; } /* serialize properties. */ ZEND_HASH_FOREACH_KEY_VAL(h, key_index, key, d) { /* skip magic member in incomplete classes */ if (incomplete_class && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) { continue; } if (key == NULL) { /* Key is numeric */ if (igbinary_serialize_long(igsd, key_index) != 0) { return 1; } } else { /* Key is string */ if (igbinary_serialize_string(igsd, key) != 0) { return 1; } } if (d == NULL) { php_error_docref(NULL, E_NOTICE, "Received NULL value from hash."); return 1; } /* https://wiki.php.net/phpng-int - This is a weak pointer, completely different from a PHP reference (&$foo has a type of IS_REFERENCE) */ if (Z_TYPE_P(d) == IS_INDIRECT) { d = Z_INDIRECT_P(d); } /* we should still add element even if it's not OK, * since we already wrote the length of the array before */ if (Z_TYPE_P(d) == IS_UNDEF) { if (igbinary_serialize_null(igsd)) { return 1; } } else { if (igbinary_serialize_zval(igsd, d)) { return 1; } } } ZEND_HASH_FOREACH_END(); return 0; } /* }}} */ /* {{{ igbinary_serialize_array_ref */ /** Serializes array reference (or reference in an object). Returns 0 on success. */ inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object) { size_t t; zend_uintptr_t key; /* The address of the pointer to the zend_refcounted struct or other struct */ static int INVALID_KEY; /* Not used, but we take the pointer of this knowing other zvals wont share it*/ /* Similar to php_var_serialize_intern's first part, as well as php_add_var_hash, for printing R: (reference) or r:(object) */ /* However, it differs from the built in serialize() in that references to objects are preserved when serializing and unserializing? (TODO check, test for backwards compatibility) */ zend_bool is_ref = Z_ISREF_P(z); /* Do I have to dereference object references so that reference ids will be the same as in php5? */ /* If I do, then more tests fail. */ /* is_ref || IS_OBJECT implies it has a unique refcounted struct */ if (object && Z_TYPE_P(z) == IS_OBJECT) { key = (zend_uintptr_t) Z_OBJ_HANDLE_P(z); /* expand object handle(uint32_t), cast to 32-bit/64-bit pointer */ } else if (is_ref) { /* NOTE: PHP removed switched from `zval*` to `zval` for the values stored in HashTables. If an array has two references to the same ZVAL, then those references will have different zvals. We use Z_COUNTED_P(ref), which will be the same iff the references are the same */ /* IS_REF implies there is a unique reference counting pointer for the reference */ key = (zend_uintptr_t) Z_COUNTED_P(z); } else if (Z_TYPE_P(z) == IS_ARRAY) { if (Z_REFCOUNTED_P(z)) { key = (zend_uintptr_t) Z_COUNTED_P(z); } else { /* Not sure if this could be a constant */ key = (zend_uintptr_t) z; } } else { /* Nothing else is going to reference this when this is serialized, this isn't ref counted or an object, shouldn't be reached. */ /* Increment the reference id for the deserializer, give up. */ ++igsd->references_id; php_error_docref(NULL, E_NOTICE, "igbinary_serialize_array_ref expected either object or reference (param object=%s), got neither (zend_type=%d)", object ? "true" : "false", (int)Z_TYPE_P(z)); return 1; } /* FIXME hack? If the top-level element was an array, we assume that it can't be a reference when we serialize it, */ /* because that's the way it was serialized in php5. */ /* Does this work with different forms of recursive arrays? */ if (igsd->references_id == 0 && !object) { key = (zend_uintptr_t)&INVALID_KEY; } t = hash_si_ptr_find_or_insert(&igsd->references, key, igsd->references_id); if (t == SIZE_MAX) { igsd->references_id++; return 1; } else { enum igbinary_type type; if (t <= 0xff) { type = object ? igbinary_type_objref8 : igbinary_type_ref8; if (igbinary_serialize8(igsd, (uint8_t) type) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) t) != 0) { return 1; } } else if (t <= 0xffff) { type = object ? igbinary_type_objref16 : igbinary_type_ref16; if (igbinary_serialize8(igsd, (uint8_t) type) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) t) != 0) { return 1; } } else { type = object ? igbinary_type_objref32 : igbinary_type_ref32; if (igbinary_serialize8(igsd, (uint8_t) type) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) t) != 0) { return 1; } } return 0; } return 1; } /* }}} */ /* {{{ igbinary_serialize_array_sleep */ /** Serializes object's properties array with __sleep -function. */ inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, zend_class_entry *ce, bool incomplete_class) { HashTable *object_properties; size_t n = zend_hash_num_elements(h); zval *d; zval *v; zend_string *key; /* Decrease array size by one, because of magic member (with class name) */ if (n > 0 && incomplete_class) { --n; } /* Serialize array id. */ if (n <= 0xff) { if (igbinary_serialize8(igsd, igbinary_type_array8) != 0) { return 1; } if (igbinary_serialize8(igsd, n) != 0) { return 1; } } else if (n <= 0xffff) { if (igbinary_serialize8(igsd, igbinary_type_array16) != 0) { return 1; } if (igbinary_serialize16(igsd, n) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, igbinary_type_array32) != 0) { return 1; } if (igbinary_serialize32(igsd, n) != 0) { return 1; } } if (n == 0) { return 0; } object_properties = Z_OBJPROP_P(z); ZEND_HASH_FOREACH_STR_KEY_VAL(h, key, d) { /* skip magic member in incomplete classes */ if (incomplete_class && key != NULL && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) { continue; } if (d == NULL || Z_TYPE_P(d) != IS_STRING) { php_error_docref(NULL, E_NOTICE, "__sleep should return an array only " "containing the names of instance-variables to " "serialize"); /* we should still add element even if it's not OK, * since we already wrote the length of the array before * serialize null as key-value pair */ if (igbinary_serialize_null(igsd) != 0) { return 1; } } else { zend_string *prop_name = Z_STR_P(d); if ((v = zend_hash_find(object_properties, prop_name)) != NULL) { if (igbinary_serialize_string(igsd, prop_name) != 0) { return 1; } if (Z_TYPE_P(v) == IS_INDIRECT) { v = Z_INDIRECT_P(v); } if (igbinary_serialize_zval(igsd, v) != 0) { return 1; } } else if (ce) { zend_string *mangled_prop_name; v = NULL; do { /* try private */ // TODO: check for FAILURE mangled_prop_name = zend_mangle_property_name(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), ce->type & ZEND_INTERNAL_CLASS); v = zend_hash_find(object_properties, mangled_prop_name); /* try protected */ if (v == NULL) { zend_string_release(mangled_prop_name); mangled_prop_name = zend_mangle_property_name("*", 1, ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), ce->type & ZEND_INTERNAL_CLASS); v = zend_hash_find(object_properties, mangled_prop_name); } /* Neither property exist */ if (v == NULL) { zend_string_release(mangled_prop_name); php_error_docref(NULL, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", Z_STRVAL_P(d)); if (igbinary_serialize_string(igsd, Z_STR_P(d)) != 0) { return 1; } if (igbinary_serialize_null(igsd) != 0) { return 1; } break; } if (Z_TYPE_P(v) == IS_INDIRECT) { v = Z_INDIRECT_P(v); } if (igbinary_serialize_string(igsd, mangled_prop_name) != 0) { zend_string_release(mangled_prop_name); return 1; } zend_string_release(mangled_prop_name); if (igbinary_serialize_zval(igsd, v) != 0) { return 1; } } while (0); } else { /* if all else fails, just serialize the value in anyway. */ if (igbinary_serialize_string(igsd, Z_STR_P(d)) != 0) { return 1; } if (Z_TYPE_P(v) == IS_INDIRECT) { v = Z_INDIRECT_P(v); } if (igbinary_serialize_zval(igsd, v) != 0) { return 1; } } } } ZEND_HASH_FOREACH_END(); return 0; } /* }}} */ /* {{{ igbinary_serialize_object_name */ /** Serialize object name. */ inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, zend_string *class_name) { struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, class_name, igsd->string_count); if (result.code == hash_si_code_inserted) { const size_t name_len = ZSTR_LEN(class_name); igsd->string_count += 1; if (name_len <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object8) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) name_len) != 0) { return 1; } } else if (name_len <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object16) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) name_len) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object32) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) name_len) != 0) { return 1; } } if (igbinary_serialize_resize(igsd, name_len)) { return 1; } memcpy(igsd->buffer+igsd->buffer_size, ZSTR_VAL(class_name), name_len); igsd->buffer_size += name_len; } else if (EXPECTED(result.code == hash_si_code_exists)) { /* already serialized string */ uint32_t value = result.value; if (value <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id8) != 0) { return 1; } if (igbinary_serialize8(igsd, (uint8_t) value) != 0) { return 1; } } else if (value <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id16) != 0) { return 1; } if (igbinary_serialize16(igsd, (uint16_t) value) != 0) { return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id32) != 0) { return 1; } if (igbinary_serialize32(igsd, (uint32_t) value) != 0) { return 1; } } } else { return 1; /* Failed to allocate copy of string */ } return 0; } /* }}} */ /* {{{ igbinary_serialize_object */ /** Serialize object. * @see ext/standard/var.c * */ inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z) { PHP_CLASS_ATTRIBUTES; zend_class_entry *ce; zval f; zval h; int r = 0; unsigned char *serialized_data = NULL; size_t serialized_len; if (igbinary_serialize_array_ref(igsd, z, true) == 0) { return 0; } ce = Z_OBJCE_P(z); /* custom serializer */ if (ce && ce->serialize != NULL) { if (ce->serialize(z, &serialized_data, &serialized_len, (zend_serialize_data *)NULL) == SUCCESS && !EG(exception)) { if (igbinary_serialize_object_name(igsd, ce->name) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (serialized_len <= 0xff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser8) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (igbinary_serialize8(igsd, (uint8_t) serialized_len) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } } else if (serialized_len <= 0xffff) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser16) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (igbinary_serialize16(igsd, (uint16_t) serialized_len) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } } else { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser32) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } if (igbinary_serialize32(igsd, (uint32_t) serialized_len) != 0) { if (serialized_data) { efree(serialized_data); } return 1; } } if (igbinary_serialize_resize(igsd, serialized_len)) { if (serialized_data) { efree(serialized_data); } return 1; } memcpy(igsd->buffer+igsd->buffer_size, serialized_data, serialized_len); igsd->buffer_size += serialized_len; } else if (EG(exception)) { /* exception, return failure */ r = 1; } else { /* Serialization callback failed, assume null output */ r = igbinary_serialize_null(igsd); } if (serialized_data) { efree(serialized_data); } return r; } /* serialize class name */ PHP_SET_CLASS_ATTRIBUTES(z); if (igbinary_serialize_object_name(igsd, class_name) != 0) { PHP_CLEANUP_CLASS_ATTRIBUTES(); return 1; } PHP_CLEANUP_CLASS_ATTRIBUTES(); if (ce && ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep") - 1)) { /* function name string */ ZVAL_STRINGL(&f, "__sleep", sizeof("__sleep") - 1); ZVAL_UNDEF(&h); /* calling z->__sleep */ r = call_user_function_ex(CG(function_table), z, &f, &h, 0, 0, 1, NULL); zval_dtor(&f); if (r == SUCCESS && !EG(exception)) { r = 0; if (Z_TYPE(h) == IS_UNDEF) { /* FIXME: is this ok? */ /* Valid, but skip */ } else if (HASH_OF(&h)) { r = igbinary_serialize_array_sleep(igsd, z, HASH_OF(&h), ce, incomplete_class); } else { php_error_docref(NULL, E_NOTICE, "__sleep should return an array only " "containing the names of instance-variables to " "serialize"); /* empty array */ r = igbinary_serialize8(igsd, igbinary_type_array8); if (r == 0) { r = igbinary_serialize8(igsd, 0); } } } else { r = 1; } /* cleanup */ zval_ptr_dtor(&h); return r; } else { return igbinary_serialize_array(igsd, z, true, incomplete_class); } } /* }}} */ /* {{{ igbinary_serialize_zval */ /** Serialize zval. */ static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z) { if (Z_ISREF_P(z)) { if (igbinary_serialize8(igsd, (uint8_t) igbinary_type_ref) != 0) { return 1; } switch (Z_TYPE_P(Z_REFVAL_P(z))) { case IS_ARRAY: return igbinary_serialize_array(igsd, z, false, false); case IS_OBJECT: break; /* Fall through */ default: /* Serialize a reference if zval already added */ if (igbinary_serialize_array_ref(igsd, z, false) == 0) { return 0; } /* Fall through */ } ZVAL_DEREF(z); } switch (Z_TYPE_P(z)) { case IS_RESOURCE: return igbinary_serialize_null(igsd); case IS_OBJECT: return igbinary_serialize_object(igsd, z); case IS_ARRAY: /* if is_ref, then php5 would have called igbinary_serialize_array_ref */ return igbinary_serialize_array(igsd, z, false, false); case IS_STRING: return igbinary_serialize_string(igsd, Z_STR_P(z)); case IS_LONG: return igbinary_serialize_long(igsd, Z_LVAL_P(z)); case IS_NULL: return igbinary_serialize_null(igsd); case IS_UNDEF: // As of php 7.1.3, started seeing "zval has unknown type 0" zend_error(E_WARNING, "igbinary_serialize_zval: zval has unexpected type IS_UNDEF(0)"); return igbinary_serialize_null(igsd); case IS_TRUE: return igbinary_serialize_bool(igsd, 1); case IS_FALSE: return igbinary_serialize_bool(igsd, 0); case IS_DOUBLE: return igbinary_serialize_double(igsd, Z_DVAL_P(z)); default: zend_error(E_ERROR, "igbinary_serialize_zval: zval has unknown type %d", (int)Z_TYPE_P(z)); /* not reached */ return 1; } return 0; } /* }}} */ /* {{{ igbinary_unserialize_data_init */ /** Inits igbinary_unserialize_data_init. */ inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd) { smart_string empty_str = { 0 }; igsd->buffer = NULL; igsd->buffer_end = NULL; igsd->buffer_ptr = NULL; igsd->strings = NULL; igsd->strings_count = 0; igsd->strings_capacity = 4; igsd->string0_buf = empty_str; igsd->error = 0; igsd->references = NULL; igsd->references_count = 0; igsd->references_capacity = 4; igsd->references = emalloc(sizeof(igsd->references[0]) * igsd->references_capacity); if (igsd->references == NULL) { return 1; } igsd->strings = (zend_string **) emalloc(sizeof(zend_string *) * igsd->strings_capacity); if (igsd->strings == NULL) { efree(igsd->references); igsd->references = NULL; return 1; } /** Don't bother allocating zvals which __wakeup, probably not common */ igsd->wakeup = NULL; igsd->wakeup_count = 0; igsd->wakeup_capacity = 0; return 0; } /* }}} */ /* {{{ igbinary_unserialize_data_deinit */ /** Deinits igbinary_unserialize_data_init. */ inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd) { if (igsd->strings) { size_t i; for (i = 0; i < igsd->strings_count; i++) { zend_string *s = igsd->strings[i]; #if ZEND_DEBUG ZEND_ASSERT(GC_REFCOUNT(s) >= 1); #endif zend_string_release(s); } efree(igsd->strings); igsd->strings = NULL; } if (igsd->references) { efree(igsd->references); igsd->references = NULL; } if (igsd->wakeup) { /* size_t i; size_t n = igsd->wakeup_count; for (i = 0; i < n; i++) { convert_to_null(&igsd->wakeup[i]); } */ efree(igsd->wakeup); } smart_string_free(&igsd->string0_buf); return; } /* }}} */ /* {{{ igbinary_unserialize_header_emit_warning */ /* Precondition: igsd->buffer_size >= 4 */ inline static void igbinary_unserialize_header_emit_warning(struct igbinary_unserialize_data *igsd, int version) { int i; char buf[9], *it; for (i = 0; i < 4; i++) { if (!isprint((int)igsd->buffer[i])) { if (version != 0 && (((unsigned int)version) & 0xff000000) == (unsigned int)version) { // Check if high order byte was set instead of low order byte zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u (wrong endianness?)", (unsigned int) version, 0x00000001, (unsigned int) IGBINARY_FORMAT_VERSION); return; } // Binary data, or a version number from a future release. zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u", (unsigned int) version, 0x00000001, (unsigned int) IGBINARY_FORMAT_VERSION); return; } } for (it = buf, i = 0; i < 4; i++) { char c = igsd->buffer[i]; if (c == '"' || c == '\\') { *it++ = '\\'; } *it++ = c; } *it = '\0'; zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: \"%s\"..., should begin with a binary version header of \"\\x00\\x00\\x00\\x01\" or \"\\x00\\x00\\x00\\x%02x\"", buf, (int)IGBINARY_FORMAT_VERSION); } /* }}} */ /* {{{ igbinary_unserialize_header */ /** Unserialize header. Check for version. */ inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd) { uint32_t version; if (IGB_NEEDS_MORE_DATA(igsd, 5)) { zend_error(E_WARNING, "igbinary_unserialize_header: expected at least 5 bytes of data, got %u byte(s)", IGB_REMAINING_BYTES(igsd)); return 1; } version = igbinary_unserialize32(igsd); /* Support older version 1 and the current format 2 */ if (version == IGBINARY_FORMAT_VERSION || version == 0x00000001) { return 0; } else { igbinary_unserialize_header_emit_warning(igsd, version); return 1; } } /* }}} */ /* {{{ igbinary_unserialize8 */ /** Unserialize 8bit value. */ inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd) { return *(igsd->buffer_ptr++); } /* }}} */ /* {{{ igbinary_unserialize16 */ /** Unserialize 16bit value. */ inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd) { uint16_t ret = ((uint16_t) (igsd->buffer_ptr[0]) << 8) | ((uint16_t) (igsd->buffer_ptr[1])); igsd->buffer_ptr += 2; return ret; } /* }}} */ /* {{{ igbinary_unserialize32 */ /** Unserialize 32bit value. */ inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd) { uint32_t ret = ((uint32_t) (igsd->buffer_ptr[0]) << 24) | ((uint32_t) (igsd->buffer_ptr[1]) << 16) | ((uint32_t) (igsd->buffer_ptr[2]) << 8) | ((uint32_t) (igsd->buffer_ptr[3])); igsd->buffer_ptr += 4; return ret; } /* }}} */ /* {{{ igbinary_unserialize64 */ /** Unserialize 64bit value. */ inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd) { uint64_t ret = ((uint64_t) ((igsd->buffer_ptr[0])) << 56) | ((uint64_t) ((igsd->buffer_ptr[1])) << 48) | ((uint64_t) ((igsd->buffer_ptr[2])) << 40) | ((uint64_t) ((igsd->buffer_ptr[3])) << 32) | ((uint64_t) ((igsd->buffer_ptr[4])) << 24) | ((uint64_t) ((igsd->buffer_ptr[5])) << 16) | ((uint64_t) ((igsd->buffer_ptr[6])) << 8) | ((uint64_t) ((igsd->buffer_ptr[7])) << 0); igsd->buffer_ptr += 8; return ret; } /* }}} */ /* {{{ igbinary_unserialize_long */ /** Unserializes zend_long */ inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_long *ret) { uint32_t tmp32; #if SIZEOF_ZEND_LONG == 8 uint64_t tmp64; #endif if (t == igbinary_type_long8p || t == igbinary_type_long8n) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } *ret = (zend_long) (t == igbinary_type_long8n ? -1 : 1) * igbinary_unserialize8(igsd); } else if (t == igbinary_type_long16p || t == igbinary_type_long16n) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } *ret = (zend_long) (t == igbinary_type_long16n ? -1 : 1) * igbinary_unserialize16(igsd); } else if (t == igbinary_type_long32p || t == igbinary_type_long32n) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } /* check for boundaries */ tmp32 = igbinary_unserialize32(igsd); #if SIZEOF_ZEND_LONG == 4 if (tmp32 > 0x80000000 || (tmp32 == 0x80000000 && t == igbinary_type_long32p)) { zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform?"); tmp32 = 0; /* t == igbinary_type_long32p ? LONG_MAX : LONG_MIN; */ } #endif *ret = (zend_long) (t == igbinary_type_long32n ? -1 : 1) * tmp32; } else if (t == igbinary_type_long64p || t == igbinary_type_long64n) { #if SIZEOF_ZEND_LONG == 8 if (IGB_NEEDS_MORE_DATA(igsd, 8)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } /* check for boundaries */ tmp64 = igbinary_unserialize64(igsd); if (tmp64 > 0x8000000000000000 || (tmp64 == 0x8000000000000000 && t == igbinary_type_long64p)) { zend_error(E_WARNING, "igbinary_unserialize_long: too big 64bit long."); tmp64 = 0; /* t == igbinary_type_long64p ? LONG_MAX : LONG_MIN */; } *ret = (zend_long) (t == igbinary_type_long64n ? -1 : 1) * tmp64; #elif SIZEOF_ZEND_LONG == 4 /* can't put 64bit long into 32bit one, placeholder zero */ *ret = 0; igbinary_unserialize64(igsd); zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform"); #else #error "Strange sizeof(zend_long)." #endif } else { *ret = 0; zend_error(E_WARNING, "igbinary_unserialize_long: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } return 0; } /* }}} */ /* {{{ igbinary_unserialize_double */ /** Unserializes double. */ inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret) { union { double d; uint64_t u; } u; (void) t; if (IGB_NEEDS_MORE_DATA(igsd, 8)) { zend_error(E_WARNING, "igbinary_unserialize_double: end-of-data"); return 1; } u.u = igbinary_unserialize64(igsd); *ret = u.d; return 0; } /* }}} */ /* {{{ igbinary_unserialize_string */ /** Unserializes string. Unserializes both actual string or by string id. Returns NULL on error. */ inline static zend_string* igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t) { size_t i; zend_string *zstr; if (t == igbinary_type_string_id8 || t == igbinary_type_object_id8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return NULL; } i = igbinary_unserialize8(igsd); } else if (t == igbinary_type_string_id16 || t == igbinary_type_object_id16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return NULL; } i = igbinary_unserialize16(igsd); } else if (t == igbinary_type_string_id32 || t == igbinary_type_object_id32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return NULL; } i = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_string: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return NULL; } if (i >= igsd->strings_count) { zend_error(E_WARNING, "igbinary_unserialize_string: string index is out-of-bounds"); return NULL; } zstr = igsd->strings[i]; // Add one more ref - Callers of this will decrease refs as needed zend_string_addref(zstr); return zstr; } /* }}} */ /* {{{ igbinary_unserialize_chararray */ /** Unserializes chararray of string. Returns NULL on error. */ inline static zend_string* igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t) { size_t l; zend_string *zstr; if (t == igbinary_type_string8 || t == igbinary_type_object8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } l = igbinary_unserialize8(igsd); if (IGB_NEEDS_MORE_DATA(igsd, l)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } /* Requires converting these into interned strings. Maybe add one-char in v3 of igbinary format? if (l == 1) { zstr = ZSTR_CHAR((zend_uchar)igsd->buffer[igsd->buffer_offset]); igsd->strings[igsd->strings_count] = zstr; igsd->strings_count += 1; igsd->buffer_offset++; return zstr; } */ } else if (t == igbinary_type_string16 || t == igbinary_type_object16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } l = igbinary_unserialize16(igsd); if (IGB_NEEDS_MORE_DATA(igsd, l)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } } else if (t == igbinary_type_string32 || t == igbinary_type_object32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } l = igbinary_unserialize32(igsd); if (IGB_NEEDS_MORE_DATA(igsd, l)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } } else { zend_error(E_WARNING, "igbinary_unserialize_chararray: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return NULL; } if (igsd->strings_count + 1 > igsd->strings_capacity) { zend_string **new_strings; igsd->strings_capacity *= 2; new_strings = (zend_string **) erealloc(igsd->strings, sizeof(zend_string *) * igsd->strings_capacity); if (new_strings == NULL) { // The cleanup function will take care of destroying the allocated zend_strings. return NULL; } igsd->strings = new_strings; } zstr = zend_string_init((const char*)(igsd->buffer + IGB_BUFFER_OFFSET(igsd)), l, 0); igsd->buffer_ptr += l; if (zstr == NULL) { return NULL; } zend_string_addref(zstr); igsd->strings[igsd->strings_count] = zstr; igsd->strings_count += 1; return zstr; } /* }}} */ /* {{{ igbinary_unserialize_array */ /** Unserializes array. */ inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags) { /* WANT_REF means that z will be wrapped by an IS_REFERENCE */ size_t n; size_t i; zval v; zval *vp; zval *z_deref; enum igbinary_type key_type; HashTable *h; if (t == igbinary_type_array8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_array16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_array32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_array: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } /* n cannot be larger than the number of minimum "objects" in the array */ if (IGB_NEEDS_MORE_DATA(igsd, n)) { zend_error(E_WARNING, "igbinary_unserialize_array: data size %zu smaller that requested array length %zu.", IGB_REMAINING_BYTES(igsd), (unsigned int) n); return 1; } z_deref = z; if (flags & WANT_REF) { if (!Z_ISREF_P(z)) { ZVAL_NEW_REF(z, z); z_deref = Z_REFVAL_P(z); } } array_init_size(z_deref, n); { struct igbinary_value_ref ref; if (flags & WANT_REF) { // We converted to reference earlier. ref.reference.reference = Z_REF_P(z); ref.type = IG_REF_IS_REFERENCE; } else { ref.reference.array = Z_ARR_P(z_deref); ref.type = IG_REF_IS_ARRAY; } /* add the new array to the list of unserialized references */ if (igsd_append_ref(igsd, ref) == SIZE_MAX) { return 1; } } /* empty array */ if (n == 0) { return 0; } h = HASH_OF(z_deref); for (i = 0; i < n; i++) { zend_long key_index = 0; zend_string *key_str = NULL; /* NULL means use key_index */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); zval_dtor(z); ZVAL_NULL(z); return 1; } key_type = (enum igbinary_type) igbinary_unserialize8(igsd); switch (key_type) { case igbinary_type_long8p: case igbinary_type_long8n: case igbinary_type_long16p: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: if (igbinary_unserialize_long(igsd, key_type, &key_index)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: key_str = igbinary_unserialize_string(igsd, key_type); if (key_str == NULL) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: key_str = igbinary_unserialize_chararray(igsd, key_type); if (key_str == NULL) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string_empty: key_str = ZSTR_EMPTY_ALLOC(); break; case igbinary_type_null: continue; default: zend_error(E_WARNING, "igbinary_unserialize_array: unknown key type '%02x', position %zu", key_type, IGB_BUFFER_OFFSET(igsd)); zval_dtor(z); ZVAL_UNDEF(z); return 1; } /* first add key into array so references can properly and not stack allocated zvals */ /* Use NULL because inserting UNDEF into array does not add a new element */ ZVAL_NULL(&v); if (key_str != NULL) { vp = zend_hash_update(h, key_str, &v); zend_string_release(key_str); } else { vp = zend_hash_index_update(h, key_index, &v); } ZEND_ASSERT(vp != NULL); if (Z_TYPE_P(vp) == IS_INDIRECT) { vp = Z_INDIRECT_P(vp); } ZEND_ASSERT(vp != NULL); if (igbinary_unserialize_zval(igsd, vp, WANT_CLEAR)) { /* zval_ptr_dtor(z); */ /* zval_ptr_dtor(vp); */ return 1; } } return 0; } /* }}} */ /* {{{ igbinary_unserialize_object_properties */ /** Unserializes array of object properties. */ inline static int igbinary_unserialize_object_properties(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z) { /* WANT_REF means that z will be wrapped by an IS_REFERENCE */ size_t n; size_t i; zval v; zval *vp; zval *z_deref; enum igbinary_type key_type; HashTable *h; zend_bool did_extend; if (t == igbinary_type_array8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_array16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_array32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_array: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } /* n cannot be larger than the number of minimum "objects" in the array */ if (IGB_NEEDS_MORE_DATA(igsd, n)) { zend_error(E_WARNING, "%s: data size %zu smaller that requested array length %zu.", "igbinary_unserialize_array", IGB_REMAINING_BYTES(igsd), n); return 1; } z_deref = z; ZVAL_DEREF(z_deref); /* empty array */ if (n == 0) { return 0; } h = HASH_OF(z_deref); did_extend = 0; for (i = 0; i < n; i++) { zend_string *key_str = NULL; /* NULL means use key_index */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); zval_dtor(z); ZVAL_NULL(z); return 1; } key_type = (enum igbinary_type) igbinary_unserialize8(igsd); switch (key_type) { case igbinary_type_long8p: case igbinary_type_long8n: case igbinary_type_long16p: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: { zend_long key_index = 0; if (igbinary_unserialize_long(igsd, key_type, &key_index)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } key_str = zend_long_to_str(key_index); if (UNEXPECTED(key_str == NULL)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; } case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: key_str = igbinary_unserialize_string(igsd, key_type); if (UNEXPECTED(key_str == NULL)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: key_str = igbinary_unserialize_chararray(igsd, key_type); if (UNEXPECTED(key_str == NULL)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string_empty: key_str = ZSTR_EMPTY_ALLOC(); break; case igbinary_type_null: continue; /* Skip unserializing this element, serialized with no value. In C, this applies to loop, not switch. */ default: zend_error(E_WARNING, "igbinary_unserialize_array: unknown key type '%02x', position %zu", key_type, IGB_BUFFER_OFFSET(igsd)); zval_dtor(z); ZVAL_UNDEF(z); return 1; } /* first add key into array so references can properly and not stack allocated zvals */ /* Use NULL because inserting UNDEF into array does not add a new element */ ZVAL_NULL(&v); zval *prototype_value = zend_hash_find(h, key_str); if (prototype_value != NULL) { if (Z_TYPE_P(prototype_value) == IS_INDIRECT) { prototype_value = Z_INDIRECT_P(prototype_value); } convert_to_null(prototype_value); vp = zend_hash_update_ind(h, key_str, &v); } else { if (!did_extend) { zend_long remaining_elements = n - i; /* Copied from var_unserializer.re. Need to ensure that IGB_REF_VAL doesn't point to invalid data. */ /* Worst case: All remaining_elements of the added properties are dynamic. */ zend_hash_extend(h, zend_hash_num_elements(h) + remaining_elements, (h->u.flags & HASH_FLAG_PACKED)); did_extend = 1; } vp = zend_hash_add_new(h, key_str, &v); } zend_string_release(key_str); ZEND_ASSERT(vp != NULL); if (Z_TYPE_P(vp) == IS_INDIRECT) { vp = Z_INDIRECT_P(vp); } ZEND_ASSERT(vp != NULL); if (igbinary_unserialize_zval(igsd, vp, WANT_CLEAR)) { /* zval_ptr_dtor(z); */ /* zval_ptr_dtor(vp); */ return 1; } } return 0; } /* }}} */ /* {{{ igbinary_unserialize_object_ser */ /** Unserializes object's property array of objects implementing Serializable -interface. */ inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, zend_class_entry *ce) { size_t n; int ret; php_unserialize_data_t var_hash; if (ce->unserialize == NULL) { zend_error(E_WARNING, "Class %s has no unserializer", ce->name); return 1; } if (t == igbinary_type_object_ser8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_object_ser16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_object_ser32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_object_ser: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } if (IGB_NEEDS_MORE_DATA(igsd, n)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } PHP_VAR_UNSERIALIZE_INIT(var_hash); ret = ce->unserialize(z, ce, (const unsigned char*)igsd->buffer_ptr, n, (zend_unserialize_data *)&var_hash); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (ret != SUCCESS || EG(exception)) { return 1; } igsd->buffer_ptr += n; return 0; } /* }}} */ /* {{{ igbinary_unserialize_object */ /** Unserialize object. * @see ext/standard/var_unserializer.c */ inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags) { zend_class_entry *ce; size_t ref_n; zend_string *class_name; int r; bool incomplete_class = false; bool is_from_serialized_data = false; zval user_func; zval retval; zval args[1]; if (t == igbinary_type_object8 || t == igbinary_type_object16 || t == igbinary_type_object32) { class_name = igbinary_unserialize_chararray(igsd, t); } else if (t == igbinary_type_object_id8 || t == igbinary_type_object_id16 || t == igbinary_type_object_id32) { class_name = igbinary_unserialize_string(igsd, t); } else { zend_error(E_WARNING, "igbinary_unserialize_object: unknown object type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } if (class_name == NULL) { return 1; } do { /* Try to find class directly */ if ((ce = zend_lookup_class(class_name)) != NULL) { /* FIXME: lookup class may cause exception in load callback */ break; } /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } /* Call unserialize callback */ ZVAL_STRING(&user_func, PG(unserialize_callback_func)); /* FIXME: Do we need a str copy? */ /* FIXME: Release arg[0] */ /* FIXME: Release class_name */ ZVAL_STR_COPY(&args[0], class_name); if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) { php_error_docref(NULL, E_WARNING, "defined (%s) but not found", ZSTR_VAL(class_name)); incomplete_class = 1; ce = PHP_IC_ENTRY; zval_ptr_dtor(&args[0]); zval_ptr_dtor(&user_func); break; } /* FIXME: always safe? */ zval_ptr_dtor(&retval); /* The callback function may have defined the class */ ce = zend_lookup_class(class_name); if (!ce) { php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", ZSTR_VAL(class_name)); incomplete_class = true; ce = PHP_IC_ENTRY; } zval_ptr_dtor(&args[0]); zval_ptr_dtor(&user_func); } while (0); /* previous user function call may have raised an exception */ if (EG(exception)) { zend_string_release(class_name); return 1; } /* add this to the list of unserialized references, get the index */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object: end-of-data"); zend_string_release(class_name); return 1; } { struct igbinary_value_ref ref; ref_n = igsd_append_ref(igsd, ref); if (ref_n == SIZE_MAX) { zend_string_release(class_name); return 1; } } t = (enum igbinary_type) igbinary_unserialize8(igsd); switch (t) { case igbinary_type_array8: case igbinary_type_array16: case igbinary_type_array32: { if (object_init_ex(z, ce) != SUCCESS) { php_error_docref(NULL, E_NOTICE, "igbinary unable to create object for class entry"); r = 1; break; } if (incomplete_class) { php_store_class_name(z, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); } struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n);; if ((flags & WANT_REF) != 0) { ZVAL_MAKE_REF(z); ref->reference.reference = Z_REF_P(z); ref->type = IG_REF_IS_REFERENCE; } else { ref->reference.object = Z_OBJ_P(z); ref->type = IG_REF_IS_OBJECT; } r = igbinary_unserialize_object_properties(igsd, t, z); break; } case igbinary_type_object_ser8: case igbinary_type_object_ser16: case igbinary_type_object_ser32: { is_from_serialized_data = true; // FIXME will this break if z isn't an object? r = igbinary_unserialize_object_ser(igsd, t, z, ce); if (r != 0) { break; } if (incomplete_class) { php_store_class_name(z, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); } struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n); if ((flags & WANT_REF) != 0) { ZVAL_MAKE_REF(z); ref->reference.reference = Z_REF_P(z); ref->type = IG_REF_IS_REFERENCE; } else { ref->reference.object = Z_OBJ_P(z); ref->type = IG_REF_IS_OBJECT; } break; } default: zend_error(E_WARNING, "igbinary_unserialize_object: unknown object inner type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); r = 1; } zend_string_release(class_name); class_name = NULL; /* If unserialize was successful, call __wakeup if __wakeup exists for this object. */ /* (But don't call __wakeup() if Serializable::unserialize was called */ if (r == 0 && !is_from_serialized_data) { struct igbinary_value_ref * const ref = &IGB_REF_VAL_2(igsd, ref_n); zval ztemp; zend_object *object; if (ref->type == IG_REF_IS_OBJECT) { object = ref->reference.object; ZVAL_OBJ(&ztemp, object); } else if (ref->type == IG_REF_IS_REFERENCE) { ztemp = ref->reference.reference->val; if (Z_TYPE(ztemp) != IS_OBJECT) { zend_error(E_WARNING, "igbinary_unserialize_object preparing to __wakeup: got reference to non-object somehow", t, IGB_BUFFER_OFFSET(igsd)); return 1; } object = Z_OBJ(ztemp); } else { zend_error(E_WARNING, "igbinary_unserialize_object preparing to __wakeup: created non-object somehow", t, IGB_BUFFER_OFFSET(igsd)); return 1; } zend_class_entry *ztemp_ce; /* May have created a reference while deserializing an object, if it was recursive. */ ztemp_ce = Z_OBJCE(ztemp); if (ztemp_ce != PHP_IC_ENTRY && zend_hash_str_exists(&ztemp_ce->function_table, "__wakeup", sizeof("__wakeup") - 1)) { if (igsd_defer_wakeup(igsd, object)) { r = 1; } } } /* ZVAL_COPY_VALUE(z, IGB_REF_VAL(igsd, ref_n)); */ return r; } /* }}} */ /* {{{ igbinary_unserialize_ref */ /** Unserializes array or object by reference. */ inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags) { size_t n; if (t == igbinary_type_ref8 || t == igbinary_type_objref8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_ref16 || t == igbinary_type_objref16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_ref32 || t == igbinary_type_objref32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_ref: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } if (n >= igsd->references_count) { zend_error(E_WARNING, "igbinary_unserialize_ref: invalid reference %zu >= %zu", (int) n, (int)igsd->references_count); return 1; } if (z != NULL) { /* FIXME: check with is refcountable or some such */ zval_ptr_dtor(z); ZVAL_UNDEF(z); } struct igbinary_value_ref* ref_ptr = &IGB_REF_VAL_2(igsd, n); struct igbinary_value_ref ref = *ref_ptr; /** * Permanently convert the zval in IGB_REF_VAL() into a IS_REFERENCE if it wasn't already one. * TODO: Can there properly be multiple reference groups to an object? * Similar to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re , for "R:" * Using `flags` because igbinary_unserialize_ref might be used both for copy on writes ($a = $b = [2]) and by PHP references($a = &$b). */ if ((flags & WANT_REF) != 0) { /* Want to create an IS_REFERENCE, not just to share the same value until modified. */ switch (ref.type) { case IG_REF_IS_OBJECT: ZVAL_OBJ(z, ref.reference.object); Z_TRY_ADDREF_P(z); ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */ /* replace the entry in IGB_REF_VAL with a reference. */ ref_ptr->reference.reference = Z_REF_P(z); ref_ptr->type = IG_REF_IS_REFERENCE; break; case IG_REF_IS_ARRAY: ZVAL_ARR(z, ref.reference.array); Z_TRY_ADDREF_P(z); ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */ /* replace the entry in IGB_REF_VAL with a reference. */ ref_ptr->reference.reference = Z_REF_P(z); ref_ptr->type = IG_REF_IS_REFERENCE; break; case IG_REF_IS_REFERENCE: // This is already a reference, convert into reference count. ZVAL_REF(z, ref.reference.reference); Z_ADDREF_P(z); break; } } else { switch (ref.type) { case IG_REF_IS_OBJECT: ZVAL_OBJ(z, ref.reference.object); Z_TRY_ADDREF_P(z); break; case IG_REF_IS_ARRAY: ZVAL_ARR(z, ref.reference.array); Z_TRY_ADDREF_P(z); break; case IG_REF_IS_REFERENCE: ZVAL_COPY(z, &(ref.reference.reference->val)); break; } } return 0; } /* }}} */ /* {{{ igbinary_unserialize_zval */ /** Unserialize zval. */ static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval *const z, int flags) { enum igbinary_type t; zend_long tmp_long; double tmp_double; zend_string *tmp_str; if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_zval: end-of-data"); return 1; } t = (enum igbinary_type) igbinary_unserialize8(igsd); switch (t) { case igbinary_type_ref: if (igbinary_unserialize_zval(igsd, z, WANT_REF)) { return 1; } /* If it is already a ref, nothing to do */ if (Z_ISREF_P(z)) { break; } const zend_uchar type = Z_TYPE_P(z); /* Permanently convert the zval in IGB_REF_VAL() into a IS_REFERENCE if it wasn't already one. */ /* TODO: Support multiple reference groups to the same object */ /* Similar to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re , for "R:" */ ZVAL_MAKE_REF(z); switch (type) { case IS_STRING: case IS_LONG: case IS_NULL: case IS_DOUBLE: case IS_FALSE: case IS_TRUE: { struct igbinary_value_ref ref; ref.reference.reference = Z_REF_P(z); ref.type = IG_REF_IS_REFERENCE; /* add the unserialized scalar to the list of unserialized references. Objects and arrays were already added in igbinary_unserialize_zval. */ if (igsd_append_ref(igsd, ref) == SIZE_MAX) { return 1; } break; } default: break; } break; case igbinary_type_objref8: case igbinary_type_objref16: case igbinary_type_objref32: case igbinary_type_ref8: case igbinary_type_ref16: case igbinary_type_ref32: if (igbinary_unserialize_ref(igsd, t, z, flags)) { return 1; } break; case igbinary_type_object8: case igbinary_type_object16: case igbinary_type_object32: case igbinary_type_object_id8: case igbinary_type_object_id16: case igbinary_type_object_id32: if (igbinary_unserialize_object(igsd, t, z, flags)) { return 1; } break; case igbinary_type_array8: case igbinary_type_array16: case igbinary_type_array32: if (igbinary_unserialize_array(igsd, t, z, flags)) { return 1; } break; case igbinary_type_string_empty: ZVAL_EMPTY_STRING(z); break; case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: tmp_str = igbinary_unserialize_string(igsd, t); if (tmp_str == NULL) { return 1; } ZVAL_STR(z, tmp_str); break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: tmp_str = igbinary_unserialize_chararray(igsd, t); if (tmp_str == NULL) { return 1; } ZVAL_STR(z, tmp_str); break; case igbinary_type_long8p: case igbinary_type_long8n: case igbinary_type_long16p: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: if (igbinary_unserialize_long(igsd, t, &tmp_long)) { return 1; } ZVAL_LONG(z, tmp_long); break; case igbinary_type_null: ZVAL_NULL(z); break; case igbinary_type_bool_false: ZVAL_BOOL(z, 0); break; case igbinary_type_bool_true: ZVAL_BOOL(z, 1); break; case igbinary_type_double: if (igbinary_unserialize_double(igsd, t, &tmp_double)) { return 1; } ZVAL_DOUBLE(z, tmp_double); break; default: zend_error(E_WARNING, "igbinary_unserialize_zval: unknown type '%02x', position %zu", t, IGB_BUFFER_OFFSET(igsd)); return 1; } return 0; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-2.0.5/src/php7/igbinary.h0000644000175000017500000000511113177372464016072 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef IGBINARY_H #define IGBINARY_H #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else # include #endif /* Forward declarations. */ struct zval; /* Constants and constant macros */ /** Binary protocol version of igbinary. */ #define IGBINARY_FORMAT_VERSION 0x00000002 #define PHP_IGBINARY_VERSION "2.0.5" /* Macros */ #ifdef PHP_WIN32 # if defined(IGBINARY_EXPORTS) || (!defined(COMPILE_DL_IGBINARY)) # define IGBINARY_API __declspec(dllexport) # elif defined(COMPILE_DL_IGBINARY) # define IGBINARY_API __declspec(dllimport) # else # define IGBINARY_API /* nothing special */ # endif #elif defined(__GNUC__) && __GNUC__ >= 4 # define IGBINARY_API __attribute__ ((visibility("default"))) #else # define IGBINARY_API /* nothing special */ #endif /** Struct that contains pointers to memory allocation and deallocation functions. * @see igbinary_serialize_data */ struct igbinary_memory_manager { void *(*alloc)(size_t size, void *context); void *(*realloc)(void *ptr, size_t new_size, void *context); void (*free)(void *ptr, void *context); void *context; }; /** Serialize zval. * Return buffer is allocated by this function with emalloc. * @param[out] ret Return buffer * @param[out] ret_len Size of return buffer * @param[in] z Variable to be serialized * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z); /** Serialize zval. * Return buffer is allocated by this function with emalloc. * @param[out] ret Return buffer * @param[out] ret_len Size of return buffer * @param[in] z Variable to be serialized * @param[in] memory_manager Pointer to the structure that contains memory allocation functions. * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager); /** Unserialize to zval. * @param[in] buf Buffer with serialized data. * @param[in] buf_len Buffer length. * @param[out] z Unserialized zval * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval *z); #endif /* IGBINARY_H */ igbinary-2.0.5/src/php7/php_igbinary.h0000644000175000017500000000455213177372464016751 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef PHP_IGBINARY_H #define PHP_IGBINARY_H #include "php.h" /** Module entry of igbinary. */ extern zend_module_entry igbinary_module_entry; #define phpext_igbinary_ptr &igbinary_module_entry #ifdef PHP_WIN32 #define PHP_IGBINARY_API __declspec(dllexport) #else #define PHP_IGBINARY_API #endif ZEND_BEGIN_MODULE_GLOBALS(igbinary) zend_bool compact_strings; ZEND_END_MODULE_GLOBALS(igbinary) #ifdef ZTS #include "TSRM.h" #endif #include "ext/standard/php_smart_string.h" /** Module init function. */ PHP_MINIT_FUNCTION(igbinary); /** Module shutdown function. */ PHP_MSHUTDOWN_FUNCTION(igbinary); /** Request init function. */ PHP_RINIT_FUNCTION(igbinary); /** Request shutdown function. */ PHP_RSHUTDOWN_FUNCTION(igbinary); /** Module info function for phpinfo(). */ PHP_MINFO_FUNCTION(igbinary); /** string igbinary_serialize(mixed value). * Returns the binary serialized value. */ PHP_FUNCTION(igbinary_serialize); /** mixed igbinary_unserialize(string data). * Unserializes the given inputstring (value). */ PHP_FUNCTION(igbinary_unserialize); #ifdef ZTS #define IGBINARY_G(v) TSRMG(igbinary_globals_id, zend_igbinary_globals *, v) #else #define IGBINARY_G(v) (igbinary_globals.v) #endif /** Backport macros from php 5.3 */ #ifndef Z_ISREF_P #define Z_ISREF_P(pz) PZVAL_IS_REF(pz) #endif #ifndef Z_ISREF_PP #define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) #endif #ifndef Z_SET_ISREF_TO_P #define Z_SET_ISREF_TO_P(pz, isref) (Z_ISREF_P(pz) = (isref)) #endif #ifndef Z_SET_ISREF_TO_PP #define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) #endif #ifndef Z_ADDREF_P #define Z_ADDREF_P(pz) ZVAL_ADDREF(pz) #endif #ifndef Z_ADDREF_PP #define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz)) #endif #endif /* PHP_IGBINARY_H */ /* * Local variables: * tab-width: 2 * c-basic-offset: 0 * End: * vim600: noet sw=2 ts=2 fdm=marker * vim<600: noet sw=2 ts=2 */ igbinary-2.0.5/src/php7/ig_win32.h0000644000175000017500000000105613177372464015713 0ustar tysontyson#ifndef _IG_WIN32_H #define _IG_WIN32_H #if PHP_WIN32 # include "win32/php_stdint.h" # if defined(_MSC_VER) && _MSC_VER >= 1800 # include # else # ifndef inline # define inline __inline # endif # ifndef __cplusplus # if !0 typedef enum { false = 0, true = 1 } _Bool; # define bool _Bool # endif # else typedef bool _Bool; # define bool _Bool # endif # define false 0 # define true 1 # define __bool_true_false_are_defined 1 # endif /* __MSC_VER */ #endif /* PHP_WIN32 */ #endif /* _IG_WIN32_H */ igbinary-2.0.5/igbinary.php0000644000175000017500000000641113177372464014771 0ustar tysontyson * @version 1.0.0 * @package igbinary */ /** Generates a storable representation of a value. * This is useful for storing or passing PHP values around without losing their type and structure. * To make the serialized string into a PHP value again, use {@link igbinary_unserialize}. * * igbinary_serialize() handles all types, except the resource-type. * You can even serialize() arrays that contain references to itself. * Circular references inside the array/object you are serialize()ing will also be stored. * * If object implements {@link http://www.php.net/~helly/php/ext/spl/interfaceSerializable.html Serializable} -interface, * PHP will call the member function serialize to get serialized representation of object. * * When serializing objects, PHP will attempt to call the member function __sleep prior to serialization. * This is to allow the object to do any last minute clean-up, etc. prior to being serialized. * Likewise, when the object is restored using unserialize() the __wakeup member function is called. * * @param mixed $value The value to be serialized. * @return string Returns a string containing a byte-stream representation of value that can be stored anywhere. * @link http://www.php.net/serialize PHP default serialize */ function igbinary_serialize($value); /** Creates a PHP value from a stored representation. * igbinary_unserialize() takes a single serialized variable and converts it back into a PHP value. * * If the variable being unserialized is an object, after successfully reconstructing the object * PHP will automatically attempt to call the __wakeup() member function (if it exists). * In case the passed string is not unserializeable, NULL is returned and E_WARNING is issued. * * @param string $str The serialized string. * @return mixed The converted value is returned, and can be a boolean, integer, float, string, array or object. * @link http://www.php.net/manual/en/function.unserialize.php PHP default unserialize * @link http://www.php.net/~helly/php/ext/spl/interfaceSerializable.html Serializable */ function igbinary_unserialize($str); ?> igbinary-2.0.5/igbinary.php.ini0000644000175000017500000000021013177372464015536 0ustar tysontyson[igbinary] extension=igbinary.so ; Enable or disable compacting of duplicate strings ; The default is On. ;igbinary.compact_strings=On igbinary-2.0.5/tags.sh0000755000175000017500000000032313177372464013745 0ustar tysontyson#!/bin/sh # # This generates the tags file for vim or emacs. (vim -t igbinary_serialize8) # # Try with "vim -t igbinary_serialize8" # find . -name "*.h" -o -name "*.c" | ctags-exuberant --language-force=c -L - igbinary-2.0.5/igbinary.spec0000644000175000017500000000310613177372464015132 0ustar tysontyson# Define version and release number %define version 1.0.2 %define release 1 Name: php-igbinary Version: %{version} Release: %{release}%{?dist} Packager: Mikko Koppanen Summary: PHP igbinary extension License: PHP Style License (http://opensource.dynamoid.com/#license) Group: Web/Applications URL: http://opensource.dynamoid.com/ # pear package creates .tgz file and the original source was .tar.gz Source: http://opensource.dynamoid.com/igbinary-%{version}.tgz Prefix: %{_prefix} Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: php-devel, make, gcc, /usr/bin/phpize %description Igbinary is a drop in replacement for the standard PHP serializer. Instead of time and space consuming textual representation, igbinary stores PHP data structures in a compact binary form. %prep %setup -q -n igbinary-%{version} %build /usr/bin/phpize && %configure && %{__make} %{?_smp_mflags} # Clean the buildroot so that it does not contain any stuff from previous builds [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} # Install the extension %{__make} install INSTALL_ROOT=%{buildroot} # Create the ini location %{__mkdir} -p %{buildroot}/etc/php.d # Preliminary extension ini echo "extension=igbinary.so" > %{buildroot}/%{_sysconfdir}/php.d/igbinary.ini %clean [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} %files %{_libdir}/php/modules/igbinary.so %{_sysconfdir}/php.d/igbinary.ini %{_includedir}/php/ext/igbinary/igbinary.h %changelog * Fri Oct 02 2009 Mikko Koppanen - Initial spec file igbinary-2.0.5/COPYING0000644000175000017500000000301713177372464013506 0ustar tysontysonCopyright (c) 2008 Sulake Dynamoid Oy, 2008-2014 Oleg Grenrus, Teddy Grenman, igbinary contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the 'igbinary' nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. igbinary-2.0.5/CREDITS0000644000175000017500000000103213177372464013466 0ustar tysontysonigbinary is written for IRC-Galleria by Sulake Dynamoid Oy and its employees. Author * Oleg Grenrus Contributors * Teddy Grenman (fixes, tests, docs) * Kari Lavikka (docs) * Pierre Joye (Windows support) * Mikko Koppanen (PECL and RPM spec files) * Tyson Andre (PHP7 compatibility fixes) Other * Original hash functions - Bob Jenkins --- http://irc-galleria.net/ - a finnish social networking site igbinary-2.0.5/README.md0000644000175000017500000001204713177372464013735 0ustar tysontysonigbinary ======== [![Build Status](https://travis-ci.org/igbinary/igbinary.svg?branch=master)](https://travis-ci.org/igbinary/igbinary) [![Build status (Windows)](https://ci.appveyor.com/api/projects/status/suhkkumj1yh9dgan?svg=true)](https://ci.appveyor.com/project/TysonAndre/igbinary-bemsx) Igbinary is a drop in replacement for the standard php serializer. Instead of time and space consuming textual representation, igbinary stores php data structures in compact binary form. Savings are significant when using memcached or similar memory based storages for serialized data. About 50% reduction in storage requirement can be expected. Specific number depends on your data. Unserialization performance is at least on par with the standard PHP serializer. Serialization performance depends on the "compact_strings" option which enables duplicate string tracking. String are inserted to a hash table which adds some overhead. In usual scenarios this does not have much significance since usage pattern is "serialize rarely, unserialize often". With "compact_strings" option igbinary is usually a bit slower than the standard serializer. Without it, a bit faster. Features -------- - Supports same data types as the standard PHP serializer: null, bool, int, float, string, array and objects. - `__autoload` & `unserialize_callback_func` - `__sleep` & `__wakeup` - Serializable -interface - Data portability between platforms (32/64bit, endianess) - Tested on Linux amd64, Linux ARM, Mac OSX x86, HP-UX PA-RISC and NetBSD sparc64 - Hooks up to APC opcode cache as a serialization handler (APC 3.1.7+) (Hooks up to the substitute APCu for recent php releases) - Compatible with PHP 5.2 – 5.6, 7.0 – 7.2 Implementation details ---------------------- Storing complex PHP data structures like arrays of associative arrays with the standard PHP serializer is not very space efficient. The main reasons in order of significance are (at least in our applications): 1. Array keys are repeated redundantly. 2. Numerical values are plain text. 3. Human readability adds some overhead. Igbinary uses two specific strategies to minimize the size of the serialized output. 1. Repetitive strings are stored only once. Collections of objects benefit significantly from this. See "compact_strings" option. 2. Numerical values are stored in the smallest primitive data type available: *123* = `int8_t`, *1234* = `int16_t`, *123456* = `int32_t` ... and so on. 3. ( Well, it is not human readable ;) How to use ---------- Add the following lines to your php.ini: ; Load igbinary extension extension=igbinary.so ; Use igbinary as session serializer session.serialize_handler=igbinary ; Enable or disable compacting of duplicate strings ; The default is On. igbinary.compact_strings=On ; Use igbinary as serializer in APC cache (3.1.7 or later) ;apc.serializer=igbinary .. and in your php code replace serialize and unserialize function calls with `igbinary_serialize` and `igbinary_unserialize`. Installing ---------- Note: Sometimes phpize must be substituted with phpize5. In such cases the following option must be given to configure script: "--with-php-config=.../php-config5" 1. `phpize` 2. `./configure: - With GCC: `./configure CFLAGS="-O2 -g" --enable-igbinary` - With ICC (Intel C Compiler) `./configure CFLAGS=" -no-prec-div -O3 -xO -unroll2 -g" CC=icc --enable-igbinary` - With clang: `./configure CC=clang CFLAGS="-O0 -g" --enable-igbinary` 3. `make` 4. `make test` 5. `make install` 6. igbinary.so is installed to the default extension directory ### To run APCu test ``` # go to modules directory cd modules # ... and create symlink to apcu extension # it will be loaded during test suite /opt/lib/php/extensions/no-debug-non-zts-20121212/apcu.so ``` Similar approach should work for APC. ### Installing on Windows If you are a contributor to/packager of igbinary, see [WINDOWS.md](./WINDOWS.md) Bugs & Contributions -------------------- Mailing list for bug reports and other development discussion can be found at http://groups.google.com/group/igbinary Fill bug reports at https://github.com/igbinary/igbinary/issues The preferred ways for contributions are pull requests and email patches (in git format). Feel free to fork at http://github.com/igbinary/igbinary Utilizing in other extensions ----------------------------- Igbinary can be called from other extensions fairly easily. Igbinary installs its header file to _ext/igbinary/igbinary.h_. There are just two straighforward functions: `igbinary_serialize` and `igbinary_unserialize`. Look at _igbinary.h_ for prototypes and usage. Add `PHP_ADD_EXTENSION_DEP(yourextension, igbinary)` to your _config.m4_ in case someone wants to compile both of them statically into php. Trivia ------ Where does the name "igbinary" come from? There was once a similar project called fbinary but it has disappeared from the Internet a long time ago. Its architecture wasn't particularly clean either. IG is an abbreviation for a finnish social networking site IRC-Galleria (http://irc-galleria.net/) igbinary-2.0.5/NEWS0000644000175000017500000000562313177372464013157 0ustar tysontyson2.0.5 2017-11-04 ======== * Same as 2.0.5RC1 (no bugs were reported in that release candidate) 2.0.5RC1 2017-10-15 ======== * Improve performance when unserializing objects/arrays and serializing objects/arrays/strings in php 5/7. * Update unserialization of integer object keys for php 7.2: Make those keys accessible when unserializing. * Properly pick up presence of gcc for default compiler flags (`cc --version` doesn't contain gcc). Add -O2 to default gcc compiler flags. * Use empty string for serializing empty $_SESSION array, similar to "session.serialize_handler=php". Older igbinary releases already unserialize the empty string to the empty array. 2.0.4 2017-04-14 ======== * Fixes bug #129: Should not call __wakeup() on data which was created by Serializable::unserialize() 2.0.3 2017-03-31 ======== * Fixes bug #126: Fatal error: "igbinary_serialize_zval: zval has unknown type 0" (IS_UNDEF) Make this a warning instead of a fatal error (and serialize as null instead), since IS_UNDEF is a known type. Later releases will fix the root cause of the warning, and consistently omit array/object/other entries for IS_UNDEF. 2.0.2 2017-02-27 ======== * Compatible with PHP 5.2 - 7.1 * Fixes crash in Memcached->setMulti (in php 7.0+) when the first level of array elements have references as values. Other extensions using igbinary shouldn't be affected. 2.0.1 2016-11-19 ======== * Compatible with PHP 5.2 - 7.0 * Fixes bug in session decoder not calling __wakeup() in php 7.0+ * (Enhancement) Reuses identical strings when unserializing objects and arrays in php 7.0+ 2.0.0 2016-10-30 ======== * Compatible with PHP 5.2 - 7.0 (Adds PHP 7 support) * Serialization format is unchanged * Performance improvements for serialization and unserialization in PHP 5.2 - 7.0 * (PHP 7) Don't call __destruct if __wakeup threw an exception (or __wakeup wasn't called yet) * Ports integration with other extensions to PHP 7.0 (session serialization, Memcached, Redis, APCu, etc.) * Fixes Windows PECL builds for PHP 5.6+ * Reword warnings for invalid header bytes of serialized data (in igbinary_unserialize). 1.2.1 2014-08-29 ======== * Compatible with PHP 5.2 - 5.6 1.2.0 2014-08-28 ======== * PECL bug #22614, igbinary_unserialize(FALSE) must return FALSE * PHP bug #54662, unserializing nested objects cause crash * Other fixes 1.1.1 2011-01-17 ======== * Critical crash fix. Thanks to Ilia Alshanetsky for spotting and fixing. 1.1.0 2011-01-17 ======== * New ini setting to disable duplicate string compacting * APC serializer registration (APC 3.1.7 beta) * Windows support (PHP 5.3) * Updated serialized binary format (1.1 reads 1.0.x format) * Minor performance improvements * Bug fixes * New source repository at https://github.com/igbinary/igbinary 1.0.2 ======== * Bug fix release 1.0.1 2008-07-05 ======== * unserialize_callback_func support * slight speedup when serializing scalars 1.0.0 2008-06-25 ======== * Public version