Skip to content

Commit f9fe7a9

Browse files
New feature: it now is also possible to call the $object->getIterator() 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
1 parent cfc3460 commit f9fe7a9

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
*.d
12
*.o
23
*.a
34
*.so
45
*.txt
56
*.a.*
67
*.so.*
78
build/
8-
.vscode/
9+
.vscode/

include/base.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Base class for defining your own objects
33
*
44
* @author Emiel Bruijntjes <[email protected]>
5-
* @copyright 2013 - 2022 Copernica BV
5+
* @copyright 2013 - 2024 Copernica BV
66
*/
77

88
/**
@@ -285,6 +285,14 @@ class PHPCPP_EXPORT Base
285285
*/
286286
Php::Value __count(Php::Parameters &params);
287287

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

289297
private:
290298
/**

zend/base.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Implementation file for the base of all classes
55
*
6-
* @copyright 2014 - 2022 Copernica BV
6+
* @copyright 2014 - 2024 Copernica BV
77
*/
88
#include "includes.h"
99

@@ -280,6 +280,20 @@ Php::Value Base::__count(Php::Parameters &params)
280280
return countable->count();
281281
}
282282

283+
/**
284+
* Method that is called when an explicit call to $object->getIterator() is
285+
* made, AND that is called when running on PHP 8 and higher (because somehow
286+
* the get_iterator function is skipped by PHP 8 for non-internal classes)?
287+
* @return Php::Value
288+
*/
289+
Php::Value Base::__getIterator()
290+
{
291+
// because the object is already implicitly iterable because deep inside PHP knows that
292+
// the "Traversable" method is implemented, we can simply return ourselves (which will
293+
// end up that the code in ClassImpl::getIterator() will be called)
294+
return this;
295+
}
296+
283297
/**
284298
* End namespace
285299
*/

zend/classimpl.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,13 @@ const struct _zend_function_entry *ClassImpl::entries()
14011401
if (!hasMethod("serialize")) entrycount += 1;
14021402
if (!hasMethod("unserialize")) entrycount += 1;
14031403
}
1404+
1405+
// if the class is iterable, we might need some extra methods
1406+
if (_base->traversable())
1407+
{
1408+
// add the getIterator method if the class does not have one defined yet
1409+
if (!hasMethod("getIterator")) entrycount += 1;
1410+
}
14041411

14051412
// allocate memory for the functions
14061413
_entries = new zend_function_entry[entrycount + 1];
@@ -1432,7 +1439,7 @@ const struct _zend_function_entry *ClassImpl::entries()
14321439
// if the class is serializable, we might need some extra methods
14331440
if (_base->serializable())
14341441
{
1435-
// the method objectneed to stay in scope for the lifetime of the script (because the register a pointer
1442+
// the method object need to stay in scope for the lifetime of the script (because the register a pointer
14361443
// to an internal string buffer) -- so we create them as static variables
14371444
static Method serialize("serialize", &Base::__serialize, 0, {});
14381445
static Method unserialize("unserialize", &Base::__unserialize, 0, { ByVal("input", Type::Undefined, true) });
@@ -1441,6 +1448,17 @@ const struct _zend_function_entry *ClassImpl::entries()
14411448
if (!hasMethod("serialize")) serialize.initialize(&_entries[i++], _name);
14421449
if (!hasMethod("unserialize")) unserialize.initialize(&_entries[i++], _name);
14431450
}
1451+
1452+
// if the class is traverable, we might need extra methods too (especially on php 8.1, maybe also 8.0?)
1453+
if (_base->traversable())
1454+
{
1455+
// the method object need to stay in scope for the lifetime of the script (because the register a pointer
1456+
// to an internal string buffer) -- so we create them as static variables
1457+
static Method getIterator("getIterator", &Base::__getIterator, 0, {});
1458+
1459+
// register the serialize and unserialize method in case this was not yet done in PHP user space
1460+
if (!hasMethod("getIterator")) getIterator.initialize(&_entries[i++], _name);
1461+
}
14441462

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

0 commit comments

Comments
 (0)