summaryrefslogtreecommitdiff
path: root/zend/object.cpp
blob: e2345c1934b915055289d80bd7a09ca26494fa39 (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
/**
 *  Object.cpp
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2014 Copernica BV
 */
#include "includes.h"

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

/**
 *  Constructor to create a new instance of a builtin class
 *
 *  @param  name        Name of the class to instantiate
 *  @param  base        The C++ object to wrap
 */
Object::Object(const char *name, Base *base) : Value()
{
    // does the object already have a handle?
    if (base->implementation())
    {
        // the object is already instantiated, we can assign it to this object
        operator=(Value(base));
    }
    else
    {
        // we need the tsrm_ls variable
        TSRMLS_FETCH();

        // this is a brand new object that should be allocated, the C++ instance
        // is already there (created by the extension) but it is not yet stored
        // in PHP, find out the classname first (we use the FatalError class
        // here because this function is called from C++ context, and zend_error()
        // would cause a longjmp() which does not clean up C++ objects created
        // by the extension).
        auto *entry = zend_fetch_class(zend_string_init(name, ::strlen(name), 0), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
        if (!entry) throw FatalError(std::string("Unknown class name ") + name);

        // construct an implementation (this will also set the implementation
        // member in the base object), this is a self-destructing object that
        // will be destructed when the last reference to it has been removed,
        // we already set the reference to zero
        new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry), 0 TSRMLS_CC);

        // now we can store it
        operator=(Value(base));

        // install the object handlers
        Z_OBJ_P(_val)->handlers = ClassImpl::objectHandlers(entry);
    }
}

/**
 *  Constructor in case the class entry is already known
 *
 *  @param  entry       Class entry
 *  @param  base        The C++ object to wrap
 */
Object::Object(zend_class_entry *entry, Base *base) : Value()
{
    // does the object already have a handle?
    if (base->implementation())
    {
        // the object is already instantiated, we can assign it to this object
        operator=(Value(base));
    }
    else
    {
        // we need the tsrm_ls variable
        TSRMLS_FETCH();

        // construct an implementation (this will also set the implementation
        // member in the base object), this is a self-destructing object that
        // will be destructed when the last reference to it has been removed,
        // we already set the reference to zero
        new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry), 0 TSRMLS_CC);

        // now we can store it
        operator=(Value(base));
    }
}

/**
 *  Copy constructor is valid if the passed in object is also an object,
 *  or when it is a string holding a classname
 *  @param  that        An other object
 */
Object::Object(const Value &value) : Value()
{
    // when a string is passed in, we are going to make a new instance of the
    // passed in string
    if (value.isString())
    {
        // instantiate the object
        if (instantiate(value)) call("__construct");
    }
    else
    {
        // this simply copies the other object
        operator=(value);
    }
}

/**
 *  Internal method to instantiate an object
 *  @param  name        Name of the class to instantiate
 *  @return bool        True if there is a __construct function
 */
bool Object::instantiate(const char *name)
{
    // we need the tsrm_ls variable
    TSRMLS_FETCH();

    // convert the name into a class_entry (we use the FatalError class
    // here because this function is called from C++ context, and zend_error()
    // would cause a longjmp() which does not clean up C++ objects created
    // by the extension).
    auto *entry = zend_fetch_class(zend_string_init(name, ::strlen(name), 0), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
    if (!entry) throw FatalError(std::string("Unknown class name ") + name);

    // initiate the zval (which was already allocated in the base constructor)
    object_init_ex(_val, entry);

    // @todo    should we call methods like allocating hashtables, copying and
    //          initializing properties, et cetera????? In all example you always
    //          see such complicated and next-to-impossible-to-understand
    //          sequences of functions being called, but this object_init_ex
    //          also seems to work...

    // @todo    is this a memory leak? the base class first initializes a stdClass,
    //          and then we overwrite it with a specific class

    // return whether there is a __construct function
    return zend_hash_exists(&entry->function_table, zend_string_init("__construct", 12, 1));
}

/**
 *  End namespace
 */
}