Skip to content

Commit

Permalink
Provide a separate way to init object properties to initial values
Browse files Browse the repository at this point in the history
We used the same function to change properties of an object:

  1. let object->property = expression
  2. class A { private object = []; }

And to be able to change object's property we had to use object's scope.
Thus, we have NEVER checked the scope in which we are actually located:

  // Global Scope

  class Bar {
      // Bar's Scope
      private prop;
  }

  class Foo {
      // Foo's Scope

      public function test (Bar bar) {
          // Here we replaced the scope by Bar's one
          let bar->prop = 42;
      }
  }

Fixes zephir-lang#2057

/cc @niden @Jurigag
  • Loading branch information
sergeyklay committed Apr 16, 2020
1 parent 2c9c549 commit aa126bc
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 24 deletions.
15 changes: 15 additions & 0 deletions Library/Backends/ZendEngine3/Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,21 @@ public function updateProperty(Variable $variable, $property, $value, Compilatio
return;
}

/* Are we going to init default object property value? */
if ($context->currentMethod &&
preg_match('/^zephir_init_properties/', $context->currentMethod->getName())) {
$context->codePrinter->output(
sprintf(
'zephir_init_property_zval(%s, ZEND_STRL("%s"), %s);',
$this->getVariableCode($variable),
$property,
$value
)
);

return;
}

$context->codePrinter->output(
sprintf(
'zephir_update_property_zval(%s, ZEND_STRL("%s"), %s);',
Expand Down
117 changes: 93 additions & 24 deletions kernels/ZendEngine3/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,10 @@ int zephir_isset_property_zval(zval *object, const zval *property)
return 0;
}

static inline zend_class_entry *zephir_lookup_class_ce(zend_class_entry *ce, const char *property_name, unsigned int property_length)
{
static inline
zend_class_entry *zephir_lookup_class_ce(zend_class_entry *ce,
const char *property_name,
unsigned int property_length) {
zend_class_entry *original_ce = ce;
zend_property_info *info;

Expand All @@ -455,6 +457,7 @@ static inline zend_class_entry *zephir_lookup_class_ce(zend_class_entry *ce, con
#endif
ce = ce->parent;
}

return original_ce;
}

Expand Down Expand Up @@ -580,40 +583,43 @@ int zephir_read_property_zval(zval *result, zval *object, zval *property, int fl
}

/**
* Checks whether obj is an object and updates property with another zval
* Checks whether obj is an object and updates property with another zval.
*
* This function is intended to set initial values to object properties.
* Do not use it for a regular property updating.
*/
int zephir_update_property_zval(zval *object, const char *property_name, unsigned int property_length, zval *value)
int zephir_init_property_zval(zval *object, const char *property_name,
unsigned int property_length, zval *value)
{
zend_class_entry *ce, *old_scope;
zend_class_entry *ce, *scope;
zval property, sep_value;

#if PHP_VERSION_ID >= 70100
old_scope = EG(fake_scope);
#else
old_scope = EG(scope);
#endif

if (Z_TYPE_P(object) != IS_OBJECT) {
php_error_docref(NULL, E_WARNING, "Attempt to assign property of non-object");
php_error_docref(NULL, E_WARNING,
"Attempt to assign property '%s' of non-object",
property_name);
return FAILURE;
}

/* Backup current scope */
scope = zephir_get_scope(0);
ce = Z_OBJCE_P(object);

/* Lookup real property owner */
if (ce->parent) {
ce = zephir_lookup_class_ce(ce, property_name, property_length);
}

#if PHP_VERSION_ID >= 70100
EG(fake_scope) = ce;
#else
EG(scope) = ce;
#endif
/* Use caller's scope */
zephir_set_scope(ce);

if (!Z_OBJ_HT_P(object)->write_property) {
const char *class_name;

class_name = Z_OBJ_P(object) ? ZSTR_VAL(Z_OBJCE_P(object)->name) : "";
zend_error(E_CORE_ERROR, "Property %s of class %s cannot be updated", property_name, class_name);
zend_error(E_CORE_ERROR,
"Property %s of class %s cannot be updated",
property_name, class_name);
}

ZVAL_STRINGL(&property, property_name, property_length);
Expand All @@ -627,15 +633,78 @@ int zephir_update_property_zval(zval *object, const char *property_name, unsigne
}
}

/* write_property will add 1 to refcount, so no Z_TRY_ADDREF_P(value); is necessary */
/* write_property will add 1 to refcount,
so no Z_TRY_ADDREF_P(value) is necessary */
Z_OBJ_HT_P(object)->write_property(object, &property, &sep_value, 0);
zval_ptr_dtor(&property);

#if PHP_VERSION_ID >= 70100
EG(fake_scope) = old_scope;
#else
EG(scope) = old_scope;
#endif
/* Restore original scope */
zephir_set_scope(scope);

return SUCCESS;
}

/**
* Checks whether obj is an object and updates property with another zval
*/
int zephir_update_property_zval(zval *object, const char *property_name,
unsigned int property_length, zval *value)
{
zend_class_entry *ce, *scope;
zval property, sep_value;

if (Z_TYPE_P(object) != IS_OBJECT) {
php_error_docref(NULL, E_WARNING,
"Attempt to assign property '%s' of non-object",
property_name);
return FAILURE;
}

/* Backup current scope */
scope = zephir_get_scope(0);

/* Is this function was called from within caller scope */
ce = zend_get_called_scope(EG(current_execute_data));
if (UNEXPECTED(!ce)) {
ce = Z_OBJCE_P(object);
}

/* Lookup real property owner */
if (ce->parent) {
ce = zephir_lookup_class_ce(ce, property_name, property_length);
}

/* Use caller's scope */
zephir_set_scope(ce);

if (!Z_OBJ_HT_P(object)->write_property) {
const char *class_name;

class_name = Z_OBJ_P(object) ? ZSTR_VAL(Z_OBJCE_P(object)->name) : "";
zend_error(E_CORE_ERROR,
"Property %s of class %s cannot be updated",
property_name, class_name);
}

ZVAL_STRINGL(&property, property_name, property_length);
ZVAL_COPY_VALUE(&sep_value, value);
if (Z_TYPE(sep_value) == IS_ARRAY) {
ZVAL_ARR(&sep_value, zend_array_dup(Z_ARR(sep_value)));
if (EXPECTED(!(GC_FLAGS(Z_ARRVAL(sep_value)) & IS_ARRAY_IMMUTABLE))) {
if (UNEXPECTED(GC_REFCOUNT(Z_ARR(sep_value)) > 0)) {
GC_DELREF(Z_ARR(sep_value));
}
}
}

/* write_property will add 1 to refcount,
so no Z_TRY_ADDREF_P(value) is necessary */
Z_OBJ_HT_P(object)->write_property(object, &property, &sep_value, 0);
zval_ptr_dtor(&property);

/* Restore original scope */
zephir_set_scope(scope);

return SUCCESS;
}

Expand Down
10 changes: 10 additions & 0 deletions kernels/ZendEngine3/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
#include "kernel/globals.h"
#include "kernel/main.h"

/* Working with scopes */
#if PHP_VERSION_ID >= 70100
# define zephir_get_scope(e) ((e) ? zend_get_executed_scope() : EG(fake_scope))
# define zephir_set_scope(s) EG(fake_scope) = (s)
#else
# define zephir_get_scope(e) EG(scope)
# define zephir_set_scope(s) EG(scope) = (s)
#endif

/** Class Retrieving/Checking */
int zephir_class_exists(zval *class_name, int autoload);
int zephir_interface_exists(zval *interface_name, int autoload);
Expand Down Expand Up @@ -49,6 +58,7 @@ int zephir_fetch_property(zval *result, zval *object, const char *property_name,
int zephir_fetch_property_zval(zval *result, zval *object, zval *property, int silent);

/** Updating properties */
int zephir_init_property_zval(zval *obj, const char *property_name, unsigned int property_length, zval *value);
int zephir_update_property_zval(zval *obj, const char *property_name, unsigned int property_length, zval *value);
int zephir_update_property_zval_zval(zval *obj, zval *property, zval *value);

Expand Down

0 comments on commit aa126bc

Please sign in to comment.