Skip to content

Commit

Permalink
New feature: it now is also possible to call the $object->getIterator…
Browse files Browse the repository at this point in the history
…() method explicitly when a C++ class implements the Traversable interface + this fixes the issue that in PHP 8 environments an error was reported that all Traversable objects could not be instantiated because they were abstract
  • Loading branch information
EmielBruijntjes committed Apr 20, 2024
1 parent cfc3460 commit f9fe7a9
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
*.d
*.o
*.a
*.so
*.txt
*.a.*
*.so.*
build/
.vscode/
.vscode/
10 changes: 9 additions & 1 deletion include/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Base class for defining your own objects
*
* @author Emiel Bruijntjes <[email protected]>
* @copyright 2013 - 2022 Copernica BV
* @copyright 2013 - 2024 Copernica BV
*/

/**
Expand Down Expand Up @@ -285,6 +285,14 @@ class PHPCPP_EXPORT Base
*/
Php::Value __count(Php::Parameters &params);

/**
* Method that is called when an explicit call to $object->getIterator() is
* made, AND that is called when running on PHP 8 and higher (because somehow
* the get_iterator function is skipped by PHP 8 for non-internal classes)?
* @return Php::Value
*/
Php::Value __getIterator();


private:
/**
Expand Down
16 changes: 15 additions & 1 deletion zend/base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Implementation file for the base of all classes
*
* @copyright 2014 - 2022 Copernica BV
* @copyright 2014 - 2024 Copernica BV
*/
#include "includes.h"

Expand Down Expand Up @@ -280,6 +280,20 @@ Php::Value Base::__count(Php::Parameters &params)
return countable->count();
}

/**
* Method that is called when an explicit call to $object->getIterator() is
* made, AND that is called when running on PHP 8 and higher (because somehow
* the get_iterator function is skipped by PHP 8 for non-internal classes)?
* @return Php::Value
*/
Php::Value Base::__getIterator()
{
// because the object is already implicitly iterable because deep inside PHP knows that
// the "Traversable" method is implemented, we can simply return ourselves (which will
// end up that the code in ClassImpl::getIterator() will be called)
return this;
}

/**
* End namespace
*/
Expand Down
20 changes: 19 additions & 1 deletion zend/classimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,13 @@ const struct _zend_function_entry *ClassImpl::entries()
if (!hasMethod("serialize")) entrycount += 1;
if (!hasMethod("unserialize")) entrycount += 1;
}

// if the class is iterable, we might need some extra methods
if (_base->traversable())
{
// add the getIterator method if the class does not have one defined yet
if (!hasMethod("getIterator")) entrycount += 1;
}

// allocate memory for the functions
_entries = new zend_function_entry[entrycount + 1];
Expand Down Expand Up @@ -1432,7 +1439,7 @@ const struct _zend_function_entry *ClassImpl::entries()
// if the class is serializable, we might need some extra methods
if (_base->serializable())
{
// the method objectneed to stay in scope for the lifetime of the script (because the register a pointer
// the method object need to stay in scope for the lifetime of the script (because the register a pointer
// to an internal string buffer) -- so we create them as static variables
static Method serialize("serialize", &Base::__serialize, 0, {});
static Method unserialize("unserialize", &Base::__unserialize, 0, { ByVal("input", Type::Undefined, true) });
Expand All @@ -1441,6 +1448,17 @@ const struct _zend_function_entry *ClassImpl::entries()
if (!hasMethod("serialize")) serialize.initialize(&_entries[i++], _name);
if (!hasMethod("unserialize")) unserialize.initialize(&_entries[i++], _name);
}

// if the class is traverable, we might need extra methods too (especially on php 8.1, maybe also 8.0?)
if (_base->traversable())
{
// the method object need to stay in scope for the lifetime of the script (because the register a pointer
// to an internal string buffer) -- so we create them as static variables
static Method getIterator("getIterator", &Base::__getIterator, 0, {});

// register the serialize and unserialize method in case this was not yet done in PHP user space
if (!hasMethod("getIterator")) getIterator.initialize(&_entries[i++], _name);
}

// last entry should be set to all zeros
zend_function_entry *last = &_entries[i];
Expand Down

0 comments on commit f9fe7a9

Please sign in to comment.