# Tracking Object Property Changes

- Thread: https://externals.io/message/113422
- Original archive: https://news-web.php.net/php.internals/113422
- Started by Joseph Montanez on 2021-03-09T02:32:46+00:00
- Messages: 5
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #113422 — Joseph Montanez — 2021-03-09T02:32:46+00:00

URL: https://externals.io/message/113422#113422

I am not sure what to title this but the gist is that I have two structs
with a one way dependency:

// Vector3 type
typedef struct Vector3 {
    float x;
    float y;
    float z;
} Vector3;

// Ray type (useful for raycast)
typedef struct Ray {
    Vector3 position;       // Ray position (origin)
    Vector3 direction;      // Ray direction
} Ray;


I've gone ahead and made a test extension here:
https://github.com/joseph-montanez/php-ext-reference-issue . I've made each
struct a PHP object. allowing me to do this:

<?php
$position = new \skeleton\Vector3(1,2,3);
$direction = new \skeleton\Vector3(4,5,6);

$ray = new \skeleton\Ray($position, $direction);

echo 'Z: ', $ray->position->z, PHP_EOL; // Outputs: 3

$position->z = 9;

echo 'Z: ', $ray->position->z, PHP_EOL; // Outputs: 9

The PHP Ray object is defined as so:
typedef struct _skeleton_ray_object {
    Ray ray;
    HashTable *prop_handler;
    zend_object std;

    skeleton_vector3_object *position;
    skeleton_vector3_object *direction;
} skeleton_ray_object;

When I update either vector3 object, I don't have a way to propagate the
changes back to the parent object(Ray). This means the raw Ray struct is
never updated. Is there a built-in way to handle propagation (listeners) in
a PHP extension or will I need to hand roll this?


Thanks,
Joseph Montanez

---

## #113424 — Michael Wallner — 2021-03-09T11:03:19+00:00

URL: https://externals.io/message/113422#113424 · In reply to: #113422

Hi Joseph!

Is this for educational purposes or real world usage?

I ask, because, if you don't have to adhere to a predefined C-API you
would avoid lots of headache by baking all this stuff into your PHP
objects with the APIs provided by Zend. But then again, it would be way
more efficient to just write it all in PHP code.

More comments following inline.


> I am not sure what to title this but the gist is that I have two structs
> with a one way dependency:
> 
> // Vector3 type
> typedef struct Vector3 {
>     float x;
>     float y;
>     float z;
> } Vector3;
> 
> // Ray type (useful for raycast)
> typedef struct Ray {
>     Vector3 position;       // Ray position (origin)
>     Vector3 direction;      // Ray direction
> } Ray;

As these are embedded structs instead of pointers to, neither do point
to the memory of the other.


> The PHP Ray object is defined as so:
> typedef struct _skeleton_ray_object {
>     Ray ray;
>     HashTable *prop_handler;
>     zend_object std;
      ^^^^^^^^^^^^^^^^
NOTE: This has to be the last line in your object struct because of an
VLA at the end of the zend_object struct. See your xyz_new() function
where you allocate additional zend_object_properties_size() bytes for
that VLA (variable length array).

> 
>     skeleton_vector3_object *position;
>     skeleton_vector3_object *direction;
> } skeleton_ray_object;
> 
> When I update either vector3 object, I don't have a way to propagate the
> changes back to the parent object(Ray). This means the raw Ray struct is
> never updated. Is there a built-in way to handle propagation (listeners) in
> a PHP extension or will I need to hand roll this?

Yes, you gotta write all of this on your own, but let me repeat that
this is a lot of boilerplate which also may entail lots of headache.


-- 
Regards,
Mike

---

## #113429 — Joseph Montanez — 2021-03-10T06:09:30+00:00

URL: https://externals.io/message/113422#113429 · In reply to: #113424

Michael,

Thanks for the feedback! The part about having Zend Object at the end of a
struct saved me because I was starting to have issues with cloning objects.

This is for an extension I was working on and have picked back up, I was
just isolating it to make it easy to work with. Its bindings for
https://www.raylib.com/. The general issue is that I won't be able to
change RayLib's structs (embedded vs pointers), so what I showed was an
isolated example. There is another author who also has a RayLib binding in
PHP and has a similar issue:

https://github.com/oraoto/raylib-phpcpp
<?php

$v = Vector2(1, 2);
// $v->x += 1; doesn't work
$v->x = $v->x + 1;

$camera = Camera2D(Vector2(1, 1), Vector2(0, 0), 1.0, 1.0);
// $camera->target->x = 1; // doesn't work
$target = $camera->target;
$target->x = 1;
$camera->target = $target;

This is far from ideal, and I can only assume people will run into this and
assume it's a bug. On my end, I've written my own (incomplete) bindings
https://github.com/joseph-montanez/raylib-php |
https://joseph-montanez.github.io/raylib-php-docs/ I am working towards
those issues, this is just a stumbling block.

The more I think about this, I probably don't need a listening system,
rather when the object is used for an internal RayLib function call, I can
unravel the sub-objects and write back to the original struct. For example
in RayLib there is a function DrawRay(Ray ray, Color color), when I
implement DrawRay, I can do the unravel work to pull the values from
position and direction PHP objects back into the Ray struct, and then pass
the Ray struct to DrawRay.



Thanks,
Joseph Montanez




> Hi Joseph!
>
> Is this for educational purposes or real world usage?
>
> I ask, because, if you don't have to adhere to a predefined C-API you
> would avoid lots of headache by baking all this stuff into your PHP
> objects with the APIs provided by Zend. But then again, it would be way
> more efficient to just write it all in PHP code.
>
> More comments following inline.
>
> 
> > […]
>
> As these are embedded structs instead of pointers to, neither do point
> to the memory of the other.
>
>
> > […]
>       ^^^^^^^^^^^^^^^^
> NOTE: This has to be the last line in your object struct because of an
> VLA at the end of the zend_object struct. See your xyz_new() function
> where you allocate additional zend_object_properties_size() bytes for
> that VLA (variable length array).
>
> > […]
> in
> > […]
>
> Yes, you gotta write all of this on your own, but let me repeat that
> this is a lot of boilerplate which also may entail lots of headache.
>
>
> --
> Regards,
> Mike

---

## #113432 — Joseph Montanez — 2021-03-10T11:24:33+00:00

URL: https://externals.io/message/113422#113432 · In reply to: #113429

While attempting this I ran into the need to use GC_ADDREF() or else the
returned sub-object data would be gargled.  Here is an example struct:

typedef struct _php_raylib_camera2d_object {
    Camera2D camera2d;
    HashTable *prop_handler;
    php_raylib_vector2_object *offset;
    php_raylib_vector2_object *target;
    zend_object std;
} php_raylib_camera2d_object;


When I returned a say "target" i.e:

static zend_object * php_raylib_camera2d_target(php_raylib_camera2d_object
*obj)
{
    return &obj->target->std;
}

In PHP the values would be incorrect. However by
adding GC_ADDREF(&obj->target->std), this seems to have fixed the problem.
So it now reads:

static zend_object * php_raylib_camera2d_target(php_raylib_camera2d_object
*obj)
{
    GC_ADDREF(&obj->target->std);
    return &obj->target->std;
}

However in PHP if someone is repeatedly access the subobject "target" does
this now much it will have many references? Will I need to manage those
references in my C code?

Here is the resulting object code in PHP:
<?php
$camera = new \raylib\Camera2d();

$camera->target->x = 5;
$camera->target->y = 6;

var_dump($camera->target);

Here "target" is accessed 3 times. Thats 3 additional reference counts. If
I unset($camera),  do I now need to manage those references and
explicitly call GC_DELREF/GC_SET_REFCOUNT in my `free_obj` function?

Thanks,
Joseph Montanez




> Michael,
>
> Thanks for the feedback! The part about having Zend Object at the end of a
> struct saved me because I was starting to have issues with cloning objects.
>
> This is for an extension I was working on and have picked back up, I was
> just isolating it to make it easy to work with. Its bindings for
> https://www.raylib.com/. The general issue is that I won't be able to
> change RayLib's structs (embedded vs pointers), so what I showed was an
> isolated example. There is another author who also has a RayLib binding in
> PHP and has a similar issue:
>
> https://github.com/oraoto/raylib-phpcpp
> <?php
>
> $v = Vector2(1, 2);
> // $v->x += 1; doesn't work
> $v->x = $v->x + 1;
>
> $camera = Camera2D(Vector2(1, 1), Vector2(0, 0), 1.0, 1.0);
> // $camera->target->x = 1; // doesn't work
> $target = $camera->target;
> $target->x = 1;
> $camera->target = $target;
>
> This is far from ideal, and I can only assume people will run into this
> and assume it's a bug. On my end, I've written my own (incomplete) bindings
> https://github.com/joseph-montanez/raylib-php |
> https://joseph-montanez.github.io/raylib-php-docs/ I am working towards
> those issues, this is just a stumbling block.
>
> The more I think about this, I probably don't need a listening system,
> rather when the object is used for an internal RayLib function call, I can
> unravel the sub-objects and write back to the original struct. For example
> in RayLib there is a function DrawRay(Ray ray, Color color), when I
> implement DrawRay, I can do the unravel work to pull the values from
> position and direction PHP objects back into the Ray struct, and then pass
> the Ray struct to DrawRay.
>
>
>
> Thanks,
> Joseph Montanez
>
>
> 
>
> > […]

---

## #113436 — Kirill Nesmeyanov — 2021-03-10T16:10:32+00:00

URL: https://externals.io/message/113422#113436 · In reply to: #113422

Hello, Joseph! I faced exactly the same problems and believe that the 
Observer API is necessary.

To solve a similar problem, I implemented my own API over FFI, however, 
it does not work very stable (I have little experience in send api), but 
it may help to solve your problems: https://github.com/SerafimArts/Observer

09.03.2021 5:32, Joseph Montanez пишет:
> I am not sure what to title this but the gist is that I have two structs
> with a one way dependency:
>
> // Vector3 type
> typedef struct Vector3 {
>      float x;
>      float y;
>      float z;
> } Vector3;
>
> // Ray type (useful for raycast)
> typedef struct Ray {
>      Vector3 position;       // Ray position (origin)
>      Vector3 direction;      // Ray direction
> } Ray;
>
>
> I've gone ahead and made a test extension here:
> https://github.com/joseph-montanez/php-ext-reference-issue . I've made each
> struct a PHP object. allowing me to do this:
>
> <?php
> $position = new \skeleton\Vector3(1,2,3);
> $direction = new \skeleton\Vector3(4,5,6);
>
> $ray = new \skeleton\Ray($position, $direction);
>
> echo 'Z: ', $ray->position->z, PHP_EOL; // Outputs: 3
>
> $position->z = 9;
>
> echo 'Z: ', $ray->position->z, PHP_EOL; // Outputs: 9
>
> The PHP Ray object is defined as so:
> typedef struct _skeleton_ray_object {
>      Ray ray;
>      HashTable *prop_handler;
>      zend_object std;
>
>      skeleton_vector3_object *position;
>      skeleton_vector3_object *direction;
> } skeleton_ray_object;
>
> When I update either vector3 object, I don't have a way to propagate the
> changes back to the parent object(Ray). This means the raw Ray struct is
> never updated. Is there a built-in way to handle propagation (listeners) in
> a PHP extension or will I need to hand roll this?
>
>
> Thanks,
> Joseph Montanez
