Skip to content

Commit

Permalink
PHP-8.0 support
Browse files Browse the repository at this point in the history
  • Loading branch information
gnat42 committed Jan 12, 2021
1 parent 26cbf1e commit f232a33
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 53 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ php:
- 7.1
- 7.2
- 7.3
- nightly # doesn't work yet on PHP 7.4! not building.
- 7.4
- 8.0
- nightly


# setting the env is the easiest, because it will mix with all the separate php versions (travis does this)
Expand Down
13 changes: 10 additions & 3 deletions zend/callable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
#else
// Sanity check
assert(info[argc].type != 0 && info[argc].name == nullptr);

// the callable we are retrieving
#if PHP_VERSION_ID < 80000
Callable *callable = reinterpret_cast<Callable*>(info[argc].type);
#else
Callable *callable = reinterpret_cast<Callable*>(info[argc].type.ptr);
#endif
#endif

// check if sufficient parameters were passed (for some reason this check
Expand Down Expand Up @@ -99,10 +102,10 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int
_argv[_argc + 1].class_name = reinterpret_cast<const char*>(this);
#else
// @todo this is broken. the zend engine, from 7.2 onwards copies over
// the struct and slices of the last element, because the num_args
// the struct and slices off the last element, because the num_args
// is incorrect in their view. another place to put this may be
// hiding it behind the fname
_argv[_argc + 1].type = reinterpret_cast<zend_type>(this);
// _argv[_argc + 1].type = reinterpret_cast<zend_type>(this);
#endif

// we use our own invoke method, which does a lookup
Expand All @@ -129,8 +132,10 @@ void Callable::initialize(zend_internal_function_info *info, const char *classna
{
// initialize all common elements
info->required_num_args = _required;
#if PHP_VERSION_ID < 80000
info->return_reference = false;
info->_is_variadic = false;
#endif

// the structure has been slightly altered since php7.2
#if PHP_VERSION_ID < 70200
Expand All @@ -147,10 +152,12 @@ void Callable::initialize(zend_internal_function_info *info, const char *classna
#else
// the properties that are available on php 7.2 and higher
info->required_num_args = _required;
#if PHP_VERSION_ID < 80000
info->return_reference = false;
info->_is_variadic = false;
info->type = ZEND_TYPE_ENCODE((int)_return, true);
#endif
#endif
}

/**
Expand Down
25 changes: 22 additions & 3 deletions zend/callable.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@ class Callable

// initialize all elements to null
_argv[i].name = nullptr;
#if PHP_VERSION_ID < 80000
_argv[i].is_variadic = false;
_argv[i].pass_by_reference = false;

#endif
// initialize the extra argument prior to 7.2
#if PHP_VERSION_ID < 70200
_argv[i].class_name = nullptr;
#else
#elif PHP_VERSION_ID < 80000
_argv[i].type = 0;
#endif
}
Expand Down Expand Up @@ -198,6 +199,23 @@ class Callable
case Type::Object: info->type_hint = IS_OBJECT; break; // must be an object of the given classname
case Type::Callable: info->type_hint = IS_CALLABLE; break; // anything that can be invoked
default: info->type_hint = IS_UNDEF; break; // if not specified we allow anything
#elif PHP_VERSION_ID >= 80000
case Type::Undefined: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_UNDEF, arg.allowNull(), 0); break; // undefined means we'll accept any type
case Type::Null: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_UNDEF, arg.allowNull(), 0); break; // this is likely an error, what good would accepting NULL be? accept anything
case Type::False: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), 0); break; // accept true as well ;)
case Type::True: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), 0); break; // accept false as well
case Type::Bool: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), 0); break; // any bool will do, true, false, the options are limitless
case Type::Numeric: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, arg.allowNull(), 0); break; // accept integers here
case Type::Float: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_DOUBLE, arg.allowNull(), 0); break; // floating-point values welcome too
case Type::String: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_STRING, arg.allowNull(), 0); break; // accept strings, should auto-cast objects with __toString as well
case Type::Array: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_ARRAY, arg.allowNull(), 0); break; // array of anything (individual members cannot be restricted)
case Type::Object: // if there is a classname and the argument is not nullable, it's simply the classname
if (!arg.classname()) info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_OBJECT, arg.allowNull(), 0);
// else info->type = (zend_type)arg.encoded();
break;
case Type::Callable: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_CALLABLE, arg.allowNull(), 0); break; // anything that can be invoke

default: info->type = ZEND_TYPE_INIT_CODE(IS_UNDEF, 0, 0); break; // if not specified we allow anything
#else
case Type::Undefined: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // undefined means we'll accept any type
case Type::Null: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // this is likely an error, what good would accepting NULL be? accept anything
Expand All @@ -216,7 +234,7 @@ class Callable
default: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // if not specified we allow anything
#endif
}

#if PHP_VERSION_ID < 80000
// from PHP 5.6 and onwards, an is_variadic property can be set, this
// specifies whether this argument is the first argument that specifies
// the type for a variable length list of arguments. For now we only
Expand All @@ -225,6 +243,7 @@ class Callable

// whether or not to pass the argument by reference
info->pass_by_reference = arg.byReference();
#endif
}

/**
Expand Down
70 changes: 55 additions & 15 deletions zend/classimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *
* @param object_ptr
* @return int
*/
int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr)
int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only)
{
// it is really unbelievable how the Zend engine manages to implement every feature
// in a complete different manner. You would expect the __invoke() and the
Expand Down Expand Up @@ -314,7 +314,11 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct

// store pointer to ourselves (note that the entry_ptr is useless
// inside this function as it is always uninitialized for some reason)
#if PHP_VERSION_ID < 80000
data->self = self(Z_OBJCE_P(object));
#else
data->self = self(object->ce);
#endif

// assign this dynamically allocated variable to the func parameter
// the cast is ok, because zend_internal_function is a member of the
Expand All @@ -323,7 +327,11 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct

// the object_ptr should be filled with the object on which the method is
// called (otherwise the Zend engine tries to call the method statically)
#if PHP_VERSION_ID < 80000
*object_ptr = Z_OBJ_P(object);
#else
*object_ptr = object;
#endif

// done
return SUCCESS;
Expand Down Expand Up @@ -372,7 +380,11 @@ zend_object_handlers *ClassImpl::objectHandlers()
_handlers.cast_object = &ClassImpl::cast;

// method to compare two objects
#if PHP_VERSION_ID < 80000
_handlers.compare_objects = &ClassImpl::compare;
#else
_handlers.compare = &ClassImpl::compare;
#endif

// set the offset between our class implementation and
// the zend_object member in the allocated structure
Expand Down Expand Up @@ -425,10 +437,17 @@ int ClassImpl::compare(zval *val1, zval *val2)
catch (const NotImplemented &exception)
{
// it was not implemented, do we have a default?
#if PHP_VERSION_ID < 80000
if (!std_object_handlers.compare_objects) return 1;

// call default
return std_object_handlers.compare_objects(val1, val2);
#else
if (!std_object_handlers.compare) return 1;

// call default
return std_object_handlers.compare(val1, val2);
#endif
}
catch (Throwable &throwable)
{
Expand All @@ -447,14 +466,17 @@ int ClassImpl::compare(zval *val1, zval *val2)
* @param type
* @return int
*/
int ClassImpl::cast(zval *val, zval *retval, int type)
int ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type)
{
// get the base c++ object
Base *object = ObjectImpl::find(val)->object();

// retrieve the class entry linked to this object
#if PHP_VERSION_ID < 80000
auto *entry = Z_OBJCE_P(val);

#else
auto *entry = val->ce;
#endif
// we need the C++ class meta-information object
ClassBase *meta = self(entry)->_base;

Expand Down Expand Up @@ -507,11 +529,14 @@ int ClassImpl::cast(zval *val, zval *retval, int type)
* @param val The object to be cloned
* @return zend_object The object to be created
*/
zend_object *ClassImpl::cloneObject(zval *val)
zend_object *ClassImpl::cloneObject(ZEND_OBJECT_OR_ZVAL val)
{
// retrieve the class entry linked to this object
#if PHP_VERSION_ID < 80000
auto *entry = Z_OBJCE_P(val);

#else
auto *entry = val->ce;
#endif
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
ClassBase *meta = impl->_base;
Expand Down Expand Up @@ -553,7 +578,7 @@ zend_object *ClassImpl::cloneObject(zval *val)
* @param count
* @return int
*/
int ClassImpl::countElements(zval *object, zend_long *count)
int ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count)
{
// does it implement the countable interface?
Countable *countable = dynamic_cast<Countable*>(ObjectImpl::find(object)->object());
Expand Down Expand Up @@ -601,7 +626,7 @@ int ClassImpl::countElements(zval *object, zend_long *count)
* @param rv Pointer to where to store the data
* @return zval
*/
zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv)
zval *ClassImpl::readDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, int type, zval *rv)
{
// what to do with the type?
//
Expand Down Expand Up @@ -663,7 +688,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv)
* @param value The new value
* @return zval
*/
void ClassImpl::writeDimension(zval *object, zval *offset, zval *value)
void ClassImpl::writeDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, zval *value)
{
// does it implement the arrayaccess interface?
ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object)->object());
Expand Down Expand Up @@ -704,7 +729,7 @@ void ClassImpl::writeDimension(zval *object, zval *offset, zval *value)
* @param check_empty Was this an isset() call, or an empty() call?
* @return bool
*/
int ClassImpl::hasDimension(zval *object, zval *member, int check_empty)
int ClassImpl::hasDimension(ZEND_OBJECT_OR_ZVAL object, zval *member, int check_empty)
{
// does it implement the arrayaccess interface?
ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object)->object());
Expand Down Expand Up @@ -753,7 +778,7 @@ int ClassImpl::hasDimension(zval *object, zval *member, int check_empty)
* @param object The object on which it is called
* @param member The member to remove
*/
void ClassImpl::unsetDimension(zval *object, zval *member)
void ClassImpl::unsetDimension(ZEND_OBJECT_OR_ZVAL object, zval *member)
{
// does it implement the arrayaccess interface?
ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object)->object());
Expand Down Expand Up @@ -834,7 +859,7 @@ zval *ClassImpl::toZval(Value &&value, int type, zval *rv)
* @param rv Pointer to where to store the data
* @return val
*/
zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_slot, zval *rv)
zval *ClassImpl::readProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, int type, void **cache_slot, zval *rv)
{
// what to do with the type?
//
Expand All @@ -857,7 +882,11 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
Base *base = ObjectImpl::find(object)->object();

// retrieve the class entry linked to this object
#if PHP_VERSION_ID < 80000
auto *entry = Z_OBJCE_P(object);
#else
auto *entry = object->ce;
#endif

// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
Expand Down Expand Up @@ -915,13 +944,17 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
* @param cache_slot The cache slot used
* @return zval
*/
PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cache_slot)
PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, zval *value, void **cache_slot)
{
// retrieve the object and class
Base *base = ObjectImpl::find(object)->object();

// retrieve the class entry linked to this object
#if PHP_VERSION_ID < 80000
auto *entry = Z_OBJCE_P(object);
#else
auto *entry = object->ce;
#endif

// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
Expand Down Expand Up @@ -1006,7 +1039,7 @@ PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(zval *object, zval *name, z
* @param cache_slot The cache slot used
* @return bool
*/
int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **cache_slot)
int ClassImpl::hasProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, int has_set_exists, void **cache_slot)
{
// the default implementation throws an exception, if we catch that
// we know for sure that the user has not overridden the __isset method
Expand All @@ -1016,7 +1049,11 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **
Base *base = ObjectImpl::find(object)->object();

// retrieve the class entry linked to this object
#if PHP_VERSION_ID < 80000
auto *entry = Z_OBJCE_P(object);
#else
auto *entry = object->ce;
#endif

// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
Expand Down Expand Up @@ -1070,15 +1107,18 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **
* @param member The member to remove
* @param cache_slot The cache slot used
*/
void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot)
void ClassImpl::unsetProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL member, void **cache_slot)
{
// the default implementation throws an exception, if we catch that
// we know for sure that the user has not overridden the __unset method
try
{
// retrieve the class entry linked to this object
#if PHP_VERSION_ID < 80000
auto *entry = Z_OBJCE_P(object);

#else
auto *entry = object->ce;
#endif
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);

Expand Down
Loading

0 comments on commit f232a33

Please sign in to comment.