Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP Extensions - Still Alive and Full of Hidden...

PHP Extensions - Still Alive and Full of Hidden Powers

With PHP gaining more and more features, are PHP extensions still necessary? Beyond just accessing system libraries, extensions unlock capabilities that pure PHP code still can’t replicate. Imagine defining custom behaviors for operators, enabling intuitive syntax through operator overloading, or tailoring equality checks with flexible object comparisons—these are things only possible in an extension. Extensions even allow objects to be cast to scalar types like int or string, adding versatility to how they’re used. In this talk, we’ll dive into some of the coolest things extensions make possible, pushing PHP beyond its usual boundaries. And yes, we’ll also explore when extensions can be taken a little too far, showing that just because something can be done doesn’t always mean it should be!

alcaeus

April 07, 2025
Tweet

More Decks by alcaeus

Other Decks in Programming

Transcript

  1. ChatGPT o3-mini-high A Cartesian vector is an ordered tuple of

    numbers representing a vector’s components along the mutually perpendicular axes of a Cartesian coordinate system.
  2. X X Y Y Z Z O O x x

    y y z z (x,y,z) (x,y,z)
  3. final readonly class CartesianVector { public function __construct( public float

    $x, public float $y, public float $z, ) {} public function equals(self $vector): bool {} public function add(self $vector): self {} public function subtract(self $vector): self {} public function multiply(float $scalar): self {} public function divide(float $scalar): self {} }
  4. $vector = new CartesianVector(1, 2, 3); $other = new CartesianVector(1,

    2, 3); var_dump($vector->equals($other)); // bool(true)
  5. $vector = new CartesianVector(1, 2, 3); $other = new CartesianVector(1,

    2, 3); var_dump($vector == $other); var_dump($vector === $other); // bool(true) // bool(false)
  6. bool(false) Notice: Object of class CartesianVector could not be converted

    to int bool(false) Notice: Object of class CartesianVector could not be converted to int bool(false)
  7. final readonly class CartesianVector implements Stringable { public function __toString():

    string { return sprintf( '(%f, %f, %f)', $this->x, $this->y, $this->z, ); } }
  8. $vector = new CartesianVector(1, 2, 3); $other = new CartesianVector(2,

    -3, 5); var_dump($vector->add($other)); var_dump($vector->subtract($other)); var_dump($vector->multiply(2)); var_dump($vector->divide(2));
  9. $vector = new CartesianVector(1, 2, 3); $other = new CartesianVector(2,

    -3, 5); var_dump($vector + $other); var_dump($vector - $other); var_dump($vector * 2); var_dump($vector / 2);
  10. zend_class_entry ce, *class_entry; INIT_NS_CLASS_ENTRY( ce, "Showcase\\Math", "CartesianVector", class_CartesianVector_methods ); class_entry

    = zend_register_internal_class_with_flags( &ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_READONLY_CLASS );
  11. ZEND_BEGIN_ARG_INFO_EX( arginfo_class_CartesianVector___construct, 0, 0, 3 ) ZEND_ARG_TYPE_INFO(0, x, IS_DOUBLE, 0)

    ZEND_ARG_TYPE_INFO(0, y, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, z, IS_DOUBLE, 0) ZEND_END_ARG_INFO()
  12. zval property_x_default_value; ZVAL_UNDEF(&property_x_default_value); zend_string *property_x_name = zend_string_init( "x", sizeof("x") -

    1, 1 ); zend_declare_typed_property( class_entry, property_x_name, &property_x_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_DOUBLE) ); zend_string_release(property_x_name);
  13. PHP_METHOD(CartesianVector, __construct) { double x, y, z; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_DOUBLE(x)

    Z_PARAM_DOUBLE(y) Z_PARAM_DOUBLE(z) ZEND_PARSE_PARAMETERS_END(); cartesianvector_update_properties( Z_OBJ_P(getThis()), x, y, z ); }
  14. final readonly class CartesianVector { public float $x; public float

    $y; public float $z; public function __construct( float $x, float $y, float $z, ) {} }
  15. #define CARTESIANVECTOR_COMMON_INIT \ zval* other; \ double x, y, z,

    otherX, otherY, otherZ; \ ZEND_PARSE_PARAMETERS_START(1, 1) \ Z_PARAM_OBJECT_OF_CLASS(other, showcase_cartesianvector_ce) \ ZEND_PARSE_PARAMETERS_END(); \ cartesianvector_read_properties(Z_OBJ_P(getThis()), &x, &y, &z); \ cartesianvector_read_properties(Z_OBJ_P(other), &otherX, &otherY, &otherZ);
  16. const zend_object_handlers std_object_handlers = { // ... zend_std_cast_object_tostring, /* cast_object

    */ NULL, /* do_operation */ zend_std_compare_objects, /* compare */ };
  17. static int cartesianvector_object_do_operation( zend_uchar opcode, zval *result, zval *op1, zval

    *op2 ) { switch (opcode) { case ZEND_ADD: return cartesianvector_add(result, op1, op2); // ... default: return FAILURE; } }
  18. if (Z_TYPE_P(op1) != IS_OBJECT || Z_TYPE_P(op2) != IS_OBJECT || Z_OBJCE_P(op1)

    != cartesianvector_ce || Z_OBJCE_P(op2) != cartesianvector_ce) { return FAILURE; }
  19. static zend_result cartesianvector_cast_object( zend_object *readobj, zval *retval, int type )

    { double magnitude; CARTESIANVECTOR_READ_PROPERTY(readobj, "magnitude", &magnitude); if (type == IS_DOUBLE) { ZVAL_DOUBLE(retval, magnitude); return SUCCESS; } return zend_std_cast_object_tostring( readobj, retval, type ); }
  20. if (Z_TYPE_P(op1) == IS_OBJECT) { return Z_OBJ_HANDLER_P(op1, compare)(op1, op2); }

    else if (Z_TYPE_P(op2) == IS_OBJECT) { return Z_OBJ_HANDLER_P(op2, compare)(op1, op2); }
  21. $vector = new CartesianVector(1, 2, 2); $other = new CartesianVector(0,

    2, 4); var_dump($vector == $other); // bool(false) var_dump($vector > $other); // bool(true)
  22. for (i = 0; i < zobj1->ce->default_properties_count; i++) { zval

    *p1, *p2; int ret; info = zobj1->ce->properties_info_table[i]; p1 = OBJ_PROP(zobj1, info->offset); p2 = OBJ_PROP(zobj2, info->offset); ret = zend_compare(p1, p2); if (ret != 0) { return ret; } } return 0;
  23. $vector = new CartesianVector(1, 2, 2); $other = new CartesianVector(0,

    2, 4); var_dump($vector == $other); // bool(false) var_dump($vector > $other); // bool(false)
  24. $vector = new CartesianVector(1, 2, 2); $other = new CartesianVector(2,

    1, 2); var_dump($vector == $other); // bool(true)
  25. if (cartesianvector_coordinates_are_equal(...) { return 0; } // If magnitudes differ,

    accept that comparison int compare_magnitudes = cartesianvector_compare_magnitudes(object1, object2); if (compare_magnitudes != 0) { return compare_magnitudes; } // Fall back to property comparison for other vectors return zend_std_compare_objects(object1, object2);
  26. !

  27. zend_result zend_call_function(...) { // ... #if ZEND_DEBUG if (!EG(exception) &&

    call->func) { ZEND_ASSERT(zend_verify_internal_return_type(...)); } #endif // ... }
  28. static void showcase_overload_functions(void) { zend_function *orig_function; orig_function = zend_hash_str_find_ptr( CG(function_table),

    ZEND_STRL("hash") ); if (orig_function != NULL) { orig_function->internal_function.handler = showcase_compromised_hash; } }
  29. static void showcase_execute_ex( zend_execute_data *execute_data ) { zend_error(E_NOTICE, "Executing function:

    %s", ...)); orig_zend_execute_ex(execute_data); } static void showcase_replace_execute(void) { orig_zend_execute_ex = zend_execute_ex; zend_execute_ex = showcase_execute_ex; }