]> granicus.if.org Git - php/commitdiff
Add spl extension
authorMarcus Boerger <helly@php.net>
Thu, 1 May 2003 23:28:28 +0000 (23:28 +0000)
committerMarcus Boerger <helly@php.net>
Thu, 1 May 2003 23:28:28 +0000 (23:28 +0000)
24 files changed:
ext/spl/CREDITS [new file with mode: 0755]
ext/spl/EXPERIMENTAL [new file with mode: 0755]
ext/spl/TODO [new file with mode: 0755]
ext/spl/config.m4 [new file with mode: 0755]
ext/spl/examples/dba_dump.php [new file with mode: 0755]
ext/spl/php_spl.c [new file with mode: 0755]
ext/spl/php_spl.h [new file with mode: 0755]
ext/spl/spl.php [new file with mode: 0755]
ext/spl/spl_array.c [new file with mode: 0755]
ext/spl/spl_array.h [new file with mode: 0755]
ext/spl/spl_engine.c [new file with mode: 0755]
ext/spl/spl_engine.h [new file with mode: 0755]
ext/spl/spl_foreach.c [new file with mode: 0755]
ext/spl/spl_foreach.h [new file with mode: 0755]
ext/spl/spl_functions.c [new file with mode: 0755]
ext/spl/spl_functions.h [new file with mode: 0755]
ext/spl/tests/.htaccess [new file with mode: 0755]
ext/spl/tests/array_access_001.phpt [new file with mode: 0755]
ext/spl/tests/array_access_002.phpt [new file with mode: 0755]
ext/spl/tests/array_access_ex.phpt [new file with mode: 0755]
ext/spl/tests/array_read.phpt [new file with mode: 0755]
ext/spl/tests/foreach.phpt [new file with mode: 0755]
ext/spl/tests/forward.phpt [new file with mode: 0755]
ext/spl/tests/sequence.phpt [new file with mode: 0755]

diff --git a/ext/spl/CREDITS b/ext/spl/CREDITS
new file mode 100755 (executable)
index 0000000..8710aac
--- /dev/null
@@ -0,0 +1,2 @@
+SPL
+Marcus Boerger
diff --git a/ext/spl/EXPERIMENTAL b/ext/spl/EXPERIMENTAL
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/ext/spl/TODO b/ext/spl/TODO
new file mode 100755 (executable)
index 0000000..5311ef6
--- /dev/null
@@ -0,0 +1,13 @@
+This is the ToDo of ext/spl:
+
+- spl::array_access cals set() which is supposed to return a value.
+  Currently you *must* return a value even when it is not used.
+    $obj[$idx] = $val;      // doesn't use the return value
+    $x = $obj[$idx] = $val; // here it is used
+  Since array_access.phpt is a test with a return value there
+  should be a test without a return value. Maybe an error message
+  is required in case there is no return value.
+
+- spl::array_access_ex is not completely done and not tested.
+
+If you have further questions: mailto:helly@php.net
diff --git a/ext/spl/config.m4 b/ext/spl/config.m4
new file mode 100755 (executable)
index 0000000..bcae05f
--- /dev/null
@@ -0,0 +1,41 @@
+dnl $Id$
+dnl config.m4 for extension SPL
+
+PHP_ARG_ENABLE(spl, enable SPL suppport,
+[  --enable-spl            Enable Standard PHP Library])
+
+dnl first enable/disable all hooks
+
+PHP_ARG_ENABLE(spl, enable all hooks,
+[  --enable-spl-hook-all       SPL: Enable all hooks])
+
+dnl now all single enable/disable for hooks
+
+PHP_ARG_ENABLE(spl, enable hook on foreach,
+[  --disable-spl-foreach       SPL: Disable hook on forach], yes)
+
+PHP_ARG_ENABLE(spl, enable hook on array read,
+[  --enable-spl-array-read     SPL: Enable hook on array read])
+
+PHP_ARG_ENABLE(spl, enable hook on array write,
+[  --enable-spl-array-write    SPL: Enable hook on array write (+read)])
+
+dnl last do checks on hooks
+
+if test "$PHP_SPL_HOOK_ALL" != "no" -o "$PHP_SPL_FOREACH" != "no"; then
+  AC_DEFINE(SPL_FOREACH, 1, [Activate opcode hook on foreach])
+  PHP_SPL="yes"
+fi
+if test "$PHP_SPL_HOOK_ALL" != "no" -o "$PHP_SPL_ARRAY_READ" != "no" -o "$PHP_SPL_ARRAY_WRITE" != "no"; then
+  AC_DEFINE(SPL_ARRAY_READ, 1, [Activate opcode hook on array read])
+  PHP_SPL="yes"
+fi
+if test "$PHP_SPL_HOOK_ALL" != "no" -o "$PHP_SPL_ARRAY_WRITE" != "no"; then
+  AC_DEFINE(SPL_ARRAY_WRITE, 1, [Activate opcode hook on array write])
+  PHP_SPL="yes"
+fi
+
+if test "$PHP_SPL" != "no"; then
+    AC_DEFINE(HAVE_SPL, 1, [Whether you want SPL (Standard Php Library) support]) 
+    PHP_NEW_EXTENSION(spl, php_spl.c spl_functions.c spl_engine.c spl_foreach.c spl_array.c, $ext_shared)
+fi
diff --git a/ext/spl/examples/dba_dump.php b/ext/spl/examples/dba_dump.php
new file mode 100755 (executable)
index 0000000..d32f176
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/* dba dump utility
+ *
+ * Usage php dba_dump <file> <handler>
+ *
+ * Note: configure with --enable-dba 
+ */
+
+class dba_reader implements spl::iterator {
+
+       public $db = NULL;
+
+       function __construct($file, $handler) {
+               $this->db = dba_open($file, 'r', $handler);
+       }
+       
+       function new_iterator() {
+               return new dba_iter($this);
+       }
+       
+       function __destruct() {
+               if ($this->db) {
+                       dba_close($this->db);
+               }
+       }
+}
+
+class dba_iter implements spl::sequence_assoc {
+
+       private $obj;
+       private $key = NULL;
+       private $val = NULL;
+
+       function __construct($obj) {
+               $this->obj = $obj;
+       }
+
+       function reset() {
+               if ($this->obj->db) {
+                       $this->key = dba_firstkey($this->obj->db);
+               }
+       }
+
+       function elem() {
+               return $this->val;
+       }
+
+       function next() {
+               $this->key = dba_nextkey($this->obj->db);
+       }
+
+       function more() {
+               if ($this->obj->db && $this->key !== false) {
+                       $this->val = dba_fetch($this->key, $this->obj->db);
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       function key() {
+               return $this->key;
+       }
+}
+
+$db = new dba_reader($argv[1], $argv[2]);
+foreach($db as $key => $val) {
+       echo "'$key' => '$val'\n";
+}
+
+?>
\ No newline at end of file
diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c
new file mode 100755 (executable)
index 0000000..b97c44f
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_spl.h"
+#include "spl_functions.h"
+#include "spl_engine.h"
+#include "spl_foreach.h"
+#include "spl_array.h"
+
+#ifdef COMPILE_DL_SPL
+ZEND_GET_MODULE(spl)
+#endif
+
+ZEND_DECLARE_MODULE_GLOBALS(spl)
+
+/* {{{ spl_functions
+ */
+function_entry spl_functions[] = {
+       PHP_FE(spl_classes,             NULL)
+       PHP_FE(class_name,              NULL)
+       PHP_FE(class_parents,           NULL)
+       PHP_FE(class_implements,        NULL)
+       {NULL, NULL, NULL}
+};
+/* }}} */
+
+/* {{{ spl_module_entry
+ */
+zend_module_entry spl_module_entry = {
+       STANDARD_MODULE_HEADER,
+       "spl",
+       spl_functions,
+       PHP_MINIT(spl),
+       PHP_MSHUTDOWN(spl),
+       PHP_RINIT(spl),
+       PHP_RSHUTDOWN(spl),
+       PHP_MINFO(spl),
+       "0.1",
+       STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+zend_namespace   *spl_ns_spl;
+zend_class_entry *spl_ce_iterator;
+zend_class_entry *spl_ce_forward;
+zend_class_entry *spl_ce_assoc;
+zend_class_entry *spl_ce_sequence;
+zend_class_entry *spl_ce_forward_assoc;
+zend_class_entry *spl_ce_sequence_assoc;
+zend_class_entry *spl_ce_array_read;
+zend_class_entry *spl_ce_array_access;
+zend_class_entry *spl_ce_array_access_ex;
+zend_class_entry *spl_ce_array_writer;
+#ifdef SPL_ARRAY_WRITE
+zend_class_entry *spl_ce_array_writer_default;
+#endif /* SPL_ARRAY_WRITE */
+
+/* {{{ spl_functions_none
+ */
+function_entry spl_functions_none[] = {
+       {NULL, NULL, NULL}
+};
+/* }}} */
+
+static unsigned char first_of_two_force_ref[] = { 2, BYREF_FORCE, BYREF_NONE };
+
+/* {{{ spl_array_writer_funcs
+ */
+function_entry spl_array_writer_funcs[] = {
+       SPL_CLASS_FE(array_writer_default,   __construct,   first_of_two_force_ref)
+       SPL_CLASS_FE(array_writer_default,   set,           NULL)
+       {NULL, NULL, NULL}
+};
+/* }}} */
+
+/* {{{ spl_init_globals
+ */
+static void spl_init_globals(zend_spl_globals *spl_globals)
+{
+#ifdef SPL_FOREACH
+       ZEND_EXECUTE_HOOK(ZEND_FE_RESET);
+       ZEND_EXECUTE_HOOK(ZEND_FE_FETCH);
+#endif
+
+#if defined(SPL_ARRAY_READ) | defined(SPl_ARRAY_WRITE)
+       ZEND_EXECUTE_HOOK(ZEND_FETCH_DIM_R);
+       ZEND_EXECUTE_HOOK(ZEND_FETCH_DIM_W);
+       ZEND_EXECUTE_HOOK(ZEND_FETCH_DIM_RW);
+#endif
+
+#ifdef SPL_ARRAY_WRITE
+       ZEND_EXECUTE_HOOK(ZEND_ASSIGN);
+#endif /* SPL_ARRAY_WRITE */
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION(spl)
+ */
+PHP_MINIT_FUNCTION(spl)
+{
+       ZEND_INIT_MODULE_GLOBALS(spl, spl_init_globals, NULL);
+
+       REGISTER_SPL_NAMESPACE(spl);
+
+       REGISTER_SPL_INTERFACE(spl, iterator);
+       REGISTER_SPL_INTF_FUNC(spl, iterator, new_iterator);
+
+       REGISTER_SPL_INTERFACE(spl, forward);
+       REGISTER_SPL_INTF_FUNC(spl, forward, current);
+       REGISTER_SPL_INTF_FUNC(spl, forward, next);
+       REGISTER_SPL_INTF_FUNC(spl, forward, more);
+
+       REGISTER_SPL_INTERFACE(spl, sequence);
+       REGISTER_SPL_INTF_FUNC(spl, sequence, rewind);
+       REGISTER_SPL_PARENT_CE(spl, sequence, forward);
+
+       REGISTER_SPL_INTERFACE(spl, assoc);
+       REGISTER_SPL_INTF_FUNC(spl, assoc, key);
+
+       REGISTER_SPL_INTERFACE(spl, forward_assoc);
+       REGISTER_SPL_PARENT_CE(spl, forward_assoc, forward);
+       REGISTER_SPL_IMPLEMENT(spl, forward_assoc, assoc);
+
+       REGISTER_SPL_INTERFACE(spl, sequence_assoc);
+       REGISTER_SPL_PARENT_CE(spl, sequence_assoc, sequence);
+       REGISTER_SPL_IMPLEMENT(spl, sequence_assoc, forward_assoc);
+
+       REGISTER_SPL_INTERFACE(spl, array_read);
+       REGISTER_SPL_INTF_FUNC(spl, array_read, get);
+       REGISTER_SPL_INTF_FUNC(spl, array_read, exists);
+
+       REGISTER_SPL_INTERFACE(spl, array_access);
+       REGISTER_SPL_PARENT_CE(spl, array_access, array_read);
+       REGISTER_SPL_INTF_FUNC(spl, array_access, set);
+
+       REGISTER_SPL_INTERFACE(spl, array_access_ex);
+       REGISTER_SPL_PARENT_CE(spl, array_access_ex, array_access);
+       REGISTER_SPL_INTF_FUNC(spl, array_access_ex, new_writer);
+
+       REGISTER_SPL_INTERFACE(spl, array_writer);
+       REGISTER_SPL_INTF_FUNC(spl, array_writer, set); 
+
+#ifdef SPL_ARRAY_WRITE
+       REGISTER_SPL_STD_CLASS(spl, array_writer_default, spl_array_writer_default_create);
+       REGISTER_SPL_FUNCTIONS(spl, array_writer_default, spl_array_writer_funcs);
+#endif
+       return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RINIT_FUNCTION(spl)
+ */
+PHP_RINIT_FUNCTION(spl)
+{
+       return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION(spl)
+ */
+PHP_RSHUTDOWN_FUNCTION(spl)
+{                      
+       return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION(spl)
+ */
+PHP_MSHUTDOWN_FUNCTION(spl)
+{                      
+       SPL_DEBUG(fprintf(stderr, "%s\n", "Shutting down SPL");)
+
+#ifdef SPL_FOREACH
+       ZEND_EXECUTE_HOOK_RESTORE(ZEND_FE_RESET);
+       ZEND_EXECUTE_HOOK_RESTORE(ZEND_FE_FETCH);
+#endif
+
+#if defined(SPL_ARRAY_READ) | defined(SPL_ARRAY_WRITE)
+       ZEND_EXECUTE_HOOK_RESTORE(ZEND_FETCH_DIM_R);
+       ZEND_EXECUTE_HOOK_RESTORE(ZEND_FETCH_DIM_W);
+       ZEND_EXECUTE_HOOK_RESTORE(ZEND_FETCH_DIM_RW); 
+#endif
+
+#ifdef SPL_ARRAY_WRITE
+       ZEND_EXECUTE_HOOK_RESTORE(ZEND_ASSIGN);
+#endif /* SPL_ARRAY_WRITE */
+       return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO(spl)
+ */
+PHP_MINFO_FUNCTION(spl)
+{
+#ifdef SPL_FOREACH
+       char *foreach = "beta";
+#else /* SPL_ARRAY_WRITE */
+       char *foreach = "beta, not hooked";
+#endif
+#ifdef SPL_ARRAY_READ
+       char *array_read = "beta";
+#else /* SPL_ARRAY_WRITE */
+       char *array_read = "beta, not hooked";
+#endif
+#ifdef SPL_ARRAY_WRITE
+       char *array_write = "beta";
+#else /* SPL_ARRAY_WRITE */
+       char *array_write = "beta, not hooked";
+#endif /* SPL_ARRAY_WRITE */
+
+       php_info_print_table_start();
+       php_info_print_table_header(2, "SPL support",        "enabled");
+       php_info_print_table_row(2,    "iterator",           foreach);
+       php_info_print_table_row(2,    "forward",            foreach);
+       php_info_print_table_row(2,    "sequence",           foreach);
+       php_info_print_table_row(2,    "assoc",              foreach);
+       php_info_print_table_row(2,    "forward_assoc",      foreach);
+       php_info_print_table_row(2,    "sequence_assoc",     foreach);
+       php_info_print_table_row(2,    "array_read",         array_read);
+       php_info_print_table_row(2,    "array_access",       array_write);
+       php_info_print_table_row(2,    "array_access_ex",    array_write);
+       php_info_print_table_row(2,    "array_writer",       array_write);
+       php_info_print_table_end();
+}
+/* }}} */
+
+/* {{{ proto string class_name(object)
+       Retrieve  */
+PHP_FUNCTION(class_name)
+{
+       zval *obj;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+               RETURN_FALSE;
+       }
+       RETURN_STRING(spl_make_fully_qualyfied_name(Z_OBJCE_P(obj) TSRMLS_CC), 0);
+}
+/* }}} */
+
+/* {{{ class_parents
+ */
+PHP_FUNCTION(class_parents)
+{
+       zval *obj;
+       zend_class_entry *parent_class;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+               RETURN_FALSE;
+       }
+       array_init(return_value);
+       parent_class = Z_OBJCE_P(obj)->parent;
+       while (parent_class) {
+               spl_add_class_name(return_value, parent_class TSRMLS_CC);
+               parent_class = parent_class->parent;
+       }
+}
+/* }}} */
+
+/* {{{ class_implements
+ */
+PHP_FUNCTION(class_implements)
+{
+       zval *obj;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+               RETURN_FALSE;
+       }
+       array_init(return_value);
+       spl_add_interfaces(return_value, Z_OBJCE_P(obj) TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_classes */
+PHP_FUNCTION(spl_classes)
+{
+       array_init(return_value);
+       zend_hash_apply_with_argument(&spl_ns_spl->class_table, (apply_func_arg_t)spl_add_classes, return_value TSRMLS_CC);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/php_spl.h b/ext/spl/php_spl.h
new file mode 100755 (executable)
index 0000000..137e451
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_SPL_H
+#define PHP_SPL_H
+
+#include "php.h"
+#include <stdarg.h>
+
+#if 0
+#define SPL_DEBUG(x)   x
+#else
+#define SPL_DEBUG(x)
+#endif
+
+extern zend_module_entry spl_module_entry;
+#define phpext_spl_ptr &spl_module_entry
+
+#if defined(PHP_WIN32) && !defined(COMPILE_DL_SPL)
+#undef phpext_spl
+#define phpext_spl NULL
+#endif
+
+PHP_MINIT_FUNCTION(spl);
+PHP_MSHUTDOWN_FUNCTION(spl);
+PHP_RINIT_FUNCTION(spl);
+PHP_RSHUTDOWN_FUNCTION(spl);
+PHP_MINFO_FUNCTION(spl);
+
+#define ZEND_EXECUTE_HOOK_PTR(name) \
+       opcode_handler_t handler_ ## name
+
+#define ZEND_EXECUTE_HOOK(name) \
+       spl_globals->handler_ ## name = zend_opcode_handlers[name]; \
+       zend_opcode_handlers[name] = spl_handler_ ## name
+
+#define ZEND_EXECUTE_HOOK_RESTORE(name) \
+       zend_opcode_handlers[name] = SPL_G(handler_ ## name)
+
+#define ZEND_EXECUTE_HOOK_ORIGINAL(name) \
+       return SPL_G(handler_ ## name)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
+
+#define ZEND_EXECUTE_HOOK_FUNCTION(name) \
+       int spl_handler_ ## name(ZEND_OPCODE_HANDLER_ARGS)
+
+
+ZEND_BEGIN_MODULE_GLOBALS(spl)
+#ifdef SPL_FOREACH
+       ZEND_EXECUTE_HOOK_PTR(ZEND_FE_RESET);
+       ZEND_EXECUTE_HOOK_PTR(ZEND_FE_FETCH);
+#endif
+#if defined(SPL_ARRAY_READ) | defined(SPL_ARRAY_WRITE)
+       ZEND_EXECUTE_HOOK_PTR(ZEND_FETCH_DIM_R);
+       ZEND_EXECUTE_HOOK_PTR(ZEND_FETCH_DIM_W);
+       ZEND_EXECUTE_HOOK_PTR(ZEND_FETCH_DIM_RW);
+#endif
+#ifdef SPL_ARRAY_WRITE
+       ZEND_EXECUTE_HOOK_PTR(ZEND_ASSIGN);
+#endif
+ZEND_END_MODULE_GLOBALS(spl)
+
+#ifdef ZTS
+# define SPL_G(v) TSRMG(spl_globals_id, zend_spl_globals *, v)
+extern int spl_globals_id;
+#else
+# define SPL_G(v) (spl_globals.v)
+extern zend_spl_globals spl_globals;
+#endif
+
+extern zend_namespace   *spl_ns_spl;
+extern zend_class_entry *spl_ce_iterator;
+extern zend_class_entry *spl_ce_forward;
+extern zend_class_entry *spl_ce_sequence;
+extern zend_class_entry *spl_ce_assoc;
+extern zend_class_entry *spl_ce_forward_assoc;
+extern zend_class_entry *spl_ce_sequence_assoc;
+extern zend_class_entry *spl_ce_array_read;
+extern zend_class_entry *spl_ce_array_access;
+extern zend_class_entry *spl_ce_array_access_ex;
+extern zend_class_entry *spl_ce_array_writer;
+#ifdef SPL_ARRAY_WRITE
+extern zend_class_entry *spl_ce_array_writer_default;
+#endif /* SPL_ARRAY_WRITE */
+
+PHP_FUNCTION(spl_classes);
+PHP_FUNCTION(class_name);
+PHP_FUNCTION(class_parents);
+PHP_FUNCTION(class_implements);
+
+#endif /* PHP_SPL_H */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl.php b/ext/spl/spl.php
new file mode 100755 (executable)
index 0000000..ee92c05
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+
+/* \brief Standard PHP Library
+ *
+ * (c) M.Boerger 2003
+ */
+namespace spl {
+
+       /*! \brief Interface to foreach() construct
+        *
+        * Any class that implements this interface can for example be used as 
+        * the input parameter to foreach() calls which would normally be an 
+        * array.
+        *
+        * The only thing a class has to do is 
+        */
+       interface iterator {
+               
+               /*! \brief Create a new iterator
+                *
+                * used for example in foreach() operator.
+                */
+               function new_iterator();
+       }
+
+       /*! \brief Simple forward iterator
+        *
+        * Any class that implements this interface can be used as the
+        * return of a foreach interface. And hence the class itself
+        * can be used as a parameter to be iterated (normally an array).
+        *
+        * \code
+               class c implements spl::foreach, spl::forward {
+                       private $num = 0;
+                       function new_iterator() {
+                               $this->num = 0;
+                               return $this;
+                       }
+                       function current() {
+                               return $this->num;
+                       }
+                       function next() {
+                               $this->num++;
+                       }
+                       function has_more() {
+                               return $this->num < 5;
+                       }
+               }
+         
+               $t = new c();
+
+               foreach($t as $num) {
+                       echo "$num\n";
+               }
+          \endcode
+        *
+        * A very interesting usage scenario are for example database queries.
+        * Without this interface you need to do it without foreach or fetch the
+        * whole rowset into an array.
+        *
+        * In the above code the class implements both the foreach and the
+        * forward interface. Doing this you cannot have nested foreach calls.
+        * If you need this you must split the two parts.
+        *
+        * \code
+               class c implements spl::foreach {
+                       public $max = 3;
+                       function new_iterator() {
+                               return new c_iter($this);
+                       }
+               }
+               class c_iter implements spl::forward {
+                       private $obj;
+                       private $num = 0;
+                       function __construct($obj) {
+                               $this->obj = $obj;
+                       }
+                       function current() {
+                               return $this->num;
+                       }
+                       function next() {
+                               $this->num++;
+                       }
+                       function has_more() {
+                               return $this->num < $this->obj->max;
+                       }
+               }
+         
+               $t = new c();
+
+               foreach($t as $outer) {
+                       foreach($t as $inner) {
+                               echo "$outer,$inner\n";
+                       }
+               }
+          \endcode
+        *
+        * You can also use this interface with the for() construct.
+        *
+        * \code
+               class c implements spl::foreach {
+                       public $max = 3;
+                       function new_iterator() {
+                               return new c_iter($this);
+                       }
+               }
+               class c_iter implements spl::forward {
+                       private $obj;
+                       private $num = 0;
+                       function __construct($obj) {
+                               $this->obj = $obj;
+                       }
+                       function current() {
+                               return $this->num;
+                       }
+                       function next() {
+                               $this->num++;
+                       }
+                       function has_more() {
+                               return $this->num < $this->obj->max;
+                       }
+               }
+
+               $t = new c();
+
+               for ($iter = $t->new_iterator(); $iter->has_more(); $iter->next()) {
+                       echo $iter->current() . "\n";
+               }
+          \endcode
+        */
+       interface forward {
+               
+               /*! \brief Retrieve the current currentent
+                *
+                * \return \c mixed current element or \c false if no more elements
+                */
+               function current();
+
+               /*! \brief Forward to next element.
+                */
+               function next();
+
+               /*! \brief Check if more elements are available.
+                *
+                * \return \c bool whether or not more elements are available
+                */
+               function has_more();
+       }
+
+       /*! \brief A restartable iterator.
+        *
+        * This iterator allows you to implement a restartable iterator. That
+        * means the iterator can be rewind to the first element after accessing
+        * any number of elements.
+        *
+        * \note If you use sequence in foreach then rewind() will be called
+        *       first.
+        */
+       interface sequence extends forward {
+
+               /*! Restart the sequence by positioning it to the first element.
+                */
+               function rewind();
+       }
+       
+       /*! \brief associative interface
+        *
+        * This interface allows to implement associative iterators
+        * and containers.
+        */
+       interface assoc {
+               
+               /*! \brief Retrieve the current elements key
+                *
+                * \return \c mixed current key or \c false if no more elements
+                */
+               function key();
+       }
+
+       /*! \brief associative foreach() interface
+        *
+        * This interface extends the forward interface to support keys.
+        * With this interface you can do:
+        * \code
+               $t = new c();
+               foreach($t as $key => $elem).
+          \endcode
+        */     
+       interface assoc_forward extends forward implements assoc {
+       }
+
+       /*! \brief associative sequence
+        */
+       interface assoc_sequence extends sequence implements assoc {
+       }
+       
+       /*! \brief array read only access for objects
+        */
+       interface array_read {
+               
+               /*! Check whether or not the given index exists.
+                * The returned value is interpreted as converted to bool.
+                */
+               function exists($index);
+
+               /*! Read the value at position $index.
+                * This function is only beeing called if exists() returns true.
+                */
+               function get($index);
+       }
+       
+       /*! \brief array read/write access for objects.
+        *
+        * The following example shows how to use an array_writer:
+        * \code 
+               class array_emulation implemets spl::array_access {
+                       private $ar = array();
+                       function exists($index) {
+                               return array_key_exists($index, $this->ar);
+                       }
+                       function get($index) {
+                               return $this->ar[$index];
+                       }
+                       function set($index, $value) {
+                               $this->ar[$index] = $value;
+                       }
+               }
+          \endcode
+        */
+       interface array_access extends array_read {
+
+               /*! Set the value identified by $index to $value.
+                */
+               function set($value, $index);
+       }
+
+       /*! \brief array read/write access with customized array_writer
+        *
+        * The internal structure requires that write access via interfaces
+        * is divided into two parts. First the index is used to create an
+        * array_writer which will later receive the new value and calls the
+        * containers set() method with appropriate parameters.
+        *
+        * Sometimes it is helpfull to overwrite this behavior and have your
+        * own implementation for the array_writer.
+        *
+        * The following example shows how to use a customized array_writer:
+        * \code 
+               class array_emulation_ex extends array_emulation implemets spl::array_access_ex {
+                       private $last_index = NULL;
+                       function new_writer($index) {
+                               $last_index = $index;
+                               return new array_write(&$this, $index);
+                       }
+               }
+          \endcode
+        */
+       interface array_access_ex extends array_access {
+
+               /*! Create an array_writer interface for the specified index.
+                *
+                * If your container uses array_access instead of array_access_ex
+                * the following code would be equal to the internal new_writer()
+                * method:
+                  \code
+                       function new_writer($index) {
+                               return new array_write(&$this, $index);
+                       }
+                  \endcode
+                */
+               function new_writer($index);
+       }
+
+       /*! \brief array writer interface
+        *
+        * for every write access to an array_access instance an array_writer
+        * is created which receives the originating object and the index as
+        * parameters for the constructor call.
+        *
+        * The following shows the equivalent php code for the default 
+        * implementation array_write.
+        * \code 
+               class array_write implements array_writer {
+                       private $obj;
+                       private $idx;
+                       function __construct(&$obj, $index = null) {
+                               $this->obj = $obj;
+                               $this->idx = $index;
+                       }
+                       function set($value) {
+                               return $this->obj->set($this->idx, $value);
+                       }
+               }
+          \endcode
+        *
+        * See array_access for more.
+        */
+       interface array_writer {
+
+               /*! Set the corresponding value to $value.
+                */
+               function set($value);
+       }
+
+}
+?>
\ No newline at end of file
diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c
new file mode 100755 (executable)
index 0000000..7c68d3c
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "zend_compile.h"
+#include "zend_execute_locks.h"
+
+#include "php_spl.h"
+#include "spl_functions.h"
+#include "spl_engine.h"
+#include "spl_array.h"
+
+#define DELETE_ZVAL(z) \
+       if ((z)->refcount < 2) { \
+               zval_dtor(z); \
+               FREE_ZVAL(z); /* maybe safe_free_zval_ptr is needed for the uninitialised things */ \
+       }
+
+#define DELETE_RET_ZVAL(z) \
+       if ((z)->refcount < 3) { \
+               zval_dtor(z); \
+               FREE_ZVAL(z); /* maybe safe_free_zval_ptr is needed for the uninitialised things */ \
+       }
+
+#define AI_PTR_2_PTR_PTR(ai) \
+       (ai).ptr_ptr = &((ai).ptr)
+
+/* {{{ spl_array_writer_default stuff */
+typedef struct {
+       zval *obj;
+       zval *idx;
+} spl_array_writer_object;
+
+static zend_class_entry *spl_array_writer_default_get_class(zval *object TSRMLS_DC)
+{
+#ifdef SPL_ARRAY_WRITE
+       return spl_ce_array_writer_default;
+#else
+       return (zend_class_entry *)1; /* force an error here: this ensures not equal */
+#endif
+}
+
+static zend_object_handlers spl_array_writer_default_handlers = {
+       ZEND_OBJECTS_STORE_HANDLERS,
+       
+       NULL,     /* read_property */
+       NULL,     /* write_property */
+       NULL,     /* get_property_ptr */
+       NULL,     /* get_property_zval_ptr */
+       NULL,     /* get */
+       NULL,     /* set */
+       NULL,     /* has_property */
+       NULL,     /* unset_property */
+       NULL,     /* get_properties */
+       NULL,     /* get_method */
+       NULL,     /* call_method */
+       NULL,     /* get_constructor */
+       spl_array_writer_default_get_class,     /* get_class_entry */
+       NULL,     /* get_class_name */
+       NULL      /* compare_objects */
+};
+/* }}} */
+
+/* {{{ spl_array_writer_dtor */
+void spl_array_writer_default_dtor(void *object, zend_object_handle handle TSRMLS_DC)
+{
+       spl_array_writer_object *writer = (spl_array_writer_object*) object;
+
+       if (writer->obj)
+       {
+               writer->obj->refcount--;
+/*             DELETE_ZVAL(writer->obj); */
+       }
+       if (writer->idx)
+       {
+               writer->idx->refcount--;
+               DELETE_ZVAL(writer->idx);
+       }
+       efree(writer);
+}
+/* }}} */
+
+/* {{{ spl_array_writer_default_create */
+zend_object_value spl_array_writer_default_create(zend_class_entry *class_type TSRMLS_DC)
+{
+       zend_object_value retval;
+       spl_array_writer_object *intern;
+
+       intern = ecalloc(sizeof(spl_array_writer_object), 1);
+
+       retval.handle = zend_objects_store_put(intern, spl_array_writer_default_dtor, NULL TSRMLS_CC);
+       retval.handlers = &spl_array_writer_default_handlers;
+
+       return retval;
+}
+/* }}} */
+
+/* {{{ spl_array_writer_default_set */
+void spl_array_writer_default_set(zval *object, zval *newval, zval **retval TSRMLS_DC)
+{
+       zval *obj, *idx;
+       spl_array_writer_object *writer;
+
+       writer = (spl_array_writer_object *) zend_object_store_get_object(object TSRMLS_CC);
+       obj = writer->obj;
+       idx = writer->idx;
+       spl_begin_method_call_arg_ex2(&obj, "set", retval, &idx, &newval, 0, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ SPL_CLASS_FUNCTION(array_writer_default, __construct) */
+SPL_CLASS_FUNCTION(array_writer_default, __construct)
+{
+       zval *object = getThis();
+       zval *obj, *idx;
+       spl_array_writer_object *writer;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &obj, &idx) == FAILURE) {
+               php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to parse parameters");
+               return;
+       }
+       writer = (spl_array_writer_object *) zend_object_store_get_object(object TSRMLS_CC);
+       writer->obj = obj; obj->refcount++;
+       writer->idx = idx; idx->refcount++;
+
+}
+/* }}} */
+
+/* {{{ SPL_CLASS_FUNCTION(array_writer_default, set) */
+SPL_CLASS_FUNCTION(array_writer_default, set)
+{
+       zval *object = getThis();
+       zval *newval;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &newval) == FAILURE) {
+               php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to parse parameters");
+               return;
+       }
+       spl_array_writer_default_set(object, newval, &return_value TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_fetch_dimension_address */
+int spl_fetch_dimension_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type TSRMLS_DC)
+{
+       zval **container_ptr = spl_get_zval_ptr_ptr(op1, Ts TSRMLS_CC);
+
+       if (spl_is_instance_of(container_ptr, spl_ce_array_read TSRMLS_CC)) {
+               zval **retval = &(T(result->u.var).var.ptr);
+               zval *dim = spl_get_zval_ptr(op2, Ts, &EG(free_op2) TSRMLS_CC);
+               zval *exists;
+
+               /*ALLOC_ZVAL(exists); not needed */
+               spl_begin_method_call_arg_ex1(container_ptr, "exists", &exists, &dim, 0, NULL TSRMLS_CC);
+               if (!i_zend_is_true(exists)) {
+                       if (type == BP_VAR_R || type == BP_VAR_RW) {
+                               SEPARATE_ZVAL(&dim);
+                               convert_to_string_ex(&dim);
+                               zend_error(E_NOTICE,"Undefined index:  %s", Z_STRVAL_P(dim));
+                               DELETE_ZVAL(dim);
+                       }
+                       if (type == BP_VAR_R || type == BP_VAR_IS) {
+                               DELETE_RET_ZVAL(exists);
+                               *retval = &EG(error_zval);
+                               (*retval)->refcount++;
+                               FREE_OP(Ts, op2, EG(free_op2));
+                               SELECTIVE_PZVAL_LOCK(*retval, result);
+                               return 0;
+                       }
+               }
+               DELETE_RET_ZVAL(exists);
+               if (type == BP_VAR_R || type == BP_VAR_IS) {
+                       spl_begin_method_call_arg_ex1(container_ptr, "get", retval, &dim, 0, NULL TSRMLS_CC);
+                       (*retval)->refcount--;
+               } else 
+#ifdef SPL_ARRAY_WRITE
+               if (spl_is_instance_of(container_ptr, spl_ce_array_access_ex TSRMLS_CC)) {
+                       /* array_access_ex instaces have their own way of creating an access_writer */
+                       spl_begin_method_call_arg_ex1(container_ptr, "new_writer", retval, &dim, 0, NULL TSRMLS_CC);
+                       T(result->u.var).var.ptr = *retval;
+                       AI_PTR_2_PTR_PTR(T(result->u.var).var);
+                       SELECTIVE_PZVAL_LOCK(*retval, result);
+               } else if (spl_is_instance_of(container_ptr, spl_ce_array_access TSRMLS_CC)) {
+                       /* array_access instances create the default array_writer: array_write */
+                       spl_array_writer_object *writer;
+                       spl_instanciate(spl_ce_array_writer_default, retval TSRMLS_CC);
+                       T(result->u.var).var.ptr = *retval;
+                       AI_PTR_2_PTR_PTR(T(result->u.var).var);
+                       writer = (spl_array_writer_object *) zend_object_store_get_object(*retval TSRMLS_CC);
+                       writer->obj = *container_ptr; writer->obj->refcount++;
+                       writer->idx = dim;            writer->idx->refcount++;
+                       SELECTIVE_PZVAL_LOCK(*retval, result);
+               } else {
+                       zend_error(E_ERROR, "Object must implement spl::array_access for write access");
+                       retval = &EG(error_zval_ptr);
+               }
+               SELECTIVE_PZVAL_LOCK(*retval, result);
+#else
+               zend_error(E_ERROR, "SPL compiled withut array write hook");
+#endif
+               FREE_OP(Ts, op2, EG(free_op2));
+               return 0;
+       }
+       return 1;
+}
+/* }}} */
+
+/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_R) */
+#ifdef SPL_ARRAY_READ
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_R)
+{
+       if (!spl_fetch_dimension_address(&EX(opline)->result, &EX(opline)->op1, &EX(opline)->op2, EX(Ts), BP_VAR_R TSRMLS_CC))
+       {
+               if (EX(opline)->extended_value == ZEND_FETCH_ADD_LOCK) {
+                       PZVAL_LOCK(*EX_T(EX(opline)->op1.u.var).var.ptr_ptr);
+               }
+               spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+
+               AI_PTR_2_PTR_PTR(EX_T(EX(opline)->result.u.var).var);
+               NEXT_OPCODE();
+       }
+       ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FETCH_DIM_R);
+}
+#endif
+/* }}} */
+
+/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_W) */
+#ifdef SPL_ARRAY_READ
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_W)
+{
+       if (!spl_fetch_dimension_address(&EX(opline)->result, &EX(opline)->op1, &EX(opline)->op2, EX(Ts), BP_VAR_W TSRMLS_CC))
+       {
+               spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+
+               NEXT_OPCODE();
+       }
+       ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FETCH_DIM_W);
+}
+#endif
+/* }}} */
+
+/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_RW) */
+#ifdef SPL_ARRAY_READ
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_RW)
+{
+       if (!spl_fetch_dimension_address(&EX(opline)->result, &EX(opline)->op1, &EX(opline)->op2, EX(Ts), BP_VAR_RW TSRMLS_CC))
+       {
+               spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+
+               NEXT_OPCODE();
+       }
+       ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FETCH_DIM_RW);
+}
+#endif
+/* }}} */
+
+/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN) */
+#ifdef SPL_ARRAY_WRITE
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN)
+{
+       zval **writer = spl_get_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+       zval *newval, *retval, *target;
+       znode *result;
+
+       if (writer && *writer && Z_TYPE_PP(writer) == IS_OBJECT) {
+               /* optimization: do pre checks and only test for handlers in case of
+                * spl::array_writer_default, for spl::array_writer we must use the 
+                * long way of calling spl_instance
+                * if (spl_is_instance_of(writer, spl_ce_array_writer_default TSRMLS_CC))
+                */
+               if ((*writer)->value.obj.handlers == &spl_array_writer_default_handlers) {
+                       newval = spl_get_zval_ptr(&EX(opline)->op2, EX(Ts), &EG(free_op2) TSRMLS_CC);
+                       spl_array_writer_default_set(*writer, newval, &retval TSRMLS_CC);
+               } else if (spl_is_instance_of(writer, spl_ce_array_writer TSRMLS_CC)) {
+                       newval = spl_get_zval_ptr(&EX(opline)->op2, EX(Ts), &EG(free_op2) TSRMLS_CC);
+                       spl_begin_method_call_arg_ex1(writer, "set", &retval, &newval, 0, NULL TSRMLS_CC);
+               } else {
+                       ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN);
+               }
+       } else {
+               ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_ASSIGN);
+       }
+       spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+
+       result = &EX(opline)->result;
+       if (result) {
+               if (retval->refcount<2) {
+                       if ((*writer)->value.obj.handlers == &spl_array_writer_default_handlers) {
+                               spl_array_writer_object *object = (spl_array_writer_object *) zend_object_store_get_object(*writer TSRMLS_CC);
+                               target = object->obj;
+                       } else {
+                               target = *writer;
+                       }
+                       zend_error(E_WARNING, "Method %s::set() did not return a value, using NULL", Z_OBJCE_P(target)->name);
+                       DELETE_ZVAL(retval);
+                       DELETE_ZVAL(newval);
+                       /* Unfortunately it doesn't work when trying to return newval.
+                        * But anyhow it wouldn't make sense...and confuse reference counting and such.
+                        */
+                       retval = &EG(uninitialized_zval);
+               } else {
+                       retval->refcount--;
+               }
+               EX_T(EX(opline)->result.u.var).var.ptr = retval;
+               AI_PTR_2_PTR_PTR(EX_T(EX(opline)->result.u.var).var);
+               SELECTIVE_PZVAL_LOCK(retval, result);
+       } else {
+               retval->refcount = 1;
+               DELETE_ZVAL(retval);
+       }
+
+       (*writer)->refcount = 1;
+       DELETE_ZVAL(*writer);
+       FREE_OP(EX(Ts), &EX(opline)->op2, EG(free_op2));
+
+       NEXT_OPCODE();
+}
+#endif
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_array.h b/ext/spl/spl_array.h
new file mode 100755 (executable)
index 0000000..7f27f5d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef SPL_ARRAY_H
+#define SPL_ARRAY_H
+
+#include "php.h"
+#include "php_spl.h"
+
+#ifdef SPL_ARRAY_READ
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_R);
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_W);
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FETCH_DIM_RW);
+#endif
+
+#ifdef SPL_ARRAY_WRITE
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_ASSIGN);
+#endif
+
+SPL_CLASS_FUNCTION(array_writer_default, __construct);
+SPL_CLASS_FUNCTION(array_writer_default, set);
+
+zend_object_value spl_array_writer_default_create(zend_class_entry *class_type TSRMLS_DC);
+
+#endif /* SPL_ARRAY_H */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_engine.c b/ext/spl/spl_engine.c
new file mode 100755 (executable)
index 0000000..84e5d8a
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "zend_compile.h"
+#include "zend_execute_locks.h"
+
+#include "php_spl.h"
+#include "spl_functions.h"
+#include "spl_engine.h"
+
+/* {{{ spl_begin_method_call_arg */
+int spl_begin_method_call_arg(zval **ce, char *function_name, zval *retval, zval *arg1 TSRMLS_DC)
+{
+       zval *args[1];
+       zval fn_name;
+       
+       ZVAL_STRING(&fn_name, function_name, 0);
+       
+       args[0] = arg1;
+       return call_user_function(&Z_OBJCE_PP(ce)->function_table, ce, &fn_name, retval, 1, args TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_begin_method_call_this */
+int spl_begin_method_call_this(zval **ce, char *function_name, zval *retval TSRMLS_DC)
+{
+       zval fn_name;
+       
+       ZVAL_STRING(&fn_name, function_name, 0);
+       
+       return call_user_function(&Z_OBJCE_PP(ce)->function_table, ce, &fn_name, retval, 0, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_begin_method_call_arg_ex1 */
+int spl_begin_method_call_arg_ex1(zval **ce, char *function_name, zval **retval, zval **arg1, int no_separation, HashTable *symbol_table TSRMLS_DC)
+{
+       zval **args[1];
+       zval fn_name;
+       
+       ZVAL_STRING(&fn_name, function_name, 0);
+       
+       args[0] = arg1;
+       return call_user_function_ex(&Z_OBJCE_PP(ce)->function_table, ce, &fn_name, retval, 1, args, no_separation, symbol_table TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_begin_method_call_arg_ex2 */
+int spl_begin_method_call_arg_ex2(zval **ce, char *function_name, zval **retval, zval **arg1, zval **arg2, int no_separation, HashTable *symbol_table TSRMLS_DC)
+{
+       zval **args[2];
+       zval fn_name;
+       
+       ZVAL_STRING(&fn_name, function_name, 0);
+       
+       args[0] = arg1;
+       args[1] = arg2;
+       return call_user_function_ex(&Z_OBJCE_PP(ce)->function_table, ce, &fn_name, retval, 2, args, no_separation, symbol_table TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_instanciate */
+void spl_instanciate(zend_class_entry *pce, zval **object TSRMLS_DC)
+{
+       ALLOC_ZVAL(*object);
+       object_init_ex(*object, pce);
+       (*object)->refcount = 1;
+       (*object)->is_ref = 1; /* check if this can be hold always */
+}
+/* }}} */
+
+/* {{{ spl_instanciate_arg_ex2 */
+int spl_instanciate_arg_ex2(zend_class_entry *pce, zval **retval, zval **arg1, zval **arg2, int no_separation, HashTable *symbol_table TSRMLS_DC)
+{
+       zval **args[2];
+       zval fn_name;
+       zval *object;
+       
+       spl_instanciate(pce, &object TSRMLS_CC);
+       
+       retval = &EG(uninitialized_zval_ptr);
+       
+       ZVAL_STRING(&fn_name, pce->constructor->common.function_name, 0);
+       
+       args[0] = arg1;
+       args[1] = arg2;
+       call_user_function_ex(&pce->function_table, &object, &fn_name, retval, 2, args, no_separation, symbol_table TSRMLS_CC);
+       *retval = object;
+       return 0;
+}
+/* }}} */
+
+/* {{{ spl_get_zval_ptr_ptr 
+   Remember to call spl_unlock_ptr_ptr when needed */
+zval ** spl_get_zval_ptr_ptr(znode *node, temp_variable *Ts TSRMLS_DC)
+{
+       if (node->op_type==IS_VAR) {
+               return T(node->u.var).var.ptr_ptr;
+       } else {
+               return NULL;
+       }
+}
+/* }}} */
+
+/* {{{ spl_unlock_zval_ptr_ptr */
+void spl_unlock_zval_ptr_ptr(znode *node, temp_variable *Ts TSRMLS_DC)
+{
+       if (node->op_type==IS_VAR) {
+               if (T(node->u.var).var.ptr_ptr) {
+                       PZVAL_UNLOCK(*T(node->u.var).var.ptr_ptr);
+               } else if (T(node->u.var).EA.type==IS_STRING_OFFSET) {
+                       PZVAL_UNLOCK(T(node->u.var).EA.data.str_offset.str);
+               }
+       }
+}
+/* }}} */
+
+/* {{{ spl_get_zval_ptr */
+zval * spl_get_zval_ptr(znode *node, temp_variable *Ts, zval **should_free TSRMLS_DC)
+{
+       switch (node->op_type) {
+               case IS_CONST:
+                       *should_free = 0;
+                       return &node->u.constant;
+                       break;
+               case IS_TMP_VAR:
+                       return *should_free = &T(node->u.var).tmp_var;
+                       break;
+               case IS_VAR:
+                       if (T(node->u.var).var.ptr) {
+                               PZVAL_UNLOCK(T(node->u.var).var.ptr);
+                               *should_free = 0;
+                               return T(node->u.var).var.ptr;
+                       } else {
+                               *should_free = &T(node->u.var).tmp_var;
+
+                               switch (T(node->u.var).EA.type) {
+                                       case IS_STRING_OFFSET: {
+                                                       temp_variable *T = &T(node->u.var);
+                                                       zval *str = T->EA.data.str_offset.str;
+
+                                                       if (T->EA.data.str_offset.str->type != IS_STRING
+                                                               || (T->EA.data.str_offset.offset<0)
+                                                               || (T->EA.data.str_offset.str->value.str.len <= T->EA.data.str_offset.offset)) {
+                                                               zend_error(E_NOTICE, "Uninitialized string offset:  %d", T->EA.data.str_offset.offset);
+                                                               T->tmp_var.value.str.val = empty_string;
+                                                               T->tmp_var.value.str.len = 0;
+                                                       } else {
+                                                               char c = str->value.str.val[T->EA.data.str_offset.offset];
+
+                                                               T->tmp_var.value.str.val = estrndup(&c, 1);
+                                                               T->tmp_var.value.str.len = 1;
+                                                       }
+                                                       PZVAL_UNLOCK(str);
+                                                       T->tmp_var.refcount=1;
+                                                       T->tmp_var.is_ref=1;
+                                                       T->tmp_var.type = IS_STRING;
+                                                       return &T->tmp_var;
+                                               }
+                                               break;
+                               }
+                       }
+                       break;
+               case IS_UNUSED:
+                       *should_free = 0;
+                       return NULL;
+                       break;
+               EMPTY_SWITCH_DEFAULT_CASE()
+       }
+       return NULL;
+}
+/* }}} */
+
+/* {{{ spl_is_instance_of */
+int spl_is_instance_of(zval **obj, zend_class_entry *ce TSRMLS_DC)
+{
+       /* Ensure everything needed is available before checking for the type.
+        * HAS_CLASS_ENTRY is neededto ensure Z_OBJCE_PP will not throw an error.
+        */
+       if (!obj || !*obj || Z_TYPE_PP(obj) != IS_OBJECT || !HAS_CLASS_ENTRY(**obj)) {
+               return 0;
+       } else {
+               zend_class_entry *instance_ce = Z_OBJCE_PP(obj);
+       
+               if (instanceof_function(instance_ce, ce TSRMLS_CC)) {
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_engine.h b/ext/spl/spl_engine.h
new file mode 100755 (executable)
index 0000000..ded59f4
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef SPL_ENGINE_H
+#define SPL_ENGINE_H
+
+#include "php.h"
+#include "php_spl.h"
+
+#include "zend_compile.h"
+#include "zend_execute_locks.h"
+
+#undef EX
+#define EX(element) execute_data->element
+#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset))
+#define T(offset) (*(temp_variable *)((char *) Ts + offset))
+
+#define NEXT_OPCODE()          \
+       EX(opline)++;                   \
+       return 0;
+
+int zend_do_fcall_common_helper(ZEND_OPCODE_HANDLER_ARGS);
+
+int spl_begin_method_call_arg(zval **ce, char *function_name, zval *retval, zval *arg1 TSRMLS_DC);
+int spl_begin_method_call_this(zval **ce, char *function_name, zval *retval TSRMLS_DC);
+int spl_begin_method_call_arg_ex1(zval **ce, char *function_name, zval **retval, zval **arg1, int no_separation, HashTable *symbol_table TSRMLS_DC);
+int spl_begin_method_call_arg_ex2(zval **ce, char *function_name, zval **retval, zval **arg1, zval **arg2, int no_separation, HashTable *symbol_table TSRMLS_DC);
+
+void spl_instanciate(zend_class_entry *pce, zval **object TSRMLS_DC);
+int spl_instanciate_arg_ex2(zend_class_entry *pce, zval **retval, zval **arg1, zval **arg2, int no_separation, HashTable *symbol_table TSRMLS_DC);
+
+zval ** spl_get_zval_ptr_ptr(znode *node, temp_variable *Ts TSRMLS_DC);
+void spl_unlock_zval_ptr_ptr(znode *node, temp_variable *Ts TSRMLS_DC);
+zval * spl_get_zval_ptr(znode *node, temp_variable *Ts, zval **should_free TSRMLS_DC);
+
+int spl_is_instance_of(zval **obj, zend_class_entry *ce TSRMLS_DC);
+
+
+#endif /* SPL_ENGINE_H */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_foreach.c b/ext/spl/spl_foreach.c
new file mode 100755 (executable)
index 0000000..8474639
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "zend_compile.h"
+#include "zend_execute_locks.h"
+
+#include "php_spl.h"
+#include "spl_functions.h"
+#include "spl_engine.h"
+#include "spl_foreach.h"
+
+/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FE_RESET) */
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FE_RESET)
+{
+       zval **obj, *retval;
+
+       if (EX(opline)->extended_value) {
+               obj = spl_get_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+               if (spl_is_instance_of(obj, spl_ce_iterator TSRMLS_CC)) {
+                       spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+                       MAKE_STD_ZVAL(retval);
+                       spl_begin_method_call_this(obj, "new_iterator", retval TSRMLS_CC);
+                       EX_T(EX(opline)->result.u.var).var.ptr = retval;
+                       EX_T(EX(opline)->result.u.var).var.ptr_ptr = &EX_T(EX(opline)->result.u.var).var.ptr;   
+                       EX(opline)->op2.u.EA.type = 0; /* missuse as index */
+
+                       PZVAL_LOCK(retval);
+
+                       NEXT_OPCODE();
+               } else if (spl_is_instance_of(obj, spl_ce_forward TSRMLS_CC)) {
+                       spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+
+                       EX_T(EX(opline)->result.u.var).var.ptr = *obj;
+                       EX_T(EX(opline)->result.u.var).var.ptr_ptr = &EX_T(EX(opline)->result.u.var).var.ptr;   
+                       EX(opline)->op2.u.EA.type = 0; /* missuse as index */
+
+                       (*obj)->refcount++;
+                       PZVAL_LOCK(*obj);
+
+                       NEXT_OPCODE();
+               }
+       }
+       ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FE_RESET);
+}
+/* }}} */
+
+/* {{{ ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FE_FETCH) */
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FE_FETCH)
+{
+       zval **obj = spl_get_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+       zval more, tmp, *value, *key, *result;
+
+       if (spl_is_instance_of(obj, spl_ce_forward TSRMLS_CC)) {
+               zend_uint index = EX(opline)->op2.u.EA.type++;
+               
+               spl_unlock_zval_ptr_ptr(&EX(opline)->op1, EX(Ts) TSRMLS_CC);
+               PZVAL_LOCK(*obj);
+
+               if (index) {
+                       spl_begin_method_call_this(obj, "next", &more TSRMLS_CC);
+               } else if (spl_is_instance_of(obj, spl_ce_sequence TSRMLS_CC)) {
+                       spl_begin_method_call_this(obj, "rewind", &more TSRMLS_CC);
+               }
+
+               spl_begin_method_call_this(obj, "has_more", &more TSRMLS_CC);
+               if (zend_is_true(&more)) {
+                       result = &EX_T(EX(opline)->result.u.var).tmp_var;
+                       array_init(result);
+                       ALLOC_ZVAL(value);
+
+                       spl_begin_method_call_this(obj, "current", value TSRMLS_CC);
+
+                       zend_hash_index_update(result->value.ht, 0, &value, sizeof(zval *), NULL);
+               
+                       if (spl_is_instance_of(obj, spl_ce_assoc TSRMLS_CC)) {
+                               ALLOC_ZVAL(key);
+                               spl_begin_method_call_this(obj, "key", key TSRMLS_CC);
+                       } else {
+                               /* If someone makes a reference to this value then there is
+                                * a real problem. And the only way to avoid it is to alloc
+                                * dealloc this temporary zval then.
+                                */
+                               tmp.value.lval = index;
+                               tmp.type = IS_LONG;
+                               tmp.refcount = 0;
+                               tmp.is_ref = 0;
+                               key = &tmp;
+                       }
+                       zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL);
+
+                       NEXT_OPCODE();
+               }
+               else
+               EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num;
+               return 0;
+       }
+       ZEND_EXECUTE_HOOK_ORIGINAL(ZEND_FE_FETCH);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_foreach.h b/ext/spl/spl_foreach.h
new file mode 100755 (executable)
index 0000000..427f331
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef SPL_FOREACH_H
+#define SPL_FOREACH_H
+
+#include "php.h"
+#include "php_spl.h"
+
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FE_RESET);
+ZEND_EXECUTE_HOOK_FUNCTION(ZEND_FE_FETCH);
+
+#endif /* SPL_FOREACH_H */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_functions.c b/ext/spl/spl_functions.c
new file mode 100755 (executable)
index 0000000..1977db1
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_spl.h"
+#include "spl_foreach.h"
+
+/* {{{ spl_destroy_class */
+void spl_destroy_class(zend_class_entry ** ppce)
+{                           
+       SPL_DEBUG(fprintf(stderr, "Destroy(%s): %s\n", (*ppce)->type == ZEND_USER_CLASS ? "user" : "other", (*ppce)->name);)
+       destroy_zend_class(ppce);
+}
+/* }}} */
+
+/* {{{ spl_register_namespace */
+void spl_register_namespace(zend_namespace ** ppns, char * namespace_name TSRMLS_DC)
+{
+       zend_namespace ns;
+
+       INIT_NAMESPACE(ns, namespace_name);
+       *ppns = zend_register_internal_namespace(&ns TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ spl_register_interface */
+void spl_register_interface(zend_class_entry ** ppce, zend_namespace * namespace_entry, char * class_name TSRMLS_DC)
+{
+       zend_class_entry ce;
+       
+       INIT_CLASS_ENTRY(ce, class_name, NULL);
+       ce.num_interfaces = 0;
+       *ppce = zend_register_internal_ns_class(&ce, NULL, namespace_entry, NULL TSRMLS_CC);
+
+       /* entries changed by initialize */
+       (*ppce)->ce_flags = ZEND_ACC_ABSTRACT | ZEND_ACC_INTERFACE;
+       (*ppce)->ns = namespace_entry;
+}
+/* }}} */
+
+/* {{{ spl_register_std_class */
+void spl_register_std_class(zend_class_entry ** ppce, zend_namespace * namespace_entry, char * class_name, void * obj_ctor TSRMLS_DC)
+{
+       zend_class_entry ce;
+       memset(&ce, 0, sizeof(zend_class_entry));
+       
+       INIT_CLASS_ENTRY(ce, class_name, NULL);
+
+       ce.num_interfaces = 0;
+
+       *ppce = zend_register_internal_ns_class(&ce, NULL, namespace_entry, NULL TSRMLS_CC);
+
+       /* entries changed by initialize */
+       (*ppce)->ce_flags = ZEND_ACC_ABSTRACT | ZEND_ACC_INTERFACE;
+       (*ppce)->create_object = obj_ctor;
+       (*ppce)->ns = namespace_entry;
+}
+/* }}} */
+
+/* {{{ spl_register_interface_function */
+void spl_register_interface_function(zend_class_entry * class_entry, char * fn_name TSRMLS_DC)
+{
+       zend_function function, *reg_function;
+       zend_internal_function *pfunction = (zend_internal_function *)&function;
+
+       pfunction->type = ZEND_INTERNAL_FUNCTION;
+       pfunction->handler = NULL;
+       pfunction->arg_types = NULL;
+       pfunction->function_name = fn_name;
+       pfunction->scope = class_entry;
+       pfunction->fn_flags = ZEND_ACC_ABSTRACT | ZEND_ACC_PUBLIC;
+       pfunction->ns = class_entry->ns;
+       pfunction->prototype = NULL;
+       zend_hash_add(&class_entry->function_table, fn_name, strlen(fn_name)+1, &function, sizeof(zend_function), (void**)&reg_function);
+}
+/* }}} */
+
+/* {{{ spl_register_parent_ce */
+void spl_register_parent_ce(zend_class_entry * class_entry, zend_class_entry * parent_class TSRMLS_DC)
+{
+       class_entry->parent = parent_class;
+}
+/* }}} */
+
+/* {{{ spl_register_implement */
+void spl_register_implement(zend_class_entry * class_entry, zend_class_entry * interface_entry TSRMLS_DC)
+{
+       zend_uint num_interfaces = ++class_entry->num_interfaces;
+       class_entry->interfaces = (zend_class_entry **) realloc(class_entry->interfaces, sizeof(zend_class_entry *) * num_interfaces);
+       class_entry->interfaces[num_interfaces-1] = interface_entry;
+}
+/* }}} */
+
+/* {{{ spl_register_functions */
+void spl_register_functions(zend_class_entry * class_entry, function_entry * function_list TSRMLS_DC)
+{
+       zend_register_functions(class_entry, function_list, &class_entry->function_table, MODULE_PERSISTENT TSRMLS_CC);
+}
+/* }}} */
+
+/* {{ spl_make_fully_qualyfied_name */
+char * spl_make_fully_qualyfied_name(zend_class_entry * pce TSRMLS_DC)
+{
+       if (pce->ns && (pce->ns != &CG(global_namespace))) {
+               char *retval;
+
+               spprintf(&retval, 0, "%s::%s", pce->ns->name, pce->name);
+               return retval;
+       } else {
+               return estrdup(pce->name);
+       }
+}
+/* }}} */
+
+/* {{{ spl_add_class_name */
+void spl_add_class_name(zval * list, zend_class_entry * pce TSRMLS_DC)
+{
+       char * str = spl_make_fully_qualyfied_name(pce TSRMLS_CC); 
+       zval *tmp;
+
+       if (zend_hash_find(Z_ARRVAL_P(list), str, strlen(str)+1, (void*)&tmp) == FAILURE) {
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_STRING(tmp, str, 0);
+               zend_hash_add(Z_ARRVAL_P(list), str, strlen(str)+1, &tmp, sizeof(zval *), NULL);
+       } else {
+               efree(str);
+       }
+}
+/* }}} */
+
+/* {{{ spl_add_interfaces */
+void spl_add_interfaces(zval *list, zend_class_entry * pce TSRMLS_DC)
+{
+       zend_uint num_interfaces;
+
+       for (num_interfaces = 0; num_interfaces < pce->num_interfaces; num_interfaces++) {
+               spl_add_class_name(list, pce->interfaces[num_interfaces] TSRMLS_CC);
+               spl_add_interfaces(list, pce->interfaces[num_interfaces] TSRMLS_CC);
+       }
+       if (pce->parent) {
+               spl_add_class_name(list, pce->parent TSRMLS_CC);
+               spl_add_interfaces(list, pce->parent TSRMLS_CC);
+       }
+}
+/* }}} */
+
+/* {{{ spl_add_interfaces */
+int spl_add_classes(zend_class_entry ** ppce, zval *list TSRMLS_DC)
+{
+       spl_add_class_name(list, *ppce TSRMLS_CC);
+       if ((*ppce)->parent) {
+               spl_add_classes(&(*ppce)->parent, list TSRMLS_CC);
+       }
+       spl_add_interfaces(list, *ppce TSRMLS_CC);
+       return 0;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/spl_functions.h b/ext/spl/spl_functions.h
new file mode 100755 (executable)
index 0000000..f6a7b57
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP version 4.0                                                      |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.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: Marcus Boerger <helly@php.net>                              |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_FUNCTIONS_H
+#define PHP_FUNCTIONS_H
+
+#include "php.h"
+
+typedef zend_object_value (*create_object_func_t)(zend_class_entry *class_type TSRMLS_DC);
+
+#define REGISTER_SPL_NAMESPACE(namespace_name) \
+       spl_register_namespace(&spl_ns_ ## namespace_name, # namespace_name TSRMLS_CC);
+
+#define REGISTER_SPL_STD_CLASS(namespace_name, class_name, obj_ctor) \
+       spl_register_std_class(&spl_ce_ ## class_name, spl_ns_ ## namespace_name, # class_name, obj_ctor TSRMLS_CC);
+
+#define REGISTER_SPL_INTERFACE(namespace_name, class_name) \
+       spl_register_interface(&spl_ce_ ## class_name, spl_ns_ ## namespace_name, # class_name TSRMLS_CC);
+
+#define REGISTER_SPL_INTF_FUNC(namespace_name, class_name, function_name) \
+       spl_register_interface_function(spl_ce_ ## class_name, # function_name TSRMLS_CC);
+
+#define REGISTER_SPL_PARENT_CE(namespace_name, class_name, parent_class) \
+       spl_register_parent_ce(spl_ce_ ## class_name, spl_ce_ ## parent_class TSRMLS_CC);
+
+#define REGISTER_SPL_IMPLEMENT(namespace_name, class_name, interface_name) \
+       spl_register_implement(spl_ce_ ## class_name, spl_ce_ ## interface_name TSRMLS_CC);
+
+#define REGISTER_SPL_FUNCTIONS(namespace_name, class_name, function_list) \
+       spl_register_functions(spl_ce_ ## class_name, function_list TSRMLS_CC);
+
+void spl_destroy_class(zend_class_entry ** ppce);
+
+void spl_register_namespace(zend_namespace ** ppns, char * namespace_name TSRMLS_DC);
+
+void spl_register_std_class(zend_class_entry ** ppce, zend_namespace * namespace_entry, char * class_name, create_object_func_t ctor TSRMLS_DC);
+
+void spl_register_interface(zend_class_entry ** ppce, zend_namespace * namespace_entry, char * class_name TSRMLS_DC);
+
+void spl_register_interface_function(zend_class_entry * class_entry, char * fn_name TSRMLS_DC);
+void spl_register_parent_ce(zend_class_entry * class_entry, zend_class_entry * parent_class TSRMLS_DC);
+void spl_register_implement(zend_class_entry * class_entry, zend_class_entry * interface_entry TSRMLS_DC);
+void spl_register_functions(zend_class_entry * class_entry, function_entry * function_list TSRMLS_DC);
+
+char * spl_make_fully_qualyfied_name(zend_class_entry * pce TSRMLS_DC);
+void spl_add_class_name(zval * list, zend_class_entry * pce TSRMLS_DC);
+void spl_add_interfaces(zval *list, zend_class_entry * pce TSRMLS_DC);
+int spl_add_classes(zend_class_entry ** ppce, zval *list TSRMLS_DC);
+
+#define SPL_CLASS_FE(class_name, function_name, arg_types) \
+       PHP_NAMED_FE( function_name, spl_ ## class_name ## function_name, arg_types)
+
+#define SPL_CLASS_FUNCTION(class_name, function_name) \
+       PHP_NAMED_FUNCTION(spl_ ## class_name ## function_name)
+
+#endif /* PHP_FUNCTIONS_H */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/spl/tests/.htaccess b/ext/spl/tests/.htaccess
new file mode 100755 (executable)
index 0000000..5a01a1c
--- /dev/null
@@ -0,0 +1,3 @@
+<IfModule mod_autoindex.c>
+    IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t *.php
+</IfModule>
diff --git a/ext/spl/tests/array_access_001.phpt b/ext/spl/tests/array_access_001.phpt
new file mode 100755 (executable)
index 0000000..5748a5c
--- /dev/null
@@ -0,0 +1,127 @@
+--TEST--
+SPL: array_access
+--SKIPIF--
+<?php
+       if (!extension_loaded("spl")) die("skip");
+       if (!in_array("spl::array_access",spl_classes())) die("skip spl::array_access not present");
+?>
+--FILE--
+<?php
+class c implements spl::array_access {
+
+       public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+       function exists($index) {
+               echo __METHOD__ . "($index)\n";
+               return array_key_exists($index, $this->a);
+       }
+       function get($index) {
+               echo __METHOD__ . "($index)\n";
+               return $this->a[$index];
+       }
+       function set($index, $newval) {
+               echo __METHOD__ . "($index,$newval)\n";
+               return $this->a[$index] = $newval;
+       }
+}
+
+$obj = new c();
+
+var_dump($obj->a);
+
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+echo "WRITE 1\n";
+$obj[1] = 'Changed 1';
+var_dump($obj[1]);
+echo "WRITE 2\n";
+$obj['4th'] = 'Changed 4th';
+var_dump($obj['4th']);
+echo "WRITE 3\n";
+$obj['5th'] = 'Added 5th';
+var_dump($obj['5th']);
+echo "WRITE 4\n";
+$obj[6] = 'Added 6';
+var_dump($obj[6]);
+
+var_dump($obj[0]);
+var_dump($obj[2]);
+
+$x = $obj[6] = 'changed 6';
+var_dump($obj[6]);
+var_dump($x);
+
+print "Done\n";
+?>
+--EXPECTF--
+array(4) {
+  [0]=>
+  string(3) "1st"
+  [1]=>
+  int(1)
+  [2]=>
+  string(3) "3rd"
+  ["4th"]=>
+  int(4)
+}
+c::exists(0)
+c::get(0)
+string(3) "1st"
+c::exists(1)
+c::get(1)
+int(1)
+c::exists(2)
+c::get(2)
+string(3) "3rd"
+c::exists(4th)
+c::get(4th)
+int(4)
+c::exists(5th)
+
+Notice: Undefined index:  5th in %s on line %d
+NULL
+c::exists(6)
+
+Notice: Undefined index:  6 in %s on line %d
+NULL
+WRITE 1
+c::exists(1)
+c::set(1,Changed 1)
+c::exists(1)
+c::get(1)
+string(9) "Changed 1"
+WRITE 2
+c::exists(4th)
+c::set(4th,Changed 4th)
+c::exists(4th)
+c::get(4th)
+string(11) "Changed 4th"
+WRITE 3
+c::exists(5th)
+c::set(5th,Added 5th)
+c::exists(5th)
+c::get(5th)
+string(9) "Added 5th"
+WRITE 4
+c::exists(6)
+c::set(6,Added 6)
+c::exists(6)
+c::get(6)
+string(7) "Added 6"
+c::exists(0)
+c::get(0)
+string(3) "1st"
+c::exists(2)
+c::get(2)
+string(3) "3rd"
+c::exists(6)
+c::set(6,changed 6)
+c::exists(6)
+c::get(6)
+string(9) "changed 6"
+string(9) "changed 6"
+Done
diff --git a/ext/spl/tests/array_access_002.phpt b/ext/spl/tests/array_access_002.phpt
new file mode 100755 (executable)
index 0000000..d415b19
--- /dev/null
@@ -0,0 +1,137 @@
+--TEST--
+SPL: array_access without return in set()
+--SKIPIF--
+<?php
+       if (!extension_loaded("spl")) die("skip");
+       if (!in_array("spl::array_access",spl_classes())) die("skip spl::array_access not present");
+?>
+--FILE--
+<?php
+class c implements spl::array_access {
+
+       public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+       function exists($index) {
+               echo __METHOD__ . "($index)\n";
+               return array_key_exists($index, $this->a);
+       }
+       function get($index) {
+               echo __METHOD__ . "($index)\n";
+               return $this->a[$index];
+       }
+       function set($index, $newval) {
+               echo __METHOD__ . "($index,$newval)\n";
+               /* return */ $this->a[$index] = $newval;
+       }
+}
+
+$obj = new c();
+
+var_dump($obj->a);
+
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+echo "WRITE 1\n";
+$obj[1] = 'Changed 1';
+var_dump($obj[1]);
+echo "WRITE 2\n";
+$obj['4th'] = 'Changed 4th';
+var_dump($obj['4th']);
+echo "WRITE 3\n";
+$obj['5th'] = 'Added 5th';
+var_dump($obj['5th']);
+echo "WRITE 4\n";
+$obj[6] = 'Added 6';
+var_dump($obj[6]);
+
+var_dump($obj[0]);
+var_dump($obj[2]);
+
+$x = $obj[6] = 'changed 6';
+var_dump($obj[6]);
+var_dump($x);
+
+print "Done\n";
+?>
+--EXPECTF--
+array(4) {
+  [0]=>
+  string(3) "1st"
+  [1]=>
+  int(1)
+  [2]=>
+  string(3) "3rd"
+  ["4th"]=>
+  int(4)
+}
+c::exists(0)
+c::get(0)
+string(3) "1st"
+c::exists(1)
+c::get(1)
+int(1)
+c::exists(2)
+c::get(2)
+string(3) "3rd"
+c::exists(4th)
+c::get(4th)
+int(4)
+c::exists(5th)
+
+Notice: Undefined index:  5th in %s on line %d
+NULL
+c::exists(6)
+
+Notice: Undefined index:  6 in %s on line %d
+NULL
+WRITE 1
+c::exists(1)
+c::set(1,Changed 1)
+
+Warning: Method c::set() did not return a value, using NULL in %s on line %d
+c::exists(1)
+c::get(1)
+string(9) "Changed 1"
+WRITE 2
+c::exists(4th)
+c::set(4th,Changed 4th)
+
+Warning: Method c::set() did not return a value, using NULL in %s on line %d
+c::exists(4th)
+c::get(4th)
+string(11) "Changed 4th"
+WRITE 3
+c::exists(5th)
+c::set(5th,Added 5th)
+
+Warning: Method c::set() did not return a value, using NULL in %s on line %d
+c::exists(5th)
+c::get(5th)
+string(9) "Added 5th"
+WRITE 4
+c::exists(6)
+c::set(6,Added 6)
+
+Warning: Method c::set() did not return a value, using NULL in %s on line %d
+c::exists(6)
+c::get(6)
+string(7) "Added 6"
+c::exists(0)
+c::get(0)
+string(3) "1st"
+c::exists(2)
+c::get(2)
+string(3) "3rd"
+c::exists(6)
+c::set(6,changed 6)
+
+Warning: Method c::set() did not return a value, using NULL in %s on line %d
+c::exists(6)
+c::get(6)
+string(9) "changed 6"
+NULL
+Done
diff --git a/ext/spl/tests/array_access_ex.phpt b/ext/spl/tests/array_access_ex.phpt
new file mode 100755 (executable)
index 0000000..2800c05
--- /dev/null
@@ -0,0 +1,154 @@
+--TEST--
+SPL: array_access
+--SKIPIF--
+<?php
+       if (!extension_loaded("spl")) die("skip");
+       if (!in_array("spl::array_access",spl_classes())) die("skip spl::array_access not present");
+?>
+--FILE--
+<?php
+class array_write implements spl::array_writer {
+       private $obj;
+       private $idx;
+
+       function __construct(&$obj, $index = null) {
+               $this->obj = &$obj;
+               $this->idx = $index;
+       }
+
+       function set($value) {
+               echo __METHOD__ . "($value,".$this->idx.")\n";
+               return $this->obj->set($this->idx, $value);
+       }
+}
+
+class c implements spl::array_access_ex {
+
+       public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+
+       function new_writer($index) {
+               return new array_write(&$this, $index);
+       }
+
+       function exists($index) {
+               echo __METHOD__ . "($index)\n";
+               return array_key_exists($index, $this->a);
+       }
+
+       function get($index) {
+               echo __METHOD__ . "($index)\n";
+               return $this->a[$index];
+       }
+
+       function set($index, $newval) {
+               echo __METHOD__ . "($index,$newval)\n";
+               return $this->a[$index] = $newval;
+       }
+}
+
+$obj = new c();
+
+var_dump($obj->a);
+
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+echo "WRITE 1\n";
+$obj[1] = 'Changed 1';
+var_dump($obj[1]);
+echo "WRITE 2\n";
+$obj['4th'] = 'Changed 4th';
+var_dump($obj['4th']);
+echo "WRITE 3\n";
+$obj['5th'] = 'Added 5th';
+var_dump($obj['5th']);
+echo "WRITE 4\n";
+$obj[6] = 'Added 6';
+var_dump($obj[6]);
+
+var_dump($obj[0]);
+var_dump($obj[2]);
+
+$x = $obj[6] = 'changed 6';
+var_dump($obj[6]);
+var_dump($x);
+
+print "Done\n";
+?>
+--EXPECTF--
+array(4) {
+  [0]=>
+  string(3) "1st"
+  [1]=>
+  int(1)
+  [2]=>
+  string(3) "3rd"
+  ["4th"]=>
+  int(4)
+}
+c::exists(0)
+c::get(0)
+string(3) "1st"
+c::exists(1)
+c::get(1)
+int(1)
+c::exists(2)
+c::get(2)
+string(3) "3rd"
+c::exists(4th)
+c::get(4th)
+int(4)
+c::exists(5th)
+
+Notice: Undefined index:  5th in /usr/src/php5/ext/spl/tests/array_access_ex.php on line 49
+NULL
+c::exists(6)
+
+Notice: Undefined index:  6 in /usr/src/php5/ext/spl/tests/array_access_ex.php on line 50
+NULL
+WRITE 1
+c::exists(1)
+array_write::set(Changed 1,1)
+c::set(1,Changed 1)
+c::exists(1)
+c::get(1)
+string(9) "Changed 1"
+WRITE 2
+c::exists(4th)
+array_write::set(Changed 4th,4th)
+c::set(4th,Changed 4th)
+c::exists(4th)
+c::get(4th)
+string(11) "Changed 4th"
+WRITE 3
+c::exists(5th)
+array_write::set(Added 5th,5th)
+c::set(5th,Added 5th)
+c::exists(5th)
+c::get(5th)
+string(9) "Added 5th"
+WRITE 4
+c::exists(6)
+array_write::set(Added 6,6)
+c::set(6,Added 6)
+c::exists(6)
+c::get(6)
+string(7) "Added 6"
+c::exists(0)
+c::get(0)
+string(3) "1st"
+c::exists(2)
+c::get(2)
+string(3) "3rd"
+c::exists(6)
+array_write::set(changed 6,6)
+c::set(6,changed 6)
+c::exists(6)
+c::get(6)
+string(9) "changed 6"
+string(9) "changed 6"
+Done
diff --git a/ext/spl/tests/array_read.phpt b/ext/spl/tests/array_read.phpt
new file mode 100755 (executable)
index 0000000..b1e95fd
--- /dev/null
@@ -0,0 +1,208 @@
+--TEST--
+SPL: array_read
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+echo "EXTERNAL\n";
+
+$a = array('1st', 1, 2=>'3rd', '4th'=>4);
+var_dump($a);
+
+class external implements spl::array_read {
+
+       function exists($index) {
+               echo __METHOD__ . "($index)\n";
+               return array_key_exists($index, $GLOBALS['a']);
+       }
+
+       function get($index) {
+               echo __METHOD__ . "($index)\n";
+               return $GLOBALS['a'][$index];
+       }
+}
+
+$obj = new external();
+
+var_dump($obj->get(0));
+var_dump($obj->get(1));
+var_dump($obj->get(2));
+var_dump($obj->get('4th'));
+var_dump($obj->get('5th'));
+var_dump($obj->get(6));
+
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+$out = $obj[0]; echo "$out\n";
+$out = $obj[1]; echo "$out\n";
+$out = $obj[2]; echo "$out\n";
+$out = $obj['4th']; echo "$out\n";
+
+echo "INTERNAL\n";
+
+class internal implements spl::array_read {
+
+       public $a = array('1st', 1, 2=>'3rd', '4th'=>4);
+
+       function exists($index) {
+               echo __METHOD__ . "($index)\n";
+               return array_key_exists($index, $GLOBALS['a']);
+       }
+
+       function get($index) {
+               echo __METHOD__ . "($index)\n";
+               return $GLOBALS['a'][$index];
+       }
+}
+
+$obj = new internal();
+
+var_dump($obj->a);
+
+var_dump($obj->get(0));
+var_dump($obj->get(1));
+var_dump($obj->get(2));
+var_dump($obj->get('4th'));
+var_dump($obj->get('5th'));
+var_dump($obj->get(6));
+
+var_dump($obj[0]);
+var_dump($obj[1]);
+var_dump($obj[2]);
+var_dump($obj['4th']);
+var_dump($obj['5th']);
+var_dump($obj[6]);
+
+$out = $obj[0]; echo "$out\n";
+$out = $obj[1]; echo "$out\n";
+$out = $obj[2]; echo "$out\n";
+$out = $obj['4th']; echo "$out\n";
+
+print "Done\n";
+?>
+--EXPECTF--
+EXTERNAL
+array(4) {
+  [0]=>
+  string(3) "1st"
+  [1]=>
+  int(1)
+  [2]=>
+  string(3) "3rd"
+  ["4th"]=>
+  int(4)
+}
+external::get(0)
+string(3) "1st"
+external::get(1)
+int(1)
+external::get(2)
+string(3) "3rd"
+external::get(4th)
+int(4)
+external::get(5th)
+
+Notice: Undefined index:  5th in %s on line %d
+NULL
+external::get(6)
+
+Notice: Undefined offset:  6 in %s on line %d
+NULL
+external::exists(0)
+external::get(0)
+string(3) "1st"
+external::exists(1)
+external::get(1)
+int(1)
+external::exists(2)
+external::get(2)
+string(3) "3rd"
+external::exists(4th)
+external::get(4th)
+int(4)
+external::exists(5th)
+
+Notice: Undefined index:  5th in %s on line %d
+NULL
+external::exists(6)
+
+Notice: Undefined index:  6 in %s on line %d
+NULL
+external::exists(0)
+external::get(0)
+1st
+external::exists(1)
+external::get(1)
+1
+external::exists(2)
+external::get(2)
+3rd
+external::exists(4th)
+external::get(4th)
+4
+INTERNAL
+array(4) {
+  [0]=>
+  string(3) "1st"
+  [1]=>
+  int(1)
+  [2]=>
+  string(3) "3rd"
+  ["4th"]=>
+  int(4)
+}
+internal::get(0)
+string(3) "1st"
+internal::get(1)
+int(1)
+internal::get(2)
+string(3) "3rd"
+internal::get(4th)
+int(4)
+internal::get(5th)
+
+Notice: Undefined index:  5th in %s on line %d
+NULL
+internal::get(6)
+
+Notice: Undefined offset:  6 in %s on line %d
+NULL
+internal::exists(0)
+internal::get(0)
+string(3) "1st"
+internal::exists(1)
+internal::get(1)
+int(1)
+internal::exists(2)
+internal::get(2)
+string(3) "3rd"
+internal::exists(4th)
+internal::get(4th)
+int(4)
+internal::exists(5th)
+
+Notice: Undefined index:  5th in %s on line %d
+NULL
+internal::exists(6)
+
+Notice: Undefined index:  6 in %s on line %d
+NULL
+internal::exists(0)
+internal::get(0)
+1st
+internal::exists(1)
+internal::get(1)
+1
+internal::exists(2)
+internal::get(2)
+3rd
+internal::exists(4th)
+internal::get(4th)
+4
+Done
diff --git a/ext/spl/tests/foreach.phpt b/ext/spl/tests/foreach.phpt
new file mode 100755 (executable)
index 0000000..6803e44
--- /dev/null
@@ -0,0 +1,184 @@
+--TEST--
+SPL: foreach and iterator
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+class c_iter implements spl::forward_assoc {
+
+       private $obj;
+       private $num = 0;
+
+       function __construct($obj) {
+               $this->obj = $obj;
+       }
+       function current() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return $this->num;
+       }
+       function next() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               $this->num++;
+       }
+       function has_more() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return $this->num < $this->obj->max;
+       }
+       function key() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               switch($this->num) {
+                       case 0: return "1st";
+                       case 1: return "2nd";
+                       case 2: return "3rd";
+                       default: return "???";
+               }
+       }
+}
+       
+class c implements spl::iterator {
+
+       public $max = 3;
+
+       function new_iterator() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return new c_iter($this);
+       }
+}
+
+$t = new c();
+
+for ($iter = $t->new_iterator(); $iter->has_more(); $iter->next()) {
+       echo $iter->current() . "\n";
+}
+
+$a = array(0,1,2);
+foreach($a as $v) {
+       echo "array:$v\n";
+}
+
+foreach($t as $v) {
+       echo "object:$v\n";
+}
+
+foreach($t as $v) {
+       foreach($t as $w) {
+               echo "double:$v:$w\n";
+       }
+}
+
+foreach($t as $i => $v) {
+       echo "object:$i=>$v\n";
+}
+
+print "Done\n";
+?>
+--EXPECT--
+c::new_iterator
+c_iter::has_more
+c_iter::current
+0
+c_iter::next
+c_iter::has_more
+c_iter::current
+1
+c_iter::next
+c_iter::has_more
+c_iter::current
+2
+c_iter::next
+c_iter::has_more
+array:0
+array:1
+array:2
+c::new_iterator
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:0
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:2
+c_iter::next
+c_iter::has_more
+c::new_iterator
+c_iter::has_more
+c_iter::current
+c_iter::key
+c::new_iterator
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:0:0
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:0:1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:0:2
+c_iter::next
+c_iter::has_more
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+c::new_iterator
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:1:1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:1:2
+c_iter::next
+c_iter::has_more
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+c::new_iterator
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:2:1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+double:2:2
+c_iter::next
+c_iter::has_more
+c_iter::next
+c_iter::has_more
+c::new_iterator
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:1st=>0
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:2nd=>1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:3rd=>2
+c_iter::next
+c_iter::has_more
+Done
\ No newline at end of file
diff --git a/ext/spl/tests/forward.phpt b/ext/spl/tests/forward.phpt
new file mode 100755 (executable)
index 0000000..2c8a584
--- /dev/null
@@ -0,0 +1,115 @@
+--TEST--
+SPL: forward
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+class c implements spl::forward_assoc {
+
+       public $max = 3;
+       public $num = 0;
+
+       function current() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return $this->num;
+       }
+       function next() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               $this->num++;
+       }
+       function has_more() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return $this->num < $this->max;
+       }
+       function key() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               switch($this->num) {
+                       case 0: return "1st";
+                       case 1: return "2nd";
+                       case 2: return "3rd";
+                       default: return "???";
+               }
+       }
+}
+
+$i = new c();
+
+$c_info = array(class_name($i) => array('inheits' => class_parents($i), 'implements' => class_implements($i)));
+print_r($c_info);
+
+echo "1st try\n";
+foreach($i as $w) {
+       echo "object:$w\n";
+}
+
+echo "2nd try\n";
+
+foreach($i as $v => $w) {
+       echo "object:$v=>$w\n";
+}
+
+echo "3rd try\n";
+$i->num = 0;
+
+foreach($i as $v => $w) {
+       echo "object:$v=>$w\n";
+}
+
+print "Done\n";
+?>
+--EXPECT--
+Array
+(
+    [c] => Array
+        (
+            [inheits] => Array
+                (
+                )
+
+            [implements] => Array
+                (
+                    [spl::forward_assoc] => spl::forward_assoc
+                    [spl::assoc] => spl::assoc
+                    [spl::forward] => spl::forward
+                )
+
+        )
+
+)
+1st try
+c::has_more
+c::current
+c::key
+object:0
+c::next
+c::has_more
+c::current
+c::key
+object:1
+c::next
+c::has_more
+c::current
+c::key
+object:2
+c::next
+c::has_more
+2nd try
+c::has_more
+3rd try
+c::has_more
+c::current
+c::key
+object:1st=>0
+c::next
+c::has_more
+c::current
+c::key
+object:2nd=>1
+c::next
+c::has_more
+c::current
+c::key
+object:3rd=>2
+c::next
+c::has_more
+Done
\ No newline at end of file
diff --git a/ext/spl/tests/sequence.phpt b/ext/spl/tests/sequence.phpt
new file mode 100755 (executable)
index 0000000..3608e15
--- /dev/null
@@ -0,0 +1,138 @@
+--TEST--
+SPL: sequence
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+class c implements spl::iterator {
+
+       public $max = 3;
+
+       function new_iterator() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return new c_iter($this);
+       }
+}
+
+class c_iter implements spl::sequence_assoc {
+
+       private $obj;
+       private $num = 0;
+
+       function __construct($obj) {
+               $this->obj = $obj;
+       }
+       function rewind() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               $this->num = 0;
+       }
+       function current() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return $this->num;
+       }
+       function next() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               $this->num++;
+       }
+       function has_more() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               return $this->num < $this->obj->max;
+       }
+       function key() {
+               echo __CLASS__ . '::' . __FUNCTION__ . "\n";
+               switch($this->num) {
+                       case 0: return "1st";
+                       case 1: return "2nd";
+                       case 2: return "3rd";
+                       default: return "???";
+               }
+       }
+}
+
+$t = new c();
+$i = $t->new_iterator(); 
+
+$c_info = array(class_name($t) => array('inheits' => class_parents($t), 'implements' => class_implements($t)),
+                class_name($i) => array('inheits' => class_parents($i), 'implements' => class_implements($i)));
+print_r($c_info);
+
+foreach($i as $w) {
+       echo "object:$w\n";
+}
+
+foreach($i as $v => $w) {
+       echo "object:$v=>$w\n";
+}
+
+print "Done\n";
+?>
+--EXPECT--
+c::new_iterator
+Array
+(
+    [c] => Array
+        (
+            [inheits] => Array
+                (
+                )
+
+            [implements] => Array
+                (
+                    [spl::iterator] => spl::iterator
+                )
+
+        )
+
+    [c_iter] => Array
+        (
+            [inheits] => Array
+                (
+                )
+
+            [implements] => Array
+                (
+                    [spl::sequence_assoc] => spl::sequence_assoc
+                    [spl::forward_assoc] => spl::forward_assoc
+                    [spl::assoc] => spl::assoc
+                    [spl::forward] => spl::forward
+                    [spl::sequence] => spl::sequence
+                )
+
+        )
+
+)
+c_iter::rewind
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:0
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:2
+c_iter::next
+c_iter::has_more
+c_iter::rewind
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:1st=>0
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:2nd=>1
+c_iter::next
+c_iter::has_more
+c_iter::current
+c_iter::key
+object:3rd=>2
+c_iter::next
+c_iter::has_more
+Done
\ No newline at end of file