summaryrefslogtreecommitdiff
path: root/src/extension.cpp
blob: 2d9ef9f3b26ac3cc817a86aa89c53dd4263dc03d (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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/**
 *  Extension.cpp
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2013 Copernica BV
 */
#include "includes.h"

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

/**
 *  If this extension is compiled for a PHP version with multi
 *  threading support, we need an additional header file
 */
#ifdef ZTS
#include "TSRM.h"
#endif

/**
 *  The way how PHP C API deals with "global" variables is stupid.
 * 
 *  This is supposed to turn into a structure that is going to be 
 * 	instantiated for each parallel running request, and for which the 
 * 	PHP engine allocates a certain amount of memory, and a magic
 *  pointer that is passed and should be forwarded to every thinkable 
 * 	PHP function.
 * 
 * 	We don't like this architecture. We have our own request object
 * 	that makes much more sense, and that we use. However, we need
 *  to assign this object somewhere, so that's what we do in this
 *  one and only global variable
 */
ZEND_BEGIN_MODULE_GLOBALS(phpcpp)
    Request *request;
ZEND_END_MODULE_GLOBALS(phpcpp)

/**
 *  And now we're going to define a macro. This also is a ridiculous
 * 	architecture from PHP to get access to a variable from the 
 *  structure above.
 */
#ifdef ZTS
#define REQUEST_G(v) TSRMG(phpcpp_globals_id, zend_phpcpp_globals *, v)
#else
#define REQUEST_G(v) (phpcpp_globals.v)
#endif

/**
 *  We're almost there, we now need to declare an instance of the
 *  structure defined above (if building for a single thread) or some
 *  sort of impossible to understand magic pointer-to-a-pointer (for
 *  multi-threading builds). We make this a static variable because
 *  this already is bad enough.
 */
static ZEND_DECLARE_MODULE_GLOBALS(phpcpp)

/**
 *  Function that must be defined to initialize the "globals"
 *  We do not have to initialize anything, but PHP needs to call this
 *  method (crazy)
 * 	@param	globals
 */
static void php_phpcpp_init_globals(zend_phpcpp_globals *globals) {}



/**
 *  Helper method to get back the current extension object
 *  @return	Extension
 */
static Extension *get_extension()
{
	// retrieve the extension or module name (because PHP of course does
	// not pass such extremely useful information as they've no clue how
	// to make a decent API
	zend_module_entry *module = EG(current_module);

	// the pointer to the extension is hidden in front of the name
	return HiddenPointer<Extension>(module->name);
}

/**
 *  Function that is called when the extension initializes
 *  @param  type        Module type
 *  @param  number      Module number
 *  @return int         0 on success
 */
static int extension_startup(INIT_FUNC_ARGS)
{
	
	
	
	// initialize and allocate the "global" variables
//	ZEND_INIT_MODULE_GLOBALS(hello, php_phpcpp_init_globals, NULL);	
	
    // initialize the extension
    return BOOL2SUCCESS(get_extension()->initialize());
}

/**
 *  Function that is called when the extension is about to be stopped
 *  @param  type        Module type
 *  @param  number      Module number
 *  @return int
 */
static int extension_shutdown(SHUTDOWN_FUNC_ARGS)
{
    // finalize the extension
    return BOOL2SUCCESS(get_extension()->finalize());
}

/**
 *  Function that is called when a request starts
 *  @param  type        Module type
 *  @param  number      Module number
 *  @return int         0 on success
 */
static int request_startup(INIT_FUNC_ARGS)
{
    // create the request
    return BOOL2SUCCESS(get_extension()->startRequest());
}

/**
 *  Function that is called when a request is ended
 *  @param  type        Module type
 *  @param  number      Module number
 *  @return int         0 on success
 */
static int request_shutdown(INIT_FUNC_ARGS)
{
    // end the request
    return BOOL2SUCCESS(get_extension()->endRequest());
}


/**
 *  Constructor
 *  @param  name        Name of the extension
 *  @param  version     Version number
 */
Extension::Extension(const char *name, const char *version) : _ptr(this, name)
{
    // allocate memory (we allocate this on the heap so that the size of the
    // entry does not have to be defined in the .h file. We pay a performance
    // price for this, but we pay this price becuase the design goal of the
    // PHP-C++ library is to have an interface that is as simple as possible
    _entry = new zend_module_entry;
    
    // assign all members (apart from the globals)
    _entry->size = sizeof(zend_module_entry);               // size of the data
    _entry->zend_api = ZEND_MODULE_API_NO;                  // api number
    _entry->zend_debug = ZEND_DEBUG;                        // debug mode enabled?
    _entry->zts = USING_ZTS;                                // is thread safety enabled?
    _entry->ini_entry = NULL;                               // the php.ini record
    _entry->deps = NULL;                                    // dependencies on other modules
    _entry->name = _ptr;                                    // extension name, with a hidden pointer to the extension object
    _entry->functions = NULL;                               // functions supported by this module (none for now)
    _entry->module_startup_func = extension_startup;        // startup function for the whole extension
    _entry->module_shutdown_func = extension_shutdown;      // shutdown function for the whole extension
    _entry->request_startup_func = request_startup;         // startup function per request
    _entry->request_shutdown_func = request_shutdown;       // shutdown function per request
    _entry->info_func = NULL;                               // information for retrieving info
    _entry->version = version;                              // version string
    _entry->globals_size = 0;                               // size of the global variables
    _entry->globals_ptr = NULL;                             // pointer to the globals
    _entry->globals_ctor = NULL;                            // constructor for global variables
    _entry->globals_dtor = NULL;                            // destructor for global variables
    _entry->post_deactivate_func = NULL;                    // unknown function
    _entry->module_started = 0;                             // module is not yet started
    _entry->type = 0;                                       // temporary or persistent module, will be filled by Zend engine
    _entry->handle = NULL;                                  // dlopen() handle, will be filled by Zend engine
    _entry->module_number = 0;                              // module number will be filled in by Zend engine
    _entry->build_id = ZEND_MODULE_BUILD_ID;                // check if extension and zend engine are compatible

    // things that only need to be initialized
#ifdef ZTS
    _entry->globals_id_ptr = NULL;
#else
    _entry->globals_ptr = NULL;
#endif

}

/**
 *  Destructor
 */
Extension::~Extension()
{
    // deallocate functions
    if (_entry->functions) delete[] _entry->functions;
    
    // deallocate entry
    delete _entry;
}

/**
 *  Add a function to the library
 *  @param  name        Function name
 *  @param  function    Function object
 *  @return Function
 */
Function &Extension::add(const char *name, const Function &function)
{
    // add the function to the map
    return _functions[name] = function;
}

/**
 *  Retrieve the module entry
 *  @return zend_module_entry
 */
zend_module_entry *Extension::module()
{
    // check if functions we're already defined
    if (_entry->functions || _functions.size() == 0) return _entry;

    // allocate memory for the functions
    zend_function_entry *functions = new zend_function_entry[_functions.size() + 1];

    // keep iterator counter
    int i = 0;

    // loop through the functions
    for (auto it = begin(_functions); it != _functions.end(); it++)
    {
        // retrieve entry
        zend_function_entry *entry = &functions[i];

        // let the function fill the entry
        it->second.fill(it->first, entry);
    }

    // last entry should be set to all zeros
    zend_function_entry *last = &functions[i];

    // all should be set to zero
    memset(last, 0, sizeof(zend_function_entry));

    // store functions in entry object
    _entry->functions = functions;

    // return the entry
    return _entry;
}

/**
 *  End of namespace
 */
}