Add spl extension

This commit is contained in:
Marcus Boerger 2003-05-01 23:28:28 +00:00
parent 0d46a490f7
commit b5a70a72d1
24 changed files with 3042 additions and 0 deletions

2
ext/spl/CREDITS Executable file
View file

@ -0,0 +1,2 @@
SPL
Marcus Boerger

0
ext/spl/EXPERIMENTAL Executable file
View file

13
ext/spl/TODO Executable file
View file

@ -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

41
ext/spl/config.m4 Executable file
View file

@ -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

72
ext/spl/examples/dba_dump.php Executable file
View file

@ -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";
}
?>

310
ext/spl/php_spl.c Executable file
View file

@ -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
*/

114
ext/spl/php_spl.h Executable file
View file

@ -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
*/

306
ext/spl/spl.php Executable file
View file

@ -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);
}
}
?>

349
ext/spl/spl_array.c Executable file
View file

@ -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
*/

49
ext/spl/spl_array.h Executable file
View file

@ -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
*/

223
ext/spl/spl_engine.c Executable file
View file

@ -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
*/

63
ext/spl/spl_engine.h Executable file
View file

@ -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
*/

130
ext/spl/spl_foreach.c Executable file
View file

@ -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
*/

37
ext/spl/spl_foreach.h Executable file
View file

@ -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
*/

187
ext/spl/spl_functions.c Executable file
View file

@ -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
*/

80
ext/spl/spl_functions.h Executable file
View file

@ -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
*/

3
ext/spl/tests/.htaccess Executable file
View file

@ -0,0 +1,3 @@
<IfModule mod_autoindex.c>
IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t *.php
</IfModule>

View file

@ -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

View file

@ -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

View file

@ -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

208
ext/spl/tests/array_read.phpt Executable file
View file

@ -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

184
ext/spl/tests/foreach.phpt Executable file
View file

@ -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

115
ext/spl/tests/forward.phpt Executable file
View file

@ -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

138
ext/spl/tests/sequence.phpt Executable file
View file

@ -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