summaryrefslogtreecommitdiff
path: root/zend/objectimpl.h
blob: ed48e269a8156a2012889b2673467cbadc398dd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
 *  ObjectImpl.h
 *
 *  Implementation class for Base objects that allow the objects to be stored
 *  in the Zend engine
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2014 Copernica BV
 */

/**
 *  Set up namespace
 */
namespace Php {

/**
 *  Class definition
 */
class ObjectImpl
{
private:
    /**
     *  Structure with a first element which is a zend_object, so that
     *  it can be casted to a zend_object
     *  @var    MixedObject
     */
    struct MixedObject
    {
        /**
         *  Pointer to ourselves
         *  @var    ObjectImpl
         */
        ObjectImpl *self;

        /**
         *  The actual object MUST be the last member, because PHP uses hackish
         *  tricks for optimization (we allocate more memory than sizeof(MixedObject))
         *  @var    zend_object
         */
        zend_object php;

    } *_mixed;

    /**
     *  Pointer to the C++ implementation
     *  @var    std::unique_ptr<Base>
     */
    std::unique_ptr<Base> _object;

public:
    /**
     *  Constructor
     *
     *  This will create a new object in the Zend engine.
     *
     *  @param  entry       Zend class entry
     *  @param  handler     Zend object handlers
     *  @param  base        C++ object that already exists
     *  @param  refcount    The initial refcount for the object
     *  @param  tsrm_ls     Optional threading data
     */
    ObjectImpl(zend_class_entry *entry, Base *base, zend_object_handlers *handlers, int refcount TSRMLS_DC) :
        _object(base)
    {
        // allocate a mixed object (for some reason this does not have to be deallocated)
        _mixed = (MixedObject *)ecalloc(1, sizeof(MixedObject) + zend_object_properties_size(entry));

        // copy properties to the mixed object
        _mixed->php.ce = entry;
        _mixed->self = this;

        // initialize the object and its properties
        zend_object_std_init  (&_mixed->php, entry TSRMLS_CC);
        object_properties_init(&_mixed->php, entry);

        // install the handlers
        _mixed->php.handlers = handlers;

        // set the initial refcount (if it is different than one, because one is the default)
        if (refcount != 1) GC_REFCOUNT(php()) = refcount;

        // the object may remember that we are its implementation object
        base->_impl = this;
    }

    /**
     *  Destructor
     */
    virtual ~ObjectImpl() = default;

    /**
     *  Destruct the object
     *  @param  tsrm_ls
     */
    void destruct(TSRMLS_D)
    {
        // destruct the object
        delete this;
    }

    /**
     *  The offset between the zend_object and the ObjectImpl
     *  in bytes. This can be used to find the other when only
     *  a pointer to one is available.
     *
     *  @return The offset in bytes
     */
    static constexpr size_t offset()
    {
        // calculate the offset in bytes
        return offsetof(MixedObject, php);
    }

    /**
     *  Find the object based on a zval
     *  @param  val         Zval object
     *  @param  tsrm_ls     Optional pointer to thread info
     *  @return ObjectImpl
     */
    static ObjectImpl *find(zval *val TSRMLS_DC)
    {
        // retrieve the zend_object from the zval and use it to find the ObjectImpl
        return find(Z_OBJ_P(val));
    }

    /**
     *  Find the object based on a zend_object
     *  @param  object      Zend object pointer
     *  @return ObjectImpl
     */
    static ObjectImpl *find(const zend_object *object)
    {
        // the zend_object is the last pointer in the struct so we have to subtract the
        // correct number of bytes from the pointer to get at the address at which the
        // actual ObjectImpl starts. to be able to actually perform this pointer arithmetic
        // we must first cast the pointer to a char (void pointer arithmetic is not allowed!)
        auto *mixed = (const MixedObject*)((char*)object - offset());

        // done
        return mixed->self;
    }

    /**
     *  Retrieve the base class of the original C++ object
     *  @return Base
     */
    Base *object() const
    {
        return _object.get();
    }

    /**
     *  Pointer to the PHP object
     *  @return zend_object
     */
    zend_object *php() const
    {
        return &_mixed->php;
    }
};

/**
 *  End of namespace
 */
}