RIP TSRMLS_CC

Adam Harvey

@LGnome

API churn

  • 333 functions/macros removed
  • 410 functions/macros added

API churn

  • 516 out of 990 functions/macros differ

What do?

  • One way migration
  • Separate branches
  • Shared codebase
  • Ignore PHP 7 and hope it goes away

Advantages

  • One time cost
  • Conceptually simple
  • Cleaner code

Disadvantages

  • You no longer support PHP 5

Advantages

  • Both versions are supported
  • Able to write idiomatic code for both versions

Disadvantages

  • Maintenance is a pain
  • Potentially double the work
  • Branches may diverge over time

Advantages

  • Both versions are supported
  • Less maintenance overhead
  • Happy users

Disadvantages

  • So many API changes

zval


typedef struct {
	zend_uchar type;
	zend_uint  refcount__gc;
	zend_uchar is_ref__gc;
	union {
		long lval;
		double dval;
		struct {
			char *val;
			int len;
		} str;
		HashTable *ht;
		zend_object_value obj;
	} value;
} zval;
						

typedef struct {
	zend_value value;
	union {
		struct {
			zend_uchar type;
			...
		}
	}
} zval;
						

IS_LONG

PHP 5


long lval;
						

PHP 7


zend_long lval;

#if defined(__LP64__) || defined(_LP64) || defined(_WIN64)
typedef int64_t zend_long;
#else
typedef int32_t zend_long;
#endif
						

IS_BOOL

PHP 5


long lval;
						

PHP 7


#define IS_FALSE 2
#define IS_TRUE  3

ZEND_API int zend_is_true(zval *op);
						

IS_STRING

PHP 5


struct {
	char *val;
	int   len;
} str;
						

PHP 7


zend_string *str;
						

typedef struct {
	zend_refcounted_h gc;
	zend_ulong        h;
	size_t            len;
	char              val[1];
} zend_string;
						

zend_string *zend_string_alloc(size_t len, int persistent);
zend_string *zend_string_init(const char *s, size_t len, int pers);
zend_string *zend_string_dup(zend_string *s, int persistent);
void zend_string_release(zend_string *s);

uint32_t zend_string_addref(zend_string *s);
uint32_t zend_string_delref(zend_string *s);

#define ZSTR_VAL(zstr)  (zstr)->val
#define ZSTR_LEN(zstr)  (zstr)->len
						

PHP 5


RETURN_STRING(str, duplicate)
ZVAL_STRING(zv, str, duplicate)
add_assoc_string(zv, key, str, duplicate)
						

PHP 7


RETURN_STRING(str)
ZVAL_STRING(zv, str)
add_assoc_string(zv, key, str)
						

#if ZEND_MODULE_API_NO < 20151012

#undef ZVAL_STRING
#define ZVAL_STRING(zv, str) do {             \
	const char *__s = (s);                \
	int __l = strlen(str);                \
	zval *__z = (zv);                     \
	Z_STRLEN_P(__z) = l;                  \
	Z_STRVAL_P(__z) = estrndup(__s, __l); \
} while (0);

#endif
						

IS_OBJECT

PHP 5


typedef struct {
	zend_object_handle handle;
	const zend_object_handlers *handlers;
} zend_object_value;

zend_object_value obj;
						

PHP 7


zend_object *obj;
						

typedef struct {
	zend_refcounted_h           gc;
	uint32_t                    handle;
	zend_class_entry           *ce;
	const zend_object_handlers *handlers;
	HashTable                  *properties;
	zval                        properties_table[1];
} zend_object;
						

IS_RESOURCE

PHP 5


long lval;
						

PHP 7


zend_resource *res;
						

typedef struct {
	zend_refcounted_h gc;
	int               handle;
	int               type;
	void             *ptr;
} zend_resource;
						

#define IS_UNDEF     0

#define IS_REFERENCE 10
zend_reference *ref;
						

PHP 5


char *str;
int len;

zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len);
						

PHP 7


char *str;
size_t len;

zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len);
						

#if ZEND_MODULE_API_NO >= 20151012
typedef size_t zend_string_len_t;
#else
typedef int zend_string_len_t;
#endif

char *str;
zend_string_len_t len;
zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len);
						

#if ZEND_MODULE_API_NO < 20151012
typedef long zend_long;
#endif

zend_long l;
zend_parse_parameters(ZEND_NUM_ARGS(), "l", &l);
						

PHP 5


zend_hash_update(ht, "key", 4, &zv, sizeof(zval *), NULL);
zend_hash_find(ht, "key", 4, &zv);
						

PHP 7


zend_string *key = zend_string_init("key", 3, 0);

zend_hash_update(ht, key, zv);
zv = zend_hash_find(ht, key);

zend_string_release(key);
						

IS_PTR


zend_string *key;
my_struct *ptr;

zend_hash_update_ptr(ht, key, ptr);
ptr = (my_struct *) zend_hash_find_ptr(ht, key);
						

zval *
compat_zend_hash_find(HashTable *ht, const char *key, size_t len) {
#if ZEND_MODULE_API_NO >= 20151012
	zend_string *zs = zend_string_init(key, len);
	zval *val = zend_hash_find(ht, zs);
	zend_string_release(zs);
	return val;
#else
	zval *val = NULL;
	int res = zend_hash_find(ht, key, len + 1, &val);
	return (res == SUCCESS) ? val : NULL;
#endif
}
						

HashPosition pos;
ulong num_key;
char *key;
uint key_len;
zval **zv_pp;

zend_hash_internal_pointer_reset_ex(&ht, &pos);
while (zend_hash_get_current_data_ex(&ht, &zv_pp, &pos)
       == SUCCESS) {
	if (zend_hash_get_current_key_ex(&ht, &key, &key_len,
	                                 &num_key, 0, &pos) ==
	                                 HASH_KEY_IS_STRING) {
		...
	}
}
						

ulong num_key;
zend_string *key;
zval *zv;

ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, key, val) {
	if (key) {
		...
	}
}
						

PHP 5


int zend_register_resource(zval *zv, void *ptr, int type);
void *zend_fetch_resource(zval **id, int default_id,
                          const char *name, int type,
													int num_types, ...);
int zend_list_delete(int id);
						

PHP 7


zend_resource *zend_register_resource(void *ptr, int type);
void *zend_fetch_resource(zend_resource *res, const char *name,
                          int type);
void *zend_fetch_resource_ex(zval *res, const char *name,
                             int type);
int zend_list_close(zend_resource *res);
						

#if ZEND_MODULE_API_NO >= 20151012
#define ZEND_REGISTER_RESOURCE(zv, ptr, type) \
	ZVAL_RES(zv, zend_register_resource(ptr, type));

#define ZEND_FETCH_RESOURCE(zv, type, id, default_id, name, type) \
	zend_fetch_resource_ex(zv, name, type);

#define ZEND_CLOSE_RESOURCE(zv) \
	zend_list_close(Z_RES_P(zv))
#else
#define ZEND_CLOSE_RESOURCE(zv) \
	zend_list_delete(Z_LVAL_P(zv))
#endif
						

PHP 5


zval *zv;

zv = zend_read_property(ce, obj, name, strlen(name), 0);
						

PHP 7


zval rv;
zval *zv;

zv = zend_read_property(ce, obj, name, strlen(name), 0, &rv);
						

zval *
compat_zend_read_property(zend_class_entry *ce, zval *obj,
                          const char *name, int name_length,
                          int silent, zval *rv TSRMLS_DC) {
#if ZEND_MODULE_API_NO >= 20151012
	return zend_read_property(ce, obj, name, name_length,
	                          silent, rv);
#else
	(void) rv;
	return zend_read_property(ce, obj, name, name_length,
	                          silent TSRMLS_CC);
#endif
}
						

PHP 5


typedef struct {
	zend_object std;
	my_struct *struct;
} my_object;

zend_object_value my_object_new(zend_class_entry *ce TSRMLS_DC) {
	my_object *intern;
	zend_object_value retval;

	intern = emalloc(sizeof(my_object));
	/* ... */
	retval.handle = zend_objects_store_put(intern,
		zend_objects_destroy_object, my_object_free, NULL TSRMLS_CC);
	return retval;
}
						

PHP 5


typedef struct {
	zend_object std;
	my_struct *struct;
} my_object;

my_object *my_object_get(zval *zv TSRMLS_DC) {
	return (my_object *)
		zend_object_store_get_object(zv TSRMLS_CC);
}
						

PHP 7


typedef struct {
	my_struct *struct;
	zend_object std;
} my_object;

zend_object *my_object_new(zend_class_entry *ce) {
	struct my_object *intern;

	intern = emalloc(sizeof(my_object) +
	                 zend_objects_properties_size(ce));
	/* ... */
	return &intern->std;
}
						

PHP 7


typedef struct {
	my_struct *struct;
	zend_object std;
} my_object;

my_object *my_object_get(zval *zv) {
	zend_object *obj = Z_OBJ_P(zv);

	return (my_object *)
		((char *)(obj) - XtOffsetOf(my_object, std));
}
						

PHP 7


zend_class_entry *my_object_ce;
zend_object_handlers my_object_handlers;

PHP_MINIT_FUNCTION(my_extension) {
	zend_class_entry ce;

	INIT_CLASS_ENTRY(ce, "My\\Object", NULL);
	my_object_ce = zend_register_internal_class_ex(&ce, NULL);

	memcpy(&my_object_handlers, &std_object_handlers,
	       sizeof(zend_object_handlers));
	my_object_handlers.offset = XtOffsetOf(my_object, std);
}
						
  • TSRMLS_C
  • TSRMLS_CC
  • TSRMLS_D
  • TSRMLS_DC
  • TSRMLS_FETCH

phpng-upgrading

https://wiki.php.net/phpng-upgrading

LXR

http://lxr.php.net/

git diff PHP-5.6..PHP-7.0 ext/gmp

pecl-compat

https://github.com/flaupretre/pecl-compat/

Thank you!

Questions?

https://joind.in/talk/fc496

@LGnome

Slides: https://lawngnome.github.io/rip-tsrmls-cc/

Image credits