Thread: Writing PECL extension, need to iterate object properties, distinguish dynamic ones

  1. #1
    Registered User
    Join Date
    Apr 2011
    Posts
    34

    Writing PECL extension, need to iterate object properties, distinguish dynamic ones

    I'm working on a data serialization routine wherein I must iterate through an object's properties while distinguishing between "sealed" properties (i.e., those defined by class definitions) and "dynamic" properties" (i.e., those assigned ad-hoc to the object that are not party of any class definition).

    I've been searching through the lxr for suitable code examples, but can't seem to find any code doing precisely this thing. Also, the code really doesn't have many useful comments to explain what functions are doing which makes it pretty hard to follow. I can't figure out what zend_lookup_class or zend_update_class_constants are doing but somehow the function get_class_vars is excluding any non-accessible properties.

    I have two big problems/questions that I'm hoping someone might help me sort out.

    1) Is there a method that will reliably return all "sealed" properties in a *consistent order*. I.e., if I encounter an array of objects of class Foo, can I be sure that iterating through an object's properties will always occur in the exact same sequence? Any variation in the sequence order will result in serialization problems for this protocol.

    2) What is the accepted/canonical/orthodox way to disinguish between public properties (i.e., properties available in the current scope) and private/protected properties? My loop below that uses zend_hash_get_current_key_ex appears to also iterate through properties unavailable in the current scope and yet I am not privy to their names.

    E.g., I ran the following code. classEntry is the zend_class_entry corresponding to some object that is an instance of class ABC which has properties X, Y, and Z. Y is a private property.

    Code:
                char *key;
                long index, i;
                uint key_len;
                HashPosition pos;
                HashTable defaultProperties;
                defaultProperties = classEntry->default_properties;
    
    
                // for dev debugging, loop through default properties to list them
                zend_hash_internal_pointer_reset_ex(&defaultProperties, &pos);
                for (;; zend_hash_move_forward_ex(&defaultProperties, &pos)) {
                    i = zend_hash_get_current_key_ex(&defaultProperties, &key, &key_len, &index, 0, &pos);
                    if (i == HASH_KEY_NON_EXISTANT) {
                        // out of members! skip the rest of the loop
                        break;
                    }
    
                    php_printf("The %s th key is %s, i is %d\n", pos->arKey, key, i);
    
                }
    The output seems reasonable enough for the X and Z properties, but something is wrong with the Y property. Here's the output:
    Code:
    The Z th key is Z, i is 1
    The  th key is , i is 1
    The X th key is X, i is 1
    So apparently I cannot check i, the return value of zend_hash_get_current_key_ex for any indication that the property is inaccessible. What should I be checking for? Or should I be using some different function or combination of parameters?

  2. #2
    Registered User
    Join Date
    Apr 2011
    Posts
    34
    I searched some more and had some luck with the source of the php function get_object_vars(). However, I am still pretty confused. PHP source is *full* of macros and has basically no comments to explain what is going on. I'm hoping someone might clear some things up.

    QUESTIONS
    1) Sadly, this approach is unable to distinguish "sealed" properties from dynamic ones. If anyone can refer me to some source which can first iterate sealed properties and then iterate only dynamic ones, that would be extremely helpful.

    2) What does zend_objects_get_address do? This reference looks very different than the usual macro, e.g. Z_OBJ_HT_P

    3) Am I correct in understanding that zend_check_property_access simply checks if the property in question is availabe in the scope in which this function call gets executed? Is this the canonical/orthodox way to make this check?

    4) What does zend_unmangle_property_name do? Why is this function changed to zend_unmangle_property_name_ex in 5.5 and later?

    5) Why do we call Z_ADDREF_PP(value) here? Is this reference counting to prevent garbage collection?

    6) An IS_INTERNED check is added in php 5.6 and yet the macro for IS_INTERNED is zero. What's that all about?

    7) The code looks quite different between the different versions:
    5.3
    5.4
    5.5
    5.6
    From what I can tell there is at least one difference between 5.3 and 5.6 -- zend_unmangle_property_name_ex does not exist before 5.5. Can anyone recommend how to make code that'll work in 5.3-5.6? Also, with 7 coming out I'd like it work there too.

  3. #3
    Registered User
    Join Date
    Apr 2011
    Posts
    34
    I'm making slow progress but, being a terrible C programmer, I could definitely use some guidance if anyone is feeling generous. I hope I'll be forgiven for seeking help on this topic so shamelessly. I've been working hard at it for two weeks and it's been rough going but I've made so much progress. I think I'm reaching a point where I need superior expertise.

    Most of the encoding functionality is complete but I'm struggling a bit with the decoding functions because I need to return zvals and other objects. In particular, I'm getting some memory leak notifications and I'm also getting some weird behavior when I add values to a HashTable and retrieve them later. I'd very much appreciate some guidance. A code review would be awesome if anyone is willing.

    I've posted the extension's source on github at https://github.com/sneakyimp/amfext

    === Problem 1: I pass a *zval to one of my functions by reference and it gets pointed to a zval object representing a string. I was trying to check Z_TYPE_P(myzval)==IS_STRING and even though it is a string with a type value of 6, the check says otherwise, though. I'm baffled by this because my code looks just like examples I've seen in 'Extending and Embedding PHP.' Code:
    Code:
            zval *assoc_key;
            MAKE_STD_ZVAL(assoc_key);
            amf_read_string(buf, buf_len, buf_cursor, assoc_key, flags, htComplexObjects, htObjectTypeTraits, htStrings TSRMLS_CC);
    
            // had to remove this check because it was failing on empty strings for some reason
            if (Z_TYPE_P(assoc_key) == IS_STRING) {
                // the baffling thing is it returns "String expected, but returned object type is 6"
                php_error_docref(NULL TSRMLS_CC, E_ERROR, "String expected, but returned object type is %d", (unsigned int)Z_TYPE_P(assoc_key));
            }
    Here's a link to the source on github: https://github.com/sneakyimp/amfext/...er/amf.c#L1242

    === Problem 2: Similar to the previous problem, but the issue is that string zval is retrieved from a HashTable and when it comes out, the type value is messed up value. Code:
    Code:
            // make sure it's a string
            // WTF? Z_TYPE_P(strz) is never IS_STRING and Z_STRVAL_P returns messed up values
            // e.g., "ZVAL stored at index 0 is not a string, type=-30420640"
            if (Z_TYPE_P(strz) != IS_STRING) {
                php_error_docref(NULL TSRMLS_CC, E_ERROR, "ZVAL stored at index %d is not a string, type=%d\n", (unsigned int)str_index, Z_STRVAL_P(strz));
            }
    Here's a link to the source on github: https://github.com/sneakyimp/amfext/...er/amf.c#L1300

    === Problem 3: Memory leaks!

    I concocted this test PHP script for my amf_decode function. It retrieves a data file containing an amf3-serialized array:
    array("abc", "abc", "abc", "a" => 1, "b" => 2, "c" => 3)

    And then decodes it. The PHP script:
    PHP Code:
    <?php
    $dir 
    dirname(__FILE__);
    $str file_get_contents($dir "/amf_arr_abc123.dat");
    var_dump(amf_decode($str));
    The output of the script:
    Code:
    array(6) {
      ["a"]=>
      int(1)
      ["b"]=>
      int(2)
      ["c"]=>
      int(3)
      [0]=>
      string(3) "abc"
      [1]=>
      string(3) "abc"
      [2]=>
      string(3) "abc"
    }
    [Fri Oct  2 12:00:21 2015]  Script:  '/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php'
    /home/jaith/src/php-5.5.29/ext/amf/amf.c(1324) :  Freeing 0x7F30D2091080 (32 bytes), script=/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php
    Last leak repeated 3 times
    [Fri Oct  2 12:00:21 2015]  Script:  '/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php'
    /home/jaith/src/php-5.5.29/ext/amf/amf.c(1330) :  Freeing 0x7F30D2091270 (2 bytes), script=/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php
    Last leak repeated 2 times
    [Fri Oct  2 12:00:21 2015]  Script:  '/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php'
    /home/jaith/src/php-5.5.29/ext/amf/amf.c(1325) :  Freeing 0x7F30D20914F0 (2 bytes), script=/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php
    Last leak repeated 3 times
    [Fri Oct  2 12:00:21 2015]  Script:  '/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php'
    /home/jaith/src/php-5.5.29/ext/amf/amf.c(1238) :  Freeing 0x7F30D2092348 (32 bytes), script=/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php
    [Fri Oct  2 12:00:21 2015]  Script:  '/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php'
    /home/jaith/src/php-5.5.29/ext/amf/amf.c(1333) :  Freeing 0x7F30D2092AB8 (1 bytes), script=/home/jaith/src/php-5.5.29/ext/amf/jta/read_arr_test2.php
    === Total 13 memory leaks detected ===

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. working on PECL extension, need C/C++ pointer guidance
    By sneakyimp in forum C Programming
    Replies: 0
    Last Post: 11-05-2011, 03:52 PM
  2. how to set up IDE for PHP/PECL development?
    By sneakyimp in forum Tech Board
    Replies: 0
    Last Post: 04-17-2011, 01:23 PM
  3. dynamic object growth
    By Perspective in forum C++ Programming
    Replies: 5
    Last Post: 03-14-2003, 03:26 PM
  4. deleteing a dynamic object
    By Eber Kain in forum C++ Programming
    Replies: 4
    Last Post: 12-14-2002, 05:59 PM
  5. dynamic object initialisation??
    By splendid bob in forum C++ Programming
    Replies: 2
    Last Post: 07-04-2002, 12:35 PM

Tags for this Thread